Python Module Structure¶
The structure of the resulting Python module is determined by the PyBind11 binding code, which is generated by AutoPyBind11, and as such AutoPyBind11 can directly shape and restructure the Python module.
By this we mean that the structure and heirarchy of Classes, free functions, nested enums/classes, and Modules/submodules, can be determined, and therefore, configured by AutoPyBind11.
The default behavior of AutoPyBind11 is to as closely mirror the C++ side as closely as possible, with the following mappings interpreted by AutoPyBind11:
C++ Language Component |
Python Representation |
Namespace(s) |
Modules and Submodules |
|
|
ex.: namespace1{
namespace2{
}
}
|
Python module structure
|
Classes |
Classes |
C++ classes are direct analogs of Python classes |
|
Free Functions | Free Functions |
|
Free functions are directly related from c++ to python |
|
Classes, their methods, and free functions are qualified by name/module scope much as they are in C++, i.e. a Class within a module would need to be referenced By
import namespace1
namespace1.classname
or
from namespace1 import classname
classname.method() # or classname.attribute
The user however, can directy AutoPyBind11 to break away from this assumtion, and either allow the user to define a unique module/class structure, or to modify an existing structure slightly. In some cases, the user may want to remove any structure what so ever, with only a single top level module, and all other library components as children with a flat import scope (no nested modules).
To inform AutoPyBind11 of this, the descriptor wrapper yaml file must be amended from it’s basic setup.
To specify an altered Python side module structure, the user has the option to dictate a purely unique Python side structure, or to retain the basic structure of the C++ code base, with a few, explicitly declared departures.
To toggle this behavior, the CLI or config argument enforce_namespace_structure will need to be specified as True or False depending on whether a general C++ structure should be retained.
Once that argument is declared (It’s default is True) in order to reassign the scope of a declared component to be bound, in the Customization field, the namespace field must be declared as such
classes:
classname:
customization:
namespace: foo
classname2:
customization:
namespace: cpp_namespace.bar
classname3:
customization:
namespace: foo.baz
functions:
functionname:
customization:
namespace: cpp-namespace
In this declaration style, the namespaces, as above, map to Python modules, and do not neccesarily need to exist. If the namespace specified during customization does exist,
the specified component will be added to that module on the Python side. For example, above, if the functionname function would be added to the existing cpp-namespace module on the Python side.
If the namespace/module does not exist, AutoPyBind11 will create it and incorporate it into the existing namespace/module heirarchy as determined by the scoping of the namespace argument.
To declare a new namespace as a child of an existing namespace, simply, using Python dot sytanx, declare this new name as a child of the desired existing parent namespace. The same is true if a user desires to create
multiple, nested custom namespaces, they need simply be declared with their full scope as in the example above with foo.baz and both namespaces will be created in the specified structure.
New Python based namespaces will be created as children of the top level module if not given an existing c++ parent. If enforce_namespace_structure is False then all cpp namespaces will be ignored, and only
structure declared in the Yaml file will be incorporated into Python.