Skip to content

Frequently asked questions

Salvador Díaz Fau edited this page Oct 27, 2026 · 18 revisions

Where are all the low level Python functions?

Python4Pascal has class functions that use all the low level Python function internally.

You can easily find them replacing the underscore with a dot and adding a "T" at the start to the function name.

For example, the function PyDict_GetItem is used by the TPyDict.GetItem function. All the low level functions that start with "PyDict_" are used by the TPyDict class.

All Python4Pascal unit names start with "uP4P." and the rest of the name is the name of the class without the "T". For example, the TPyDict class is available in the uP4P.PyDict.pas unit.

If you decide to use low level functions you can find them in the uP4P.LibFunctions unit.

How does Python4Pascal handle the Python objects lifecycle?

Python keeps track of all the objects used by the code and deletes them periodically with the garbage collector when they are no longer used.

All object types in Python are extensions of an internal Python type called PyObject.

Speaking in Delphi/Lazarus terms, PyObjects are a record which contain a reference count field. The garbage collector will delete a PyObject if the reference count reaches zero.

Using Python objects with the low level API functions can be tricky because you need to know exactly when to increase or decrease the reference count of all objects in order to avoid memory leaks.

Python4Pascal uses the IPyObject interface to simplify using the Python objects.

All interfaces are automatically destroyed when they go out of scope and IPyObject will decrease the reference count at that moment.

All the constructor functions return an IPyObject instance and most of the functions use IPyObject parameters. The Python4Pascal functions will increase or decrease the reference count automatically.

There are however some occasions when you need to handle PyObjects directly. For example, in callback methods.

It's recommended to read the Python documentation about reference counting.

How does Python4Pascal handle the immortal Python objects?

A few Python types are never destroyed and they require some especial handling. Some examples of immortal objects are the None objects and the Boolean objects.

Due to internal changes in recent versions of Python it's recommended to use the TPyNone and TPyBool functions to get instances of these objects.

As you can see, some of the functions in those classes return a IPyObject instance and some return a PPyObject pointer. The functions returning IPyObject instances can be used in Python4Pascal functions. In case of callback methods you may have to use the functions that return a PPyObject pointer as a callback result.

How are strings handled by Python4Pascal?

Python4Pascal uses the native string type for each IDE and converts the strings to the string format used by the Python API automatically.

How can I run Python code in a Delphi/Lazarus thread?

Python4Pascal includes the TP4PThread class in the uP4P.Thread unit to simplify running Python code in a Delphi thread.

The SimplePythonThread demo shows how to use the TP4PThread class.

Follow these steps:

  • Call GlobalP4PEngine.SaveThreadState in the main thread to detach the attached thread state and allow other threads to run Python code.
  • Create a TP4PThread instance and use the TP4PThread.OnExecutePython event to execute the Python code. The SimplePythonThread demo do basically the same. It overrides TP4PThread.doOnExecutePython to get the same results.
  • Call GlobalP4PEngine.RestoreThreadState when the thread is terminated to set the attached thread state. This allows you to run Python in the main thread again. SimplePythonThread uses the TP4PThread.OnTerminate event to call GlobalP4PEngine.RestoreThreadState.

Notice that it's possible to use different values for the TP4PThread.ThreadInit property in order to change the thread initialization method. Read this article to know all the low details about running Python in threads.

How can I create a Python module?

Python4Pascal includes the TCustomPyModule class in the uP4P.CustomPyModule unit to simplify the creation of custom Python modules.

Recent Python versions have changed the way modules are initialized and for that reason we should include the old way and the new way in our custom modules until the Python project fully deprecates the old way.

The TCustomPyModule.UseLegacyAPI property controls how the module is initialized.

We'll have to use a custom TPyModuleDef class for the old module initialization, and a custom TPyModuleSlotManager class for the new module initialization.

The SimplePythonModule shows how to use all these classes in a simple module.

SimplePythonModule has a TTestModule class that inherits from TCustomPyModule and it overrides the CreateModuleDef and CreateSlotManager procedures to create a TPyTestModuleDef instance and a TPyTestSlotManager instance.

TPyTestModuleDef inherits from TPyModuleDef and overrides the AddAllMethods procedure to add our custom module methods.

TPyTestSlotManager inherits from TPyModuleSlotManager and overrides the AddAllMethods procedure to add our custom module methods.

All the AddAllMethods procedures call the AddMethod function to create a custom method with a method name, the docstring, a callback function and the flags that will indicate how the call should be constructed.

The SimplePythonModule demo creates several module methods that use the METH_VARARGS flag. That means that the callback expects two PPyObject values. The first one is the self object and the second parameter is called args and it's a tuple object representing all arguments.

