Sign in Join
Wiki copytemporary

Developing Python Modules with Pascal/ru: what changed

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

Title: Developing Python Modules with Pascal/ru

OldNewLine
1+## Введение
2+
3+Python — популярный скриптовый язык, который часто используется для добавления функциональности другими приложениями, такими как [OpenOffice](https://ru.wikipedia.org/wiki/OpenOffice.org) и [Quantum GIS](https://ru.wikipedia.org/wiki/QGIS). На вашем компьютере уже может быть установлена какая-то версия Python. Если нет, можно загрузить Python с официального вебсайта: <http://www.python.org/>.
4+
5+Можно расширять Python путём разработки скомпилированных библиотек (называемых модулями), которые добавляют функции в Python. В этом разделе рассматривается как создать библиотеку на Pascal (Delphi или Free Pascal).
6+
7+Эта статья описывает очень низкий уровень, подход «с нуля». Для установки моста Python-Pascal, смотрите [[Python4Delphi/ru]].
8+
9+## Минимальное Python API
10+
11+Скопируйте и сохраните с помощью текстового в файл PyAPI.pas:
12+
13+``` pascal
14+unit PyAPI;
15+
16+{
17+
18+ Минимальный набор деклараций функций Python для библиотек модулей.
19+
20+ Author: Фил (MacPgmr на fastermac.net).
21+
22+ Для добавления других деклараций функций Python, смотрите заголовочные файлы (.h), включённые в любой дистрибутив Python.
23+
24+}
25+
26+{$IFDEF FPC}
27+ {$MODE Delphi}
28+{$ENDIF}
29+
30+interface
31+
32+{$DEFINE IS32BIT}
33+{$IFDEF CPUX64} {Delphi}
34+ {$UNDEF IS32BIT}
35+{$ENDIF}
36+{$IFDEF CPU64} {FPC}
37+ {$UNDEF IS32BIT}
38+{$ENDIF}
39+
40+const
41+{$IFDEF MSWINDOWS}
42+ {$IFDEF USE_PYTHON23}
43+ PythonLib = 'python23.dll';
44+ {$ELSE}
45+ PythonLib = 'python27.dll';
46+ {$ENDIF}
47+{$ENDIF}
48+
49+{$IFDEF LINUX}
50+ {$IFDEF USE_PYTHON23}
51+ PythonLib = 'python23.so';
52+ {$ELSE}
53+ PythonLib = 'python27.so';
54+ {$ENDIF}
55+{$ENDIF}
56+
57+{$IFDEF DARWIN}
58+ PythonLib = ''; //Связывает с Python.framework (-k'-framework Python').
59+ // Для ссылки на конкретную версию Python, передайте
60+ // полный путь к этой версии библиотеки, например,
61+ // -k'/System/Library/Frameworks/Python.framework/Versions/2.6/Python'
62+{$ENDIF}
63+
64+type
65+{$IFDEF IS32BIT}
66+ c_long = LongInt;
67+ c_ulong = LongWord;
68+ c_int = LongInt;
69+{$ELSE}
70+ c_long = Int64;
71+ c_ulong = UInt64;
72+ c_int = Int64; //"int" также будет 8-байтным в 64-битном Python
73+{$ENDIF}
74+
75+ PyMethodDef = packed record
76+ name : PAnsiChar; //имя функции Python
77+ meth : Pointer; //Адрес реализуемой функции
78+ flags : c_int; //METH_xxx флаги; описывает аргументы функции
79+ doc : PAnsiChar; //Описание функции
80+ end;
81+
82+ PyObject = Pointer;
83+
84+const
85+{$IFDEF USE_PYTHON23}
86+ PYTHON_API_VERSION = 1012; //Используется также вместе с Python 2.4
87+{$ELSE}
88+ PYTHON_API_VERSION = 1013;
89+{$ENDIF}
90+ METH_VARARGS = 1;
91+
92+function Py_InitModule( name : PAnsiChar;
93+ var methods : PyMethodDef;
94+ doc : PAnsiChar = nil;
95+ self : PyObject = nil;
96+ apiver : c_int = PYTHON_API_VERSION) : PyObject; cdecl;
97+ external PythonLib name {$IFDEF IS32BIT}'Py_InitModule4'{$ELSE}'Py_InitModule4_64'{$ENDIF};
98+
99+function PyArg_ParseTuple(args : PyObject;
100+ format : PAnsiChar) : c_int; cdecl; varargs; external PythonLib;
101+ //Заметьте, что varargs позволяет нам эмулировать переменное количество аргументов в C (...).
102+
103+function PyInt_FromLong(along : c_long) : PyObject; cdecl; external PythonLib;
104+
105+function PyLong_FromLong(along : c_long) : PyObject; cdecl; external PythonLib;
106+
107+function PyLong_FromUnsignedLong(aulong : c_ulong) : PyObject; cdecl; external PythonLib;
108+
109+function PyString_FromString(astr : PAnsiChar) : PyObject; cdecl; external PythonLib;
110+
111+implementation
112+
113+
114+end.
115+```
116+
117+Если у вас разные версии Python, определите USE_PYTHON23 как ссылку на версию 2.3 или просто измените имя библиотеки (PythonLib). Если вам нужны другие функции API Python, просто добавьте их PyAPI.pas, следуя примеру PyInt_FromLong.
118+
119+## Пример простого модуля
120+
121+Вот простая библиотека, которая использует модуль Python API. Скопируйте этот код и сохраните его в файле PyMinMod.dpr:
122+
123+``` pascal
124+library PyMinMod;
125+
126+{
127+
128+ Минимальный модуль (библиотека) Python, включающий простые функции.
129+
130+ Автор: Фил (MacPgmr на fastermac.net).
131+
132+ Для компиляции этого модуля:
133+ - На Delphi: Откройте этот файл .dpr и скомпилируйте.
134+ - На Lazarus: Откройте файл .lpi и скомпилируйте.
135+
136+ Для раздачи модуля:
137+ - С Delphi: Переименуйте скомпилированный .dll в .pyd.
138+ - С Lazarus на Windows: Переименуйте скомпилированный .so в .pyd.
139+ - С Lazarus на OS X и Linux: Оставьте расширение .so extension.
140+
141+}
142+
143+uses
144+ SysUtils,
145+ PyAPI;
146+
147+
148+function SumTwoIntegers(Self : PyObject;
149+ Args : PyObject) : PyObject; cdecl;
150+var
151+ Arg1 : Integer;
152+ Arg2 : Integer;
153+begin
154+ PyArg_ParseTuple(Args, 'ii', @Arg1, @Arg2); //Получает 2 аргумента типа int
155+ Result := PyInt_FromLong(Arg1 + Arg2); //Суммируем их и возвращаем sum
156+// Result := PyLong_FromLong(Arg1 + Arg2);
157+// Result := PyLong_FromUnsignedLong(Arg1 + Arg2);
158+end;
159+
160+
161+function ConcatTwoStrings(Self : PyObject;
162+ Args : PyObject) : PyObject; cdecl;
163+ {Из документации Python про формат «s»: «Вы не должны выделять память под саму строку; указатель
164+ на существующую строку сохранён в переменной типа указатель на символ (character pointer) чей адрес вы передаёте.»
165+ Из документации Python по PyString_FromString: «Вернуть новый строковый объект с копией строки v в случае успеха».
166+ }
167+var
168+ Arg1 : PAnsiChar;
169+ Arg2 : PAnsiChar;
170+begin
171+ PyArg_ParseTuple(Args, 'ss', @Arg1, @Arg2); //Получить два строковых аргумента
172+ Result := PyString_FromString(PAnsiChar(AnsiString(Arg1) + AnsiString(Arg2)));
173+ //Соединить и вернуть строку
174+end;
175+
176+
177+var
178+ Methods : packed array [0..2] of PyMethodDef;
179+
180+procedure initPyMinMod; cdecl;
181+begin
182+ Methods[0].name := 'SumTwoIntegers';
183+ Methods[0].meth := @SumTwoIntegers;
184+ Methods[0].flags := METH_VARARGS;
185+ Methods[0].doc := 'Tests passing ints to and from module function';
186+
187+ Methods[1].name := 'ConcatTwoStrings';
188+ Methods[1].meth := @ConcatTwoStrings;
189+ Methods[1].flags := METH_VARARGS;
190+ Methods[1].doc := 'Tests passing strings to and from module function';
191+
192+ Methods[2].name := nil;
193+ Methods[2].meth := nil;
194+ Methods[2].flags := 0;
195+ Methods[2].doc := nil;
196+
197+ Py_InitModule('PyMinMod', Methods[0]);
198+end;
199+
200+
201+exports
202+ initPyMinMod;
203+
204+end.
205+```
206+
207+Можно добавить в этот модуль ещё функций по примеру в initPyMinMod.
208+
209+На Delphi, просто откройте PyMinMod.dpr и скомпилируйте.
210+
211+На FPC, просто скомпилируйте из командной строки. Например, чтобы создать 64-битный модуль на OS X:
212+
213+``` pascal
214+ppcx64 -Sd -k'-framework Python' -oPyMinMod.so PyMinMod.dpr
215+```
216+
217+На Lazarus, вы, возможно, захотите создать файл проекта. Можете сделать его сами или просто скопировать этот проект и сохранить его в файле PyMinMod.lpi:
218+
219+``` xml
220+<?xml version="1.0"?>
221+<CONFIG>
222+ <ProjectOptions>
223+ <PathDelim Value="/"/>
224+ <Version Value="6"/>
225+ <General>
226+ <MainUnit Value="0"/>
227+ <IconPath Value="./"/>
228+ <TargetFileExt Value=".exe"/>
229+ <UseAppBundle Value="False"/>
230+ <ActiveEditorIndexAtStart Value="0"/>
231+ </General>
232+ <PublishOptions>
233+ <Version Value="2"/>
234+ <IgnoreBinaries Value="False"/>
235+ <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
236+ <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
237+ </PublishOptions>
238+ <RunParams>
239+ <local>
240+ <FormatVersion Value="1"/>
241+ <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
242+ </local>
243+ </RunParams>
244+ <Units Count="1">
245+ <Unit0>
246+ <Filename Value="PyMinMod.dpr"/>
247+ <IsPartOfProject Value="True"/>
248+ <UnitName Value="PyMinMod"/>
249+ <CursorPos X="1" Y="1"/>
250+ <TopLine Value="1"/>
251+ <EditorIndex Value="0"/>
252+ <UsageCount Value="20"/>
253+ <Loaded Value="True"/>
254+ <SyntaxHighlighter Value="Delphi"/>
255+ </Unit0>
256+ </Units>
257+ <JumpHistory Count="0" HistoryIndex="-1"/>
258+ </ProjectOptions>
259+ <CompilerOptions>
260+ <Version Value="8"/>
261+ <Target>
262+ <Filename Value="PyMinMod.so"/>
263+ </Target>
264+ <Parsing>
265+ <SyntaxOptions>
266+ <SyntaxMode Value="Delphi"/>
267+ <CStyleOperator Value="False"/>
268+ <AllowLabel Value="False"/>
269+ <CPPInline Value="False"/>
270+ </SyntaxOptions>
271+ </Parsing>
272+ <CodeGeneration>
273+ <Checks>
274+ <IOChecks Value="True"/>
275+ <RangeChecks Value="True"/>
276+ <OverflowChecks Value="True"/>
277+ <StackChecks Value="True"/>
278+ </Checks>
279+ </CodeGeneration>
280+ <Linking>
281+ <Options>
282+ <PassLinkerOptions Value="True"/>
283+ <LinkerOptions Value="-framework Python"/>
284+ <Win32>
285+ <GraphicApplication Value="True"/>
286+ </Win32>
287+ <ExecutableType Value="Library"/>
288+ </Options>
289+ </Linking>
290+ <Other>
291+ <CompilerPath Value="$(CompPath)"/>
292+ </Other>
293+ </CompilerOptions>
294+ <Debugging>
295+ <Exceptions Count="2">
296+ <Item1>
297+ <Name Value="ECodetoolError"/>
298+ </Item1>
299+ <Item2>
300+ <Name Value="EFOpenError"/>
301+ </Item2>
302+ </Exceptions>
303+ </Debugging>
304+</CONFIG>
305+```
306+
307+После компиляции модуля переименуйте его, если необходимо, как указано в комментариях к PyMinMod.dpr. Затем протестируйте модуль, создав простой файл test.py, который содержит эти три строки:
308+
309+``` python
310+import PyMinMod
311+print "Value returned by SumTwoIntegers: " + str(PyMinMod.SumTwoIntegers(1, 2))
312+print "Value returned by ConcatTwoStrings: " + PyMinMod.ConcatTwoStrings("Hey ", "there")
313+```
314+
315+Учтите, что Python различает большие и малые буквы, так что если ваш скомпилированный модуль в нижнем регистре, измените соответственно ссылку на "PyMinMod".
316+
317+Теперь откройте окно терминала и запустите такой скрипт:
318+
319+``` bash
320+python test.py
321+```
322+
323+Скрипт должен выдать такую строку:
324+
325+ Value returned by SumTwoIntegers: 3
326+ Value returned by ConcatTwoStrings: Hey there
327+
328+Здесь мы создали простой модуль Python, реализующий две функции Python. После того как вы импортировали модуль в свой скрипт Python, вы можете использовать функции так же, как и встроенные функции Python.
329+
330+## Использование вашего модуля в серверных приложениях
331+
332+Если у вас установлены OpenOffice или NeoOffice, можете протестировать ваш модуль с помощью макроопределений на Python.
333+
334+Сохраните этот скрипт в файл test_minmod.py, поместите его в папку, указанную в скрипте. Возможно вам придётся создать папку python\Library1 в разделе Scripts.
335+
336+``` python
337+# Макроопределение Python, тестирующее модуль на Pascal. Создаёт новый документ и вставляет результат функции модуля.
338+
339+import sys, os
340+# Укажем Python где искать наш модуль на Pascal
341+if sys.platform == 'win32':
342+ sys.path.append(os.path.expanduser('~\Application Data\OpenOffice.org2\user\Scripts\python\Library1'))
343+elif sys.platform == 'darwin':
344+ sys.path.append(os.path.expanduser('~/Library/Preferences/NeoOffice-2.2/user/Scripts/python/Library1'))
345+
346+# Импортировать модуль Pascal, содержащий функцию SumTwoIntegers
347+import PyMinMod
348+
349+import uno
350+
351+def TestMinMod():
352+ ctx = uno.getComponentContext()
353+ smgr = ctx.ServiceManager
354+ desktop = smgr.createInstance('com.sun.star.frame.Desktop')
355+ doc = desktop.loadComponentFromURL('private:factory/swriter', '_blank', 0, ())
356+ textCursor = doc.Text.createTextCursor()
357+ doc.Text.insertString(textCursor, 'Sum of 1 + 2 = ' + str(PyMinMod.SumTwoIntegers(1, 2)), 0)
358+```
359+
360+Поскольку OpenOffice вероятно включает Python 2.3, компилируйте модуль для версии 2.3, дополнив на вкладку Other диалога Lazarus Compiler Options:
361+
362+ -dUSE_PYTHON23
363+
364+В Delphi, введите USE_PYTHON23 на вкладке Directories/Conditionals диалога Project Options.
365+
366+Если надо переименуйте свой модуль, поместите его в ту же папку, что и test_minmod.py. Теперь проверьте, запустив его из OpenOffice выбрав Tools \| Macros \| Organize Macros \| Python и запустив макроопределение TestMinMod.
367+
368+## Смотрите также
369+
370+- [[Python4Delphi/ru]]
371+
372+---
373+*From the Free Pascal wiki, page "Developing Python Modules with Pascal/ru", as it stood on 2023-06-04 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).*
374+Category: Other programming languages
375+Category: Python
376+Category: Russian
377+Category: Tutorials

Back to history