Tutorial 5) Dynamic, Flattened Module Access with Ekans
0. Introduction
Tuning quantum chips is inherently iterative: you run measurements, analyse the results, tweak parameters, and repeat. In practice, this also means frequently modifying your measurement scripts, configurations, and helper functions.
However, Python’s import system is static. Once a module is imported, the running interpreter does not automatically pick up changes you make on disk. This forces you to either restart your session or manually reload modules—both of which interrupt workflow and can become error-prone in complex projects.
This is exactly the problem that Ekans solves.
1. What is Ekans?
Ekans is a lightweight proxy layer that sits on top of your package and provides:
A live view of your codebase
It mirrors your folder and module structure, so your package is accessible exactly as it exists on disk.
Explicit and predictable API access
Only objects explicitly exposed via
__init__.py(through__all__) are attached to the namespace. There is no implicit or hidden importing.
Full module access
All leaf modules can be accessed individually in case objects that that are not exposed to the publkic API (
__all__) are required.
One-line dynamic reloading
Changes to any part of your codebase can be picked up instantly with a single reload call with no kernel restarts required.
With Ekans, you can:
🔁 Iterate faster — modify code and immediately test it without restarting your environment
🧠 Reduce mental overhead — no need to track what needs re-importing
🧩 Work with large codebases comfortably — even deeply nested module structures stay manageable
⚡ Stay focused on experiments — less time fighting Python imports, more time analysing results
This notebook demonstrates how to use Ekans to dynamically load, reload, and interact with a structured Python package in a seamless way.
2. Hands on example
We will use the arbok_driver.examples package. This might look intimidating now but will be a handy reference once you start playing around yourself.
Let’s start how one would normally set up a driver and a generic measurement.
from arbok_driver import ArbokDriver, Device, Ekans, Measurement
2026-03-28 11:05:15,086 - qm - INFO - Starting session: 69b72c99-5465-4145-b0c6-44db25a0b2d8
from arbok_driver.examples.sequences import SquarePulseScalable
from arbok_driver.examples.configurations.hardware import (
opx1000_config,
divider_config,
)
from arbok_driver.examples.configurations.sequence import (
square_pulse_scalable_conf
)
mock_device = Device(
name = 'mock_device',
opx_config = opx1000_config,
divider_config = divider_config)
qm_driver = ArbokDriver('qm_driver', mock_device)
mock_measurement = Measurement(qm_driver, 'mock_measurement')
square_pulse_scaled = SquarePulseScalable(
mock_measurement,
'square_pulse_scaled',
square_pulse_scalable_conf
)
Here we used direct imports from our modules but we can make that dynamic using Ekans. Insead of importing the files directly as shown above, you can create an Ekans instance for a given (sub)module.
from arbok_driver import examples
ek_sequences = Ekans(examples.sequences)
ek_configs = Ekans(examples.configurations)
Ekans mirrors your folder structure and exposes modules directly.
Objects are only available if they are explicitly exported in a package’s __init__.py using __all__. This keeps the namespace clean and predictable.
Leaf modules themselves are always accessible as modules.
Creating an Ekans instance will:
Discover all submodules
Import or reload them
Build a namespace mirroring your folder structure
Attach only explicitly exported objects (via
__all__)
ek_configs.hardware.divider_config
{'P1': {'division': 6},
'P2': {'division': 6},
'P3': {'division': 6},
'gate_2': {'division': 2},
'readout_element': {'division': 2}}
ek_sequences.CoulombPeaks
arbok_driver.examples.sequences.coulomb_peaks.CoulombPeaks
Explicit API via __init__.py
Ekans follows standard Python practices:
Each package defines its public API via
__all__Only those names are exposed at the package level
All other objects remain accessible through their module
Example:
# sub_sequences/__init__.py
from .square_pulse import SquarePulse
__all__ = ["SquarePulse"]
This allows:
ek_sequences.SquarePulse # ✅ exported (in __all__)
ek_sequences.square_pulse # ✅ module
ek_sequences.square_pulse.SquarePulse # ✅ always works
arbok_driver.examples.sequences.square_pulse.SquarePulse
But prevents accidental exposure of internal objects.
Have a look at the given folder structure here and play around!
arbok_driver
├── ...
├── examples
│ ├── configurations
│ │ ├── __init__.py
│ │ ├── hardware
│ │ │ ├── divider_config.py
│ │ │ ├── __init__.py
│ │ │ ├── opx1000_config.py
│ │ └── sequence
│ │ ├── __init__.py
│ │ ├── coulomb_peaks_config.py
│ │ ├── device_config.py
│ │ ├── parity_init_conf.py
│ │ ├── parity_readout_conf.py
│ │ ├── square_pulse_confs.py
│ │ └── stability_8q_conf.py
│ ├── experiments
│ │ ├── __init__.py
│ │ └── square_pulse_experiments.py
│ ├── readout_classes
│ │ ├── __init__.py
│ │ ├── dc_average.py
│ │ ├── dc_chopped_readout.py
│ │ ├── difference.py
│ │ └── threshold.py
│ └── sub_sequences
│ ├── __init__.py
│ ├── coulomb_peaks.py
│ ├── parity_initialization.py
│ ├── parity_readout.py
│ ├── square_pulse.py
│ ├── square_pulse_scalable.py
│ └── stability_map.py
├── ...
Each submodule contains Python files defining classes/functions.
3. Live module re-loading
Assume one has made changes to files on disc. Use reload_modules to update the changes from disk and make them available to you.
ek_configs.reload_modules()
ek_sequences.reload_modules()
This will:
reload all modules
rebuild the namespace
reflect code changes without restarting Python
Now the previous measurement can be re-constructed and run.
qm_driver.reset_measurements()
mock_measurement = Measurement(qm_driver, 'mock_measurement')
refreshed_sequence = ek_sequences.SquarePulseScalable(
mock_measurement,
'square_pulse_scaled',
ek_configs.sequence.square_pulse_scalable_conf
)
Deleting measurement: mock_measurement
4. Rules and limitations
To ensure Ekans works reliably, your package should follow these conventions:
1. Every folder must be a Python package
Each directory must contain an __init__.py file.
This ensures Python treats the directory as a proper package
Without it, Python may create a namespace package, which can break module discovery and reloading in
Ekans
2. Only .py files are treated as modules
Ekans only considers Python source files:
✅
.pyfiles are loaded and exposed as modules🚫 Other file types (e.g.
.txt,.json,.yaml) are ignored
These non-Python files do not interfere with Ekans, but they will not be accessible through it.
3. Modules must be importable
All Python modules should be valid at import time:
They must not raise exceptions during import (unless explicitly intended)
All internal and external imports must resolve correctly
If a module fails to import, it may prevent parts of your package from being available through Ekans.
Following these rules ensures that Ekans can correctly discover, structure, and dynamically reload your codebase.
4. Public API must be defined via __all__
Ekans only exposes objects that are explicitly listed in a module’s __all__.
✅ Ensures a clean and predictable namespace
✅ Prevents accidental exposure of internal objects
If __all__ is not defined, no objects will be attached at the package level.
5. Summary
Traditional Python imports are static, meaning that changes to your codebase require manual reloading or restarting your session. This becomes cumbersome when working with complex, modular projects that evolve rapidly during development and experimentation.
Ekans addresses this by providing a dynamic interface to your package:
It mirrors your folder structure as a nested, intuitive namespace
It exposes a clean and explicit API defined via
__init__.pyand__all__It enables one-line reloading, so changes on disk are immediately reflected in your running session
By following a few simple rules—ensuring all directories are proper packages, keeping modules importable, and explicitly defining your public API—you can fully leverage Ekans to create a more predictable and efficient development workflow.
Key takeaway
Ekanslets you treat your codebase as a live, structured, and explicitly defined object, rather than a static snapshot.
This reduces surprises, improves maintainability, and helps you stay focused on designing experiments and analysing results.