Difference between revisions of "Developing Python Modules with Pascal"

From Lazarus wiki
Jump to navigationJump to search
(Took out template)
(Added "Using your module in a host application")
Line 272: Line 272:
  
 
What we've done here is create a simple Python module that implements a Python function for adding two integers. Once you've imported the module into your Python script, you can use the function in the same way that you use built-in Python functions.
 
What we've done here is create a simple Python module that implements a Python function for adding two integers. Once you've imported the module into your Python script, you can use the function in the same way that you use built-in Python functions.
 +
 +
== Using your module in a host application ==
 +
 +
If you have OpenOffice or NeoOffice installed, you can test running your module from a Python macro.
 +
 +
Copy and paste this script into a text editor and save it as file test_minmod.py, then place it in the folder specified in the script. You may have to create the python\Library1 folder under Scripts.
 +
 +
<pre>
 +
# Python macro that tests Pascal module by creating new document and inserting module function result.
 +
 +
import sys, os
 +
# Tell Python where to look for our Pascal module
 +
if sys.platform == 'win32':
 +
  sys.path.append(os.path.expanduser('~\Application Data\OpenOffice.org2\user\Scripts\python\Library1'))
 +
elif sys.platform == 'darwin':
 +
  sys.path.append(os.path.expanduser('~/Library/Preferences/NeoOffice-2.2/user/Scripts/python/Library1'))
 +
 +
# Import Pascal module that contains SumTwoIntegers function
 +
import PyMinMod
 +
 +
import uno
 +
 +
def TestMinMod():
 +
  ctx = uno.getComponentContext()
 +
  smgr = ctx.ServiceManager
 +
  desktop = smgr.createInstance('com.sun.star.frame.Desktop')
 +
  doc = desktop.loadComponentFromURL('private:factory/swriter', '_blank', 0, ())
 +
  textCursor = doc.Text.createTextCursor()
 +
  doc.Text.insertString(textCursor, 'Sum of 1 + 2 = ' + str(PyMinMod.SumTwoIntegers(1, 2)), 0)
 +
</pre>
 +
 +
Since OO probably includes Python 2.3, compile your module against 2.3 by adding this on the Lazarus Compiler Options dialog's Other tab:
 +
 +
<pre>
 +
-dUSE_PYTHON23
 +
</pre>
 +
 +
With Delphi, enter USE_PYTHON23 on the Project Options dialog's Directories/Conditionals tab.
 +
 +
Rename your compiled module if necessary, then place it in the same folder as test_minmod.py. Now test running it from OO by choosing Tools | Macros | Organize Macros | Python and running the TestMinMod macro.

Revision as of 00:24, 3 January 2009

Introduction

Python is a popular scripting language that is often used to add functionality to other applications such as OpenOffice and Quantum GIS. Your computer may already have a version of Python installed. If not, you can download Python from the official Website: http://www.python.org/.

You can extend Python by developing compiled libraries (called modules) that add functions to Python. This topic discusses how to create a library with Pascal (Delphi or Free Pascal).

Minimum Python API

Copy and paste this code into a text editor and save it as file PyAPI.pas:

unit PyAPI;

{ 

  Minimum set of Python function declarations for module libraries.
  
  Author: Phil (MacPgmr at fastermac.net).
  
  To add other Python function declarations, see the Python header
   files (.h) included with every Python distribution.

}

{$IFDEF FPC}
 {$MODE Delphi}
{$ENDIF} 

interface

const
{$IFDEF MSWINDOWS}
 {$IFDEF USE_PYTHON23}
  PythonLib = 'python23.dll';
 {$ELSE}
  PythonLib = 'python25.dll';
 {$ENDIF}
{$ENDIF} 

{$IFDEF LINUX}
 {$IFDEF USE_PYTHON23}
  PythonLib = 'python23.so';
 {$ELSE}
  PythonLib = 'python25.so';
 {$ENDIF}
{$ENDIF} 

{$IFDEF DARWIN}
  PythonLib = '';  //Link against Python.framework (-k-framework -kPython).
                   // To link Python 2.3, add -k-F/System/Library/Frameworks
{$ENDIF} 

type
  PyMethodDef = packed record
    name  : PChar;    //Python function name
    meth  : Pointer;  //Address of function that implements it
    flags : Integer;  //METH_xxx flags; describe function's arguments
    doc   : PChar;    //Description of funtion
    end;
    
  PyObject = Pointer;

const
{$IFDEF USE_PYTHON23}
  PYTHON_API_VERSION = 1012;  //Also used with Python 2.4
{$ELSE}
  PYTHON_API_VERSION = 1013;
{$ENDIF}
  METH_VARARGS = 1;

function Py_InitModule(    name    : PChar;
                       var methods : PyMethodDef;
                           doc     : PChar = nil;
                           self    : PyObject = nil;
                           apiver  : LongInt = PYTHON_API_VERSION) : PyObject; cdecl; external PythonLib name 'Py_InitModule4';

function PyArg_ParseTuple(args   : PyObject; 
                          format : PChar) : Integer; cdecl; varargs; external PythonLib;
 //Note varargs allows us to simulate C variable number of arguments (...).

function PyInt_FromLong(along : LongInt) : PyObject; cdecl; external PythonLib;


implementation


end.

If you have a different version of Python, define USE_PYTHON23 to link against version 2.3 or just change the library name (PythonLib). If you need other Python API functions, just add them to PyAPI.pas following the example of PyInt_FromLong.

A simple module example

Here is a simple library that uses this Python API unit. Copy and paste this code into a text editor and save it as file PyMinMod.dpr:

library PyMinMod;

{

  Minimal Python module (library) that includes a single function.
  
  Author: Phil (MacPgmr at fastermac.net).
  
  For a good explanation of modules from a C perspective, see:
    http://superjared.com/entry/anatomy-python-c-module/

  To compile this module:
    - With Delphi: Open this .dpr file and compile.
    - With Lazarus: Open .lpi file and compile.
    
  To deploy module:
    - With Delphi: Rename compiled .dll to .pyd.
    - With Lazarus on Windows: Rename compiled .so to .pyd.
    - With Lazarus on OS X and Linux: .so extension is okay.

}

uses
  SysUtils,
  PyAPI;


function SumTwoIntegers(Self : PyObject;
                        Args : PyObject) : PyObject; cdecl;
var
  Arg1 : Integer;
  Arg2 : Integer;
begin
  PyArg_ParseTuple(Args, 'ii', @Arg1, @Arg2);  //Get the two input arguments
  Result := PyInt_FromLong(Arg1 + Arg2);  //Add them together and return sum
end;


var
  Methods : packed array [0..1] of PyMethodDef;

procedure initPyMinMod; cdecl;
begin
  Methods[0].name := 'SumTwoIntegers';
  Methods[0].meth := @SumTwoIntegers;
  Methods[0].flags := METH_VARARGS;
  Methods[0].doc := 'Tests argument passing to and from module function';

  Methods[1].name := nil;
  Methods[1].meth := nil;
  Methods[1].flags := 0;
  Methods[1].doc := nil;

  Py_InitModule('PyMinMod', Methods[0]);
end;


exports
  initPyMinMod;

end.

You can add more functions to the module by following the example in initPyMinMod.

With Delphi, just open PyMinMod.dpr and compile; with Lazarus, you'll probably want to create a project file. You can do that yourself or just copy and paste this project file into a text editor and save it as file PyMinMod.lpi:

<?xml version="1.0"?>
<CONFIG>
  <ProjectOptions>
    <PathDelim Value="/"/>
    <Version Value="6"/>
    <General>
      <MainUnit Value="0"/>
      <IconPath Value="./"/>
      <TargetFileExt Value=".exe"/>
      <UseAppBundle Value="False"/>
      <ActiveEditorIndexAtStart Value="0"/>
    </General>
    <PublishOptions>
      <Version Value="2"/>
      <IgnoreBinaries Value="False"/>
      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
      <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
    </PublishOptions>
    <RunParams>
      <local>
        <FormatVersion Value="1"/>
        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
      </local>
    </RunParams>
    <Units Count="1">
      <Unit0>
        <Filename Value="PyMinMod.dpr"/>
        <IsPartOfProject Value="True"/>
        <UnitName Value="PyMinMod"/>
        <CursorPos X="1" Y="1"/>
        <TopLine Value="1"/>
        <EditorIndex Value="0"/>
        <UsageCount Value="20"/>
        <Loaded Value="True"/>
        <SyntaxHighlighter Value="Delphi"/>
      </Unit0>
    </Units>
    <JumpHistory Count="0" HistoryIndex="-1"/>
  </ProjectOptions>
  <CompilerOptions>
    <Version Value="8"/>
    <Target>
      <Filename Value="PyMinMod.so"/>
    </Target>
    <Parsing>
      <SyntaxOptions>
        <SyntaxMode Value="Delphi"/>
        <CStyleOperator Value="False"/>
        <AllowLabel Value="False"/>
        <CPPInline Value="False"/>
      </SyntaxOptions>
    </Parsing>
    <CodeGeneration>
      <Checks>
        <IOChecks Value="True"/>
        <RangeChecks Value="True"/>
        <OverflowChecks Value="True"/>
        <StackChecks Value="True"/>
      </Checks>
    </CodeGeneration>
    <Linking>
      <Options>
        <PassLinkerOptions Value="True"/>
        <LinkerOptions Value="-framework Python"/>
        <Win32>
          <GraphicApplication Value="True"/>
        </Win32>
        <ExecutableType Value="Library"/>
      </Options>
    </Linking>
    <Other>
      <CompilerPath Value="$(CompPath)"/>
    </Other>
  </CompilerOptions>
  <Debugging>
    <Exceptions Count="2">
      <Item1>
        <Name Value="ECodetoolError"/>
      </Item1>
      <Item2>
        <Name Value="EFOpenError"/>
      </Item2>
    </Exceptions>
  </Debugging>
</CONFIG>

Once you've compiled the module, rename it if necessary per the comments in PyMinMod.dpr. Then test the module by creating a simple test.py file that contains these two lines:

import PyMinMod
print "Value returned by SumTwoIntegers: " + str(PyMinMod.SumTwoIntegers(1, 2))

Note that Python is case sensitive so if your compiled module is in lower-case, change the "PyMinMod" references accordingly.

Now open a terminal window and run the script like this:

python test.py

The script should output the following line:

Value returned by SumTwoIntegers: 3

What we've done here is create a simple Python module that implements a Python function for adding two integers. Once you've imported the module into your Python script, you can use the function in the same way that you use built-in Python functions.

Using your module in a host application

If you have OpenOffice or NeoOffice installed, you can test running your module from a Python macro.

Copy and paste this script into a text editor and save it as file test_minmod.py, then place it in the folder specified in the script. You may have to create the python\Library1 folder under Scripts.

# Python macro that tests Pascal module by creating new document and inserting module function result.

import sys, os
# Tell Python where to look for our Pascal module
if sys.platform == 'win32':
  sys.path.append(os.path.expanduser('~\Application Data\OpenOffice.org2\user\Scripts\python\Library1'))
elif sys.platform == 'darwin':
  sys.path.append(os.path.expanduser('~/Library/Preferences/NeoOffice-2.2/user/Scripts/python/Library1'))

# Import Pascal module that contains SumTwoIntegers function
import PyMinMod

import uno

def TestMinMod():
  ctx = uno.getComponentContext()
  smgr = ctx.ServiceManager
  desktop = smgr.createInstance('com.sun.star.frame.Desktop')
  doc = desktop.loadComponentFromURL('private:factory/swriter', '_blank', 0, ())
  textCursor = doc.Text.createTextCursor()
  doc.Text.insertString(textCursor, 'Sum of 1 + 2 = ' + str(PyMinMod.SumTwoIntegers(1, 2)), 0)

Since OO probably includes Python 2.3, compile your module against 2.3 by adding this on the Lazarus Compiler Options dialog's Other tab:

-dUSE_PYTHON23

With Delphi, enter USE_PYTHON23 on the Project Options dialog's Directories/Conditionals tab.

Rename your compiled module if necessary, then place it in the same folder as test_minmod.py. Now test running it from OO by choosing Tools | Macros | Organize Macros | Python and running the TestMinMod macro.