Generated Python bindings

PythonTarget emits a Python package with a generated low-level layer and an author-controlled public layer:

mylib_py/
├── __init__.py
├── _lowlevel.py
└── _facade.py

Applications normally import from mylib_py. Direct ABI access remains available as mylib_py._lowlevel.

Regenerated low-level bindings

_lowlevel.py is rewritten on every build. It contains:

  • ctypes.Structure definitions for ABI structs and carriers;
  • ctypes signatures for exported entrypoints;
  • generated enums from API metadata;
  • carrier conversion and release helpers;
  • JLWError; and
  • shared-library discovery and loading.

Every structure is followed by an import-time layout check against the sizes and offsets recorded by juliac. A mismatch fails at import rather than silently interpreting memory with the wrong layout.

Do not edit this file: regeneration replaces it.

Author-editable façade

_facade.py is created only if it does not exist. Its starter content exposes idiomatic functions for recognized signatures and leaves a TODO: hand-wrap re-export when target-specific policy is required.

With @api metadata, generated functions preserve the declared public name, positional/keyword split, keyword defaults, enums, and docstring. Carrier arguments and results become the Python values listed in Supported Julia types. A non-zero status raises JLWError, whose .code and .message attributes retain the library error.

Keep _facade.py under version control. When an API changes, a safe workflow is to generate a fresh façade on a branch and merge the relevant changes into the maintained file. Simply rebuilding does not overwrite it.

Hand-written @ccallable entrypoints have no API sidecar declaration. The generator still recognizes common carrier shapes and status returns, but it cannot recover Julia-level names, keyword arguments, defaults, or docstrings. Unrecognized structs, raw ownership transfers, and ambiguous policies are deliberately left to a human.

Package exports

__init__.py is regenerated and re-exports the façade. Therefore functions added manually to _facade.py become package-level imports, while the mechanical layer stays explicitly accessible for advanced use.

Error conversion

An @api entrypoint catches exceptions and returns a JLWResult or direct JLWStatus; the façade raises JLWError on failure and returns the converted value on success:

from mylib_py import scale, JLWError

try:
    scale(bad_input)
except JLWError as err:
    print(err.code, err.message)

For a hand-written entrypoint, the same conversion applies when the return is a direct JLWStatus or a struct containing one. On success an unrecognized result struct remains a low-level ctypes value.

Library loading

For a bundled package the loader searches the configured bundle directory first, then falls back to a shared library beside the package. A privatized bundle records that it can safely coexist with other wrapped libraries. A non-privatized package warns when another JuliaLibWrapping package is already loaded in the process.

See Bundling for distribution and Multiple wrapped libraries in one process for the runtime implications.