PyComp / python_doc_md /Advanced /c-api /extension-modules.md
ITookAPill's picture
PyComp First Commit
9273228
|
Raw
History Blame Contribute Delete
13.1 kB
# Defining extension modules {#extension-modules}
A C extension for CPython is a shared library (for example, a `.so` file on Linux, `.pyd` DLL on Windows), which is loadable into the Python process (for example, it is compiled with compatible compiler settings), and which exports an `export hook`{.interpreted-text role="dfn"} function (or an old-style `initialization function <extension-pyinit>`{.interpreted-text role="ref"}).
To be importable by default (that is, by `importlib.machinery.ExtensionFileLoader`{.interpreted-text role="py:class"}), the shared library must be available on `sys.path`{.interpreted-text role="py:attr"}, and must be named after the module name plus an extension listed in `importlib.machinery.EXTENSION_SUFFIXES`{.interpreted-text role="py:attr"}.
:::: note
::: title
Note
:::
Building, packaging and distributing extension modules is best done with third-party tools, and is out of scope of this document. One suitable tool is Setuptools, whose documentation can be found at <https://setuptools.pypa.io/en/latest/setuptools.html>.
::::
## Extension export hook
::: versionadded
3.15
Support for the `PyModExport_{<name>}`{.interpreted-text role="samp"} export hook was added in Python 3.15. The older way of defining modules is still available: consult either the `extension-pyinit`{.interpreted-text role="ref"} section or earlier versions of this documentation if you plan to support earlier Python versions.
:::
The export hook must be an exported function with the following signature:
For modules with ASCII-only names, the `export hook <extension-export-hook>`{.interpreted-text role="ref"} must be named `PyModExport_{<name>}`{.interpreted-text role="samp"}, with `<name>` replaced by the module\'s name.
For non-ASCII module names, the export hook must instead be named `PyModExportU_{<name>}`{.interpreted-text role="samp"} (note the `U`), with `<name>` encoded using Python\'s *punycode* encoding with hyphens replaced by underscores. In Python:
``` python
def hook_name(name):
try:
suffix = b'_' + name.encode('ascii')
except UnicodeEncodeError:
suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
return b'PyModExport' + suffix
```
The export hook returns an array of `PyModuleDef_Slot`{.interpreted-text role="c:type"} entries, terminated by an entry with a slot ID of `0`. These slots describe how the module should be created and initialized.
This array must remain valid and constant until interpreter shutdown. Typically, it should use `static` storage. Prefer using the `Py_mod_create`{.interpreted-text role="c:macro"} and `Py_mod_exec`{.interpreted-text role="c:macro"} slots for any dynamic behavior.
The export hook may return `NULL` with an exception set to signal failure.
It is recommended to define the export hook function using a helper macro:
> Declare an extension module export hook. This macro:
>
> - specifies the `PyModuleDef_Slot*`{.interpreted-text role="c:expr"} return type,
> - adds any special linkage declarations required by the platform, and
> - for C++, declares the function as `extern "C"`.
For example, a module called `spam` would be defined like this:
``` c
PyABIInfo_VAR(abi_info);
static PyModuleDef_Slot spam_slots[] = {
{Py_mod_abi, &abi_info},
{Py_mod_name, "spam"},
{Py_mod_init, spam_init_function},
...
{0, NULL},
};
PyMODEXPORT_FUNC
PyModExport_spam(void)
{
return spam_slots;
}
```
The export hook is typically the only non-`static` item defined in the module\'s C source.
The hook should be kept short \-- ideally, one line as above. If you do need to use Python C API in this function, it is recommended to call `PyABIInfo_Check(&abi_info, "modulename")` first to raise an exception, rather than crash, in common cases of ABI mismatch.
:::: note
::: title
Note
:::
It is possible to export multiple modules from a single shared library by defining multiple export hooks. However, importing them requires a custom importer or suitably named copies/links of the extension file, because Python\'s import machinery only finds the function corresponding to the filename. See the [Multiple modules in one library](https://peps.python.org/pep-0489/#multiple-modules-in-one-library) section in `489`{.interpreted-text role="pep"} for details.
::::
## Multi-phase initialization
The process of creating an extension module follows several phases:
- Python finds and calls the export hook to get information on how to create the module.
- Before any substantial code is executed, Python can determine which capabilities the module supports, and it can adjust the environment or refuse loading an incompatible extension. Slots like `Py_mod_abi`{.interpreted-text role="c:data"}, `Py_mod_gil`{.interpreted-text role="c:data"} and `Py_mod_multiple_interpreters`{.interpreted-text role="c:data"} influence this step.
- By default, Python itself then creates the module object \-- that is, it does the equivalent of calling `~object.__new__`{.interpreted-text role="py:meth"} when creating an object. This step can be overridden using the `Py_mod_create`{.interpreted-text role="c:data"} slot.
- Python sets initial module attributes like `~module.__package__`{.interpreted-text role="attr"} and `~module.__loader__`{.interpreted-text role="attr"}, and inserts the module object into `sys.modules`{.interpreted-text role="py:attr"}.
- Afterwards, the module object is initialized in an extension-specific way \-- the equivalent of `~object.__init__`{.interpreted-text role="py:meth"} when creating an object, or of executing top-level code in a Python-language module. The behavior is specified using the `Py_mod_exec`{.interpreted-text role="c:data"} slot.
This is called *multi-phase initialization* to distinguish it from the legacy (but still supported) `single-phase initialization <single-phase-initialization>`{.interpreted-text role="ref"}, where an initialization function returns a fully constructed module.
::: versionchanged
3.5
Added support for multi-phase initialization (`489`{.interpreted-text role="pep"}).
:::
## Multiple module instances
By default, extension modules are not singletons. For example, if the `sys.modules`{.interpreted-text role="py:attr"} entry is removed and the module is re-imported, a new module object is created and, typically, populated with fresh method and type objects. The old module is subject to normal garbage collection. This mirrors the behavior of pure-Python modules.
Additional module instances may be created in `sub-interpreters <sub-interpreter-support>`{.interpreted-text role="ref"} or after Python runtime reinitialization (`Py_Finalize`{.interpreted-text role="c:func"} and `Py_Initialize`{.interpreted-text role="c:func"}). In these cases, sharing Python objects between module instances would likely cause crashes or undefined behavior.
To avoid such issues, each instance of an extension module should be *isolated*: changes to one instance should not implicitly affect the others, and all state owned by the module, including references to Python objects, should be specific to a particular module instance. See `isolating-extensions-howto`{.interpreted-text role="ref"} for more details and a practical guide.
A simpler way to avoid these issues is `raising an error on repeated initialization <isolating-extensions-optout>`{.interpreted-text role="ref"}.
All modules are expected to support `sub-interpreters <sub-interpreter-support>`{.interpreted-text role="ref"}, or otherwise explicitly signal a lack of support. This is usually achieved by isolation or blocking repeated initialization, as above. A module may also be limited to the main interpreter using the `Py_mod_multiple_interpreters`{.interpreted-text role="c:data"} slot.
## `PyInit` function {#extension-pyinit}
::: deprecated
3.15
This functionality is `soft deprecated`{.interpreted-text role="term"}. It will not get new features, but there are no plans to remove it.
:::
Instead of `PyModExport_modulename`{.interpreted-text role="c:func"}, an extension module can define an older-style `initialization function`{.interpreted-text role="dfn"} with the signature:
Its name should be `PyInit_{<name>}`{.interpreted-text role="samp"}, with `<name>` replaced by the name of the module. For non-ASCII module names, use `PyInitU_{<name>}`{.interpreted-text role="samp"} instead, with `<name>` encoded in the same way as for the `export hook <extension-export-hook>`{.interpreted-text role="ref"} (that is, using Punycode with underscores).
If a module exports both `PyInit_{<name>}`{.interpreted-text role="samp"} and `PyModExport_{<name>}`{.interpreted-text role="samp"}, the `PyInit_{<name>}`{.interpreted-text role="samp"} function is ignored.
Like with `PyMODEXPORT_FUNC`{.interpreted-text role="c:macro"}, it is recommended to define the initialization function using a helper macro:
> Declare an extension module initialization function. This macro:
>
> - specifies the `PyObject*`{.interpreted-text role="c:expr"} return type,
> - adds any special linkage declarations required by the platform, and
> - for C++, declares the function as `extern "C"`.
Normally, the initialization function (`PyInit_modulename`) returns a `PyModuleDef`{.interpreted-text role="c:type"} instance with non-`NULL` `~PyModuleDef.m_slots`{.interpreted-text role="c:member"}. This allows Python to use `multi-phase initialization <multi-phase-initialization>`{.interpreted-text role="ref"}.
Before it is returned, the `PyModuleDef` instance must be initialized using the following function:
> Ensure a module definition is a properly initialized Python object that correctly reports its type and a reference count.
>
> Return *def* cast to `PyObject*`, or `NULL` if an error occurred.
>
> Calling this function is required before returning a `PyModuleDef`{.interpreted-text role="c:type"} from a module initialization function. It should not be used in other contexts.
>
> Note that Python assumes that `PyModuleDef` structures are statically allocated. This function may return either a new reference or a borrowed one; this reference must not be released.
>
> ::: versionadded
> 3.5
> :::
For example, a module called `spam` would be defined like this:
``` c
static struct PyModuleDef spam_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "spam",
...
};
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModuleDef_Init(&spam_module);
}
```
### Legacy single-phase initialization {#single-phase-initialization}
::: deprecated
3.15
Single-phase initialization is `soft deprecated`{.interpreted-text role="term"}. It is a legacy mechanism to initialize extension modules, with known drawbacks and design flaws. Extension module authors are encouraged to use multi-phase initialization instead.
However, there are no plans to remove support for it.
:::
In single-phase initialization, the old-style `initialization function <extension-pyinit>`{.interpreted-text role="ref"} (`PyInit_modulename`) should create, populate and return a module object. This is typically done using `PyModule_Create`{.interpreted-text role="c:func"} and functions like `PyModule_AddObjectRef`{.interpreted-text role="c:func"}.
Single-phase initialization differs from the `default <multi-phase-initialization>`{.interpreted-text role="ref"} in the following ways:
- Single-phase modules are, or rather *contain*, "singletons".
When the module is first initialized, Python saves the contents of the module\'s `__dict__` (that is, typically, the module\'s functions and types).
For subsequent imports, Python does not call the initialization function again. Instead, it creates a new module object with a new `__dict__`, and copies the saved contents to it. For example, given a single-phase module `_testsinglephase` [^1] that defines a function `sum` and an exception class `error`:
``` python
>>> import sys
>>> import _testsinglephase as one
>>> del sys.modules['_testsinglephase']
>>> import _testsinglephase as two
>>> one is two
False
>>> one.__dict__ is two.__dict__
False
>>> one.sum is two.sum
True
>>> one.error is two.error
True
```
The exact behavior should be considered a CPython implementation detail.
- To work around the fact that `PyInit_modulename` does not take a *spec* argument, some state of the import machinery is saved and applied to the first suitable module created during the `PyInit_modulename` call. Specifically, when a sub-module is imported, this mechanism prepends the parent package name to the name of the module.
A single-phase `PyInit_modulename` function should create "its" module object as soon as possible, before any other module objects can be created.
- Non-ASCII module names (`PyInitU_modulename`) are not supported.
- Single-phase modules support module lookup functions like `PyState_FindModule`{.interpreted-text role="c:func"}.
- The module\'s `PyModuleDef.m_slots`{.interpreted-text role="c:member"} must be NULL.
[^1]: `_testsinglephase` is an internal module used in CPython\'s self-test suite; your installation may or may not include it.