Sign in Join
Wiki copytemporary

Developing Python Modules with Pascal: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Developing Python Modules with Pascal

OldNewLine
1+## Introduction
2+
3+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/>.
4+
5+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).
6+
7+This article describes a very low level, "from scratch" approach. For an established python-pascal bridge, see [[Python4Delphi]].
8+
9+## Minimum Python API
10+
11+Copy and paste this code into a text editor and save it as file PyAPI.pas:
12+
13+``` pascal
14+unit PyAPI;
15+
16+{
17+
18+ Minimum set of Python function declarations for module libraries.
19+
20+ Author: Phil (MacPgmr at fastermac.net).
21+
22+ To add other Python function declarations, see the Python header
23+ files (.h) included with every Python distribution.
24+
25+}
26+
27+{$IFDEF FPC}
28+ {$MODE Delphi}
29+{$ENDIF}
30+
31+interface
32+
33+{$DEFINE IS32BIT}
34+{$IFDEF CPUX64} {Delphi}
35+ {$UNDEF IS32BIT}
36+{$ENDIF}
37+{$IFDEF CPU64} {FPC}
38+ {$UNDEF IS32BIT}
39+{$ENDIF}
40+
41+const
42+{$IFDEF MSWINDOWS}
43+ {$IFDEF USE_PYTHON23}
44+ PythonLib = 'python23.dll';
45+ {$ELSE}
46+ PythonLib = 'python27.dll';
47+ {$ENDIF}
48+{$ENDIF}
49+
50+{$IFDEF LINUX}
51+ {$IFDEF USE_PYTHON23}
52+ PythonLib = 'python23.so';
53+ {$ELSE}
54+ PythonLib = 'python27.so';
55+ {$ENDIF}
56+{$ENDIF}
57+
58+{$IFDEF DARWIN}
59+ PythonLib = ''; //Link against Python.framework (-k'-framework Python').
60+ // To link against a specific version of Python, pass the
61+ // full path to that version's library instead, for example,
62+ // -k'/System/Library/Frameworks/Python.framework/Versions/2.6/Python'
63+{$ENDIF}
64+
65+type
66+{$IFDEF IS32BIT}
67+ c_long = LongInt;
68+ c_ulong = LongWord;
69+ c_int = LongInt;
70+{$ELSE}
71+ c_long = Int64;
72+ c_ulong = UInt64;
73+ c_int = Int64; //"int" also appears to be 8 bytes with 64-bit Python
74+{$ENDIF}
75+
76+ PyMethodDef = packed record
77+ name : PAnsiChar; //Python function name
78+ meth : Pointer; //Address of function that implements it
79+ flags : c_int; //METH_xxx flags; describe function's arguments
80+ doc : PAnsiChar; //Description of funtion
81+ end;
82+
83+ PyObject = Pointer;
84+
85+const
86+{$IFDEF USE_PYTHON23}
87+ PYTHON_API_VERSION = 1012; //Also used with Python 2.4
88+{$ELSE}
89+ PYTHON_API_VERSION = 1013;
90+{$ENDIF}
91+ METH_VARARGS = 1;
92+
93+function Py_InitModule( name : PAnsiChar;
94+ var methods : PyMethodDef;
95+ doc : PAnsiChar = nil;
96+ self : PyObject = nil;
97+ apiver : c_int = PYTHON_API_VERSION) : PyObject; cdecl;
98+ external PythonLib name {$IFDEF IS32BIT}'Py_InitModule4'{$ELSE}'Py_InitModule4_64'{$ENDIF};
99+
100+function PyArg_ParseTuple(args : PyObject;
101+ format : PAnsiChar) : c_int; cdecl; varargs; external PythonLib;
102+ //Note varargs allows us to simulate C variable number of arguments (...).
103+
104+function PyInt_FromLong(along : c_long) : PyObject; cdecl; external PythonLib;
105+
106+function PyLong_FromLong(along : c_long) : PyObject; cdecl; external PythonLib;
107+
108+function PyLong_FromUnsignedLong(aulong : c_ulong) : PyObject; cdecl; external PythonLib;
109+
110+function PyString_FromString(astr : PAnsiChar) : PyObject; cdecl; external PythonLib;
111+
112+implementation
113+
114+
115+end.
116+```
117+
118+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.
119+
120+## A simple module example
121+
122+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:
123+
124+``` pascal
125+library PyMinMod;
126+
127+{
128+
129+ Minimal Python module (library) that includes simple functions.
130+
131+ Author: Phil (MacPgmr at fastermac.net).
132+
133+ To compile this module:
134+ - With Delphi: Open this .dpr file and compile.
135+ - With Lazarus: Open .lpi file and compile.
136+
137+ To deploy module:
138+ - With Delphi: Rename compiled .dll to .pyd.
139+ - With Lazarus on Windows: Rename compiled .so to .pyd.
140+ - With Lazarus on OS X and Linux: .so extension is okay.
141+
142+}
143+
144+uses
145+ SysUtils,
146+ PyAPI;
147+
148+
149+function SumTwoIntegers(Self : PyObject;
150+ Args : PyObject) : PyObject; cdecl;
151+var
152+ Arg1 : Integer;
153+ Arg2 : Integer;
154+begin
155+ PyArg_ParseTuple(Args, 'ii', @Arg1, @Arg2); //Get the two int arguments
156+ Result := PyInt_FromLong(Arg1 + Arg2); //Add them together and return sum
157+// Result := PyLong_FromLong(Arg1 + Arg2);
158+// Result := PyLong_FromUnsignedLong(Arg1 + Arg2);
159+end;
160+
161+
162+function ConcatTwoStrings(Self : PyObject;
163+ Args : PyObject) : PyObject; cdecl;
164+ {From Python documentation for "s" format: "You must not provide storage for
165+ the string itself; a pointer to an existing string is stored into the
166+ character pointer variable whose address you pass."
167+ From Python documentation for PyString_FromString: "Return a new string
168+ object with a copy of the string v as value on success".}
169+var
170+ Arg1 : PAnsiChar;
171+ Arg2 : PAnsiChar;
172+begin
173+ PyArg_ParseTuple(Args, 'ss', @Arg1, @Arg2); //Get the two string arguments
174+ Result := PyString_FromString(PAnsiChar(AnsiString(Arg1) + AnsiString(Arg2)));
175+ //Concatenate and return string
176+end;
177+
178+
179+var
180+ Methods : packed array [0..2] of PyMethodDef;
181+
182+procedure initPyMinMod; cdecl;
183+begin
184+ Methods[0].name := 'SumTwoIntegers';
185+ Methods[0].meth := @SumTwoIntegers;
186+ Methods[0].flags := METH_VARARGS;
187+ Methods[0].doc := 'Tests passing ints to and from module function';
188+
189+ Methods[1].name := 'ConcatTwoStrings';
190+ Methods[1].meth := @ConcatTwoStrings;
191+ Methods[1].flags := METH_VARARGS;
192+ Methods[1].doc := 'Tests passing strings to and from module function';
193+
194+ Methods[2].name := nil;
195+ Methods[2].meth := nil;
196+ Methods[2].flags := 0;
197+ Methods[2].doc := nil;
198+
199+ Py_InitModule('PyMinMod', Methods[0]);
200+end;
201+
202+
203+exports
204+ initPyMinMod;
205+
206+end.
207+```
208+
209+You can add more functions to the module by following the example in initPyMinMod.
210+
211+With Delphi, just open PyMinMod.dpr and compile.
212+
213+With FPC, just compile from the command line. For example, to create a 64-bit module on OS X:
214+
215+``` pascal
216+ppcx64 -Sd -k'-framework Python' -oPyMinMod.so PyMinMod.dpr
217+```
218+
219+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:
220+
221+``` xml
222+<?xml version="1.0"?>
223+<CONFIG>
224+ <ProjectOptions>
225+ <PathDelim Value="/"/>
226+ <Version Value="6"/>
227+ <General>
228+ <MainUnit Value="0"/>
229+ <IconPath Value="./"/>
230+ <TargetFileExt Value=".exe"/>
231+ <UseAppBundle Value="False"/>
232+ <ActiveEditorIndexAtStart Value="0"/>
233+ </General>
234+ <PublishOptions>
235+ <Version Value="2"/>
236+ <IgnoreBinaries Value="False"/>
237+ <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
238+ <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
239+ </PublishOptions>
240+ <RunParams>
241+ <local>
242+ <FormatVersion Value="1"/>
243+ <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
244+ </local>
245+ </RunParams>
246+ <Units Count="1">
247+ <Unit0>
248+ <Filename Value="PyMinMod.dpr"/>
249+ <IsPartOfProject Value="True"/>
250+ <UnitName Value="PyMinMod"/>
251+ <CursorPos X="1" Y="1"/>
252+ <TopLine Value="1"/>
253+ <EditorIndex Value="0"/>
254+ <UsageCount Value="20"/>
255+ <Loaded Value="True"/>
256+ <SyntaxHighlighter Value="Delphi"/>
257+ </Unit0>
258+ </Units>
259+ <JumpHistory Count="0" HistoryIndex="-1"/>
260+ </ProjectOptions>
261+ <CompilerOptions>
262+ <Version Value="8"/>
263+ <Target>
264+ <Filename Value="PyMinMod.so"/>
265+ </Target>
266+ <Parsing>
267+ <SyntaxOptions>
268+ <SyntaxMode Value="Delphi"/>
269+ <CStyleOperator Value="False"/>
270+ <AllowLabel Value="False"/>
271+ <CPPInline Value="False"/>
272+ </SyntaxOptions>
273+ </Parsing>
274+ <CodeGeneration>
275+ <Checks>
276+ <IOChecks Value="True"/>
277+ <RangeChecks Value="True"/>
278+ <OverflowChecks Value="True"/>
279+ <StackChecks Value="True"/>
280+ </Checks>
281+ </CodeGeneration>
282+ <Linking>
283+ <Options>
284+ <PassLinkerOptions Value="True"/>
285+ <LinkerOptions Value="-framework Python"/>
286+ <Win32>
287+ <GraphicApplication Value="True"/>
288+ </Win32>
289+ <ExecutableType Value="Library"/>
290+ </Options>
291+ </Linking>
292+ <Other>
293+ <CompilerPath Value="$(CompPath)"/>
294+ </Other>
295+ </CompilerOptions>
296+ <Debugging>
297+ <Exceptions Count="2">
298+ <Item1>
299+ <Name Value="ECodetoolError"/>
300+ </Item1>
301+ <Item2>
302+ <Name Value="EFOpenError"/>
303+ </Item2>
304+ </Exceptions>
305+ </Debugging>
306+</CONFIG>
307+```
308+
309+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 three lines:
310+
311+``` python
312+import PyMinMod
313+print "Value returned by SumTwoIntegers: " + str(PyMinMod.SumTwoIntegers(1, 2))
314+print "Value returned by ConcatTwoStrings: " + PyMinMod.ConcatTwoStrings("Hey ", "there")
315+```
316+
317+Note that Python is case sensitive so if your compiled module is in lower-case, change the "PyMinMod" references accordingly.
318+
319+Now open a terminal window and run the script like this:
320+
321+``` bash
322+python test.py
323+```
324+
325+The script should output the following line:
326+
327+ Value returned by SumTwoIntegers: 3
328+ Value returned by ConcatTwoStrings: Hey there
329+
330+What we've done here is create a simple Python module that implements two Python functions. Once you've imported the module into your Python script, you can use the functions in the same way that you use built-in Python functions.
331+
332+## Using your module in a host application
333+
334+If you have OpenOffice or NeoOffice installed, you can test running your module from a Python macro.
335+
336+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.
337+
338+``` python
339+# Python macro that tests Pascal module by creating new document and inserting module function result.
340+
341+import sys, os
342+# Tell Python where to look for our Pascal module
343+if sys.platform == 'win32':
344+ sys.path.append(os.path.expanduser('~\Application Data\OpenOffice.org2\user\Scripts\python\Library1'))
345+elif sys.platform == 'darwin':
346+ sys.path.append(os.path.expanduser('~/Library/Preferences/NeoOffice-2.2/user/Scripts/python/Library1'))
347+
348+# Import Pascal module that contains SumTwoIntegers function
349+import PyMinMod
350+
351+import uno
352+
353+def TestMinMod():
354+ ctx = uno.getComponentContext()
355+ smgr = ctx.ServiceManager
356+ desktop = smgr.createInstance('com.sun.star.frame.Desktop')
357+ doc = desktop.loadComponentFromURL('private:factory/swriter', '_blank', 0, ())
358+ textCursor = doc.Text.createTextCursor()
359+ doc.Text.insertString(textCursor, 'Sum of 1 + 2 = ' + str(PyMinMod.SumTwoIntegers(1, 2)), 0)
360+```
361+
362+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:
363+
364+ -dUSE_PYTHON23
365+
366+With Delphi, enter USE_PYTHON23 on the Project Options dialog's Directories/Conditionals tab.
367+
368+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.
369+
370+## See also
371+
372+- [[Python4Delphi]]
373+
374+---
375+*From the Free Pascal wiki, page "Developing Python Modules with Pascal", as it stood on 2023-06-03 after a change by AMXS52, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
376+Category: Other programming languages
377+Category: Python
378+Category: Tutorials

Back to history