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
| Old | New | Line | |
|---|---|---|---|
| 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 |
