Skip to content

Add a package option to the Python generator - #143

Merged
glopesdev merged 3 commits into
harp-tech:mainfrom
glopesdev:emit-python-package-init
Sep 18, 2026
Merged

glopesdev merged 3 commits into
harp-tech:mainfrom
glopesdev:emit-python-package-init

Conversation

@glopesdev

@glopesdev glopesdev commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

The Python target gains a package option to generate the interface as __init__.py together with a py.typed marker, so the output directory alone determines the import path. Generating into src/mypackage/mydevice produces the mypackage.mydevice package directly, and a device repository no longer needs a re-export module. The default is unchanged, so callers that do not set the option get the same single device.py module.

The generated relative import of the companion converters module resolves unchanged from a package initializer, since __package__ is the parent package in both forms, and no generated module reads __name__ or __module__, so nothing in the output depends on being a leaf module.

Public surface

PythonImplementation gains PackageFileName and TypedMarkerFileName beside the existing DeviceFileName, and an IsPackage member with a default on the primary constructor. PythonGenerator takes the flag with a default. Both defaults keep existing call sites compiling, so the change is additive.

An option rather than a caller-supplied file name, because the generated module already emits from .converters import ..., which means a configurable name could produce a module that imports from itself, and any file the generator emits later might do the same.

Keeping a hand-written package root

A package that wants to own its root can still have one, by pointing -o at a private subpackage and re-exporting from a root it writes itself. The generated relative import resolves against that subpackage, so a hand-written converters module moves down with it, and a hand-written module at the root can import the register classes freely.

Notes for review

One constraint comes with package mode. A hand-written module beside the generated one must not import back into its own package root, because while __init__.py is executing the package is only partly initialized. converters.py never does, importing only from harp.protocol, but the failure it would produce is an ImportError whose message suggests renaming __init__.py over a collision with a library name, which is not the cause.

The interop fixture writes by enumerating the implementation rather than naming files, so the selection and the py.typed emission are both covered by tests, with package mode being a test for the layout a published device package uses.

The package README gains a paragraph on the import path, with a neutral mypackage root example.

Closes #141

The Python target now writes its output to __init__.py rather than
device.py, so the output directory alone determines the import path.
Generating into src/mypackage/mydevice produces the mypackage.mydevice
package rather than a module below it, and the generated relative import
of the companion converters module resolves unchanged from the package
initializer.

Closes harp-tech#141
@glopesdev glopesdev added the feature New planned feature label Sep 17, 2026
The Python target now has a package option generating the interface as
__init__.py together with a py.typed marker.
@glopesdev glopesdev changed the title Emit the Python interface as a package initializer Add a package option to the Python generator Sep 18, 2026
@glopesdev
glopesdev merged commit ee53a42 into harp-tech:main Sep 18, 2026
9 checks passed
@glopesdev
glopesdev deleted the emit-python-package-init branch September 18, 2026 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New planned feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Emit the Python device interface as __init__.py

2 participants