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.pyApplications 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.Structuredefinitions for ABI structs and carriers;ctypessignatures 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.