Difference between revisions of "Developing Python Modules with Pascal"

From Lazarus wiki
Jump to navigationJump to search
(First draft)
 
(Added subtopics)
Line 6: Line 6:
  
 
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).
 
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:
 
Copy and paste this code into a text editor and save it as file PyAPI.pas:
Line 31: Line 33:
 
const
 
const
 
{$IFDEF MSWINDOWS}
 
{$IFDEF MSWINDOWS}
 +
{$IFDEF USE_PYTHON23}
 +
  PythonLib = 'python23.dll';
 +
{$ELSE}
 
   PythonLib = 'python25.dll';
 
   PythonLib = 'python25.dll';
 +
{$ENDIF}
 
{$ENDIF}  
 
{$ENDIF}  
 +
 
{$IFDEF LINUX}
 
{$IFDEF LINUX}
 +
{$IFDEF USE_PYTHON23}
 +
  PythonLib = 'python23.so';
 +
{$ELSE}
 
   PythonLib = 'python25.so';
 
   PythonLib = 'python25.so';
 +
{$ENDIF}
 
{$ENDIF}  
 
{$ENDIF}  
 +
 
{$IFDEF DARWIN}
 
{$IFDEF DARWIN}
   PythonLib = '';  //Link against Python.framework (-k-framework -kPython)
+
   PythonLib = '';  //Link against Python.framework (-k-framework -kPython).
 +
                  // To link Python 2.3, add -k-F/System/Library/Frameworks
 
{$ENDIF}  
 
{$ENDIF}  
  
Line 51: Line 64:
  
 
const
 
const
 +
{$IFDEF USE_PYTHON23}
 +
  PYTHON_API_VERSION = 1012;  //Also used with Python 2.4
 +
{$ELSE}
 
   PYTHON_API_VERSION = 1013;
 
   PYTHON_API_VERSION = 1013;
 +
{$ENDIF}
 
   METH_VARARGS = 1;
 
   METH_VARARGS = 1;
  
Line 73: Line 90:
 
</pre>
 
</pre>
  
If you have a different version of Python, 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.
+
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:
 
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:

Revision as of 21:05, 2 January 2009

Template:Developing Python Modules with Pascal

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.