The callbacks can be implemented in any way you want but the demo calls GlobalP4PEngine.SearchModule to search for the module and then calls the module function that corresponds to that callback.

For example, the demo creates a method called is_even with the mytest_is_even callback that will call TTestModule.NumberIsEven.

Notice that the callbacks and the module functions like NumberIsEven have to deal with low level arguments and return values. We can wrap the arguments with TPyObject.Wrap and use TPyTuple.GetItem to get the argument value.

The return value must be a PPyObject and in the case of NumberIsEven we use TPyBool.ReturnTrue or TPyBool.ReturnFalse to return a valid Py_True or Py_False value. It can also use TPyNone.ReturnNone to return a valid Py_None value if the callback received incorrect arguments.

If you use custom modules in the main application thread you can destroy them right before the DestroyGlobalP4PEngine call but if you use custom modules in Delphi/Lazarus threads you'll have to use the TP4PThread.OnFinalizeModules and TP4PThread.OnDestroyModules events to call TCustomHeapType.Finalize and TCustomHeapType.Free respectively. See the SimplePythonThreadWithModule demo for all the details.

See the code comments in TCustomPyModule, TPyMethodDef and TPyModuleSlotManager for more details.

How can I use Python classes from Delphi or Lazarus code?

You can use Python classes with variants and using wrappers.

The PythonVariants demo shows how easy is to create a Python class instance and use it directly from Delphi. The TMainForm.Run2BtnClick procedure creates an instance of the MyClass class and uses the properties and methods as if MyClass was a normal Delphi class.

If you prefer to have full control of the data type conversions or you prefer to avoid using variants then use custom wrapper as you can see in the PythonClassWrapper demo.

PythonClassWrapper has a TMyClass class that uses the Python4Pascal functions to wrap the Python class called MyClass. All TMyClass function results and arguments use Delphi/Lazarus types.

Some old Delphi versions have known memory leaks while using variants and at the time of this writing Lazarus has issues using custom variants. In those cases it's recomended to use a Python class wrapper.

How to send information between Python code and Delphi/Lazarus code?

There are several ways to send and receive information from/to Python:

  1. The quickest way is just running custom Python code and add your data in the string variable with the Python code. To get the results just use the GlobalP4PEngine.OnOutputMessage event to parse the output messages with the result data.
  2. A much better way is to use a custom module, variants or wrapper classes. The functions in that module can be used by the Python code to send data in the function parameters to Delphi/Lazarus. The variants or the wrapper classes can be used by the Delphi/Lazarus code to call Python functions and get the results directly.

How can I create a Python extension module using Delphi/Lazarus?

It's possible to create an extension module with Python4Pascal in Delphi or Lazarus.

Extension modules are in fact a normal library (dll/so/dylib) that exports a especial function used by Python to initialize a custom module. In Windows the module extensions change the file name extension to "pyd" instead of "dll".

Recent Python versions have changed the name of the exported function. Older Python versions initialize extensions with PyInit_extensionname. Python 3.15 initializes extensions with PyModExport_extensionname.

The SimplePythonExtension demo shows how to create a simple extension module.

Notice that the exported functions create GlobalP4PEngine and then call GlobalP4PEngine.LoadInExtensionModule before creating the module.

It's also necessary to call TCustomPyModule.Finalize before the DestroyGlobalP4PEngine call. The module must be destroyed after that.

How can I run Python without installing it in the operating system?

Python4Pascal can also use the embeddable Python package available in python.org.

In the case of Windows follow these steps:

  1. Download and decompress the embeddable Python package.
  2. Open the directory where you decompressed the package and decompress the python*.zip file in a directory called "Lib".
  3. Use the full path to the biggest python*.dll file as the parameter in GlobalP4PEngine.Load.

In the case of Android follow these steps:

  1. Download and decompress the embeddable Python package.
  2. Open the prefix/lib directory and compress all the contents of that directory in a file called python.zip.
  3. python.zip must be deployed to the "assets\internal" directory.
  4. In FMX, the System.StartUpCopy unit will copy python.zip to the documents directory at startup.
  5. Decompress the python.zip file in the documents directory at runtime to a "python" subdirectory.
  6. Concatenate the full paths to "python/python*.*" and "python/python*.*/site-packages" with the default path separator and set the new value to GlobalP4PEngine.ModuleSearchPaths.
  7. Use the full path to the "libpython*.*.so" file as the parameter in GlobalP4PEngine.Load.

See the FMX version of the SimplePythonCodeRun demo for all the details.