Basic workflow#
The editables
project is designed to support build backends, allowing them
to declare what they wish to expose as “editable”, and returning a list of
support files that need to be included in the wheel generated by the
build_editable
backend hook.
Note that the editables
library does not build wheel files directly - it
returns the content that needs to be added to the wheel, but it is the build
backend’s responsibility to actually create the wheel from that data.
Create a project#
The first step is for the backend to create an “editable project”. The project name must follow the normal rules for Python project names from PEP 426.
project = EditableProject("myproject")
Specify what to expose#
Once the project has been created, the backend can specify which files should be exposed when the editable install is done. There are two mechanisms currently implemented for this.
Adding a directory to sys.path
#
To add a particular directory (typically the project’s “src” directory) to
sys.path
at runtime, simply call the add_to_path
method
project.add_to_path("src")
This will simply write the given directory into the .pth
file added to the
wheel. See the “Implementation Details” section for more information. Note that
this method requires no runtime support.
Adding a directory as package content#
To expose a directory as a package on sys.path
, call the add_to_subpackage
method, giving the package name to use, and the path to the directory containing
the contents of that package.
For example, if the directory src
contains a package my_pkg
, which you want
to expose to the target interpreter as some.package.my_pkg
, run the following:
project.add_to_subpackage("some.package", "src")
Note that everything in the source directory will be available under the given
package name, and the source directory should not contain an __init__.py
file (if it does, that file will simply be ignored).
Also, the target (some.package
here) must not be an existing package that
is already part of the editable wheel. This is because its __init__.py
file
will be overwritten by the one created by this method.
Mapping individual files/packages#
To expose a single .py
file as a module, call the map
method, giving the
name by which the module can be imported, and the path to the implementation
.py
file. It is possible to give the module a name that is not the same as
the implementation filename, although this is expected to be extremely uncommon.
project.map("module", "src/module.py")
To expose a directory with an __init__.py
file as a package, the map
method is used in precisely the same way, but with the directory name:
project.map("mypackage", "src/mypackage")
The directory must be a Python package - i.e., it must contain an __init__.py
file, and the target package name must be a top-level name, not a dotted name.
Using the map
method does require a runtime support module.
Build the wheel#
Files to add#
Once all of the content to expose is specified, the backend can start building
the wheel. To determine what files to write to the wheel, the files
method
should be used. This returns a sequence of pairs, each of which specifies a
filename, and the content to write to that file. Both the name and the content
are strings, and so should be encoded appropriately (i.e., in UTF-8) when
writing to the wheel.
for name, content in my_project.files():
wheel.add_file(name, content)
Note that the files to be added must be included unchanged - it is not supported for the caller to modify the returned content. Also, it is the caller’s responsibility to ensure that none of the generated files clash with files that the caller is adding to the wheel as part of its own processes.
Runtime dependencies#
If the map
method is used, the resulting wheel will require that the runtime
support module is installed. To ensure that is the case, dependency metadata
must be added to the wheel. The dependencies
method provides the required
metadata.
for dep in my_project.dependencies():
wheel.metadata.dependencies.add(dep)
Note that if the backend only uses the add_to_path
method, no runtime support
is needed, so the dependencies
method will return an empty list. For safety,
and to protect against future changes, it should still be called, though.