Namespace Packages¶
canVODpy uses Python 3.3+ implicit namespace packages to let ten independent packages share the canvod.* import prefix — a unified API backed by separately installable wheels.
from canvod.readers import Rnxv3Obs
from canvod.auxiliary import Sp3File
from canvod.grids import EqualAreaBuilder
from canvod.store import MyIcechunkStore
How It Works¶
A namespace package is created by omitting __init__.py from the shared parent directory. Python treats a directory without __init__.py as a namespace that multiple packages can contribute to.
canvod-readers/src/
canvod/ ← namespace directory — NO __init__.py
readers/
__init__.py ← regular package starts here
base.py
canvod-auxiliary/src/
canvod/ ← same namespace, different package
auxiliary/
__init__.py
src/
canvod/
__init__.py ← Makes canvod a REGULAR package
readers/ ← Blocks other packages from contributing
If any installed package adds canvod/__init__.py, it claims
exclusive ownership of canvod — breaking all other sub-packages.
After uv sync installs all packages:
site-packages/
canvod/ ← Python merges all contributors here
readers/ ← from canvod-readers
auxiliary/ ← from canvod-auxiliary
grids/ ← from canvod-grids
vod/ ← from canvod-vod
store/ ← from canvod-store
store_metadata/ ← from canvod-store-metadata
viz/ ← from canvod-viz
utils/ ← from canvod-utils
ops/ ← from canvod-ops
virtualiconvname/ ← from canvod-virtualiconvname
Build Configuration¶
The uv_build backend uses a dotted module-name to produce the correct wheel structure:
# packages/canvod-readers/pyproject.toml
[build-system]
requires = ["uv_build>=0.9.17,<0.10.0"]
build-backend = "uv_build"
[tool.uv.build-backend]
module-name = "canvod.readers" # dot → namespace package
The dot in "canvod.readers" tells uv_build:
- Treat
canvod/as a namespace (do not include__init__.pyfor it). - Package only the
readers/subdirectory and its contents.
Wheel Contents¶
canvod_readers-0.1.0-py3-none-any.whl
canvod/
readers/ ← package starts here (has __init__.py)
__init__.py
base.py
canvod_readers-0.1.0.dist-info/
METADATA
WHEEL
RECORD
Note
The wheel contains canvod/readers/ but no canvod/__init__.py.
This is correct — the absence of __init__.py is what makes it a namespace.
Import Resolution¶
When Python processes from canvod.readers import Rnxv3Obs:
canvod— no__init__.pyfound → namespace package (open for extension).readers— has__init__.py→ regular package (standard import).Rnxv3Obs— imported fromcanvod/readers/__init__.py.
Independent Installation¶
Because each sub-package is self-contained, users install only what they need:
pip install canvod-readers # readers only
pip install canvod-readers canvod-store # subset
pip install canvodpy # everything
Verification¶
import canvod
print(canvod.__file__) # AttributeError — namespace packages have no __file__
from canvod import readers
print(readers.__file__) # e.g. …/site-packages/canvod/readers/__init__.py
Common Pitfalls¶
Adding __init__.py to the namespace directory
src/canvod/__init__.py converts the namespace into a regular package,
preventing all other packages from contributing to canvod.*.
Incorrect module-name
# WRONG — creates a regular top-level package
module-name = "canvod_readers"
# CORRECT — creates a namespace sub-package
module-name = "canvod.readers"