halo_width migration guide (v0.2 → v0.3.0)#
Changed in version v0.2: Per-axis halo widths via
SimulationNode.halo_width(),
returning a dict[int, int] ({axis: width}). Pointwise nodes
return {}; stencil nodes return a non-empty dict. The legacy
boolean requires_halo property was retained as a derived
fallback (bool(self.halo_width())) and any subclass overriding
requires_halo directly emitted a
FutureWarning at class-definition time.
Changed in version v0.3.0: The requires_halo property and the __init_subclass__ compat
shim were removed. Subclasses that override requires_halo
instead of halo_width now raise
MigrationError at class-definition time.
Why this changed#
Pencil decomposition (the multi-GPU sharding path —
ShardedStencilNode
in v0.2.1, plus
ShardedUnstructuredNode
added in v0.3.0) needs to know each axis’s halo width, not just
“do you need a halo at all”. The boolean was load-bearing
once — when stencils were 1-D and one axis per node — but it
silently lost information the moment we shipped 3-D LBM.
The v0.2 release kept the boolean as a derived fallback for
back-compat and emitted a FutureWarning. v0.3.0 closes
the deprecation cycle.
What to do#
If you override halo_width already#
Nothing. You already write to the new API; the v0.3.0 hard-removal doesn’t affect you.
If you override requires_halo only#
Replace the property override with a halo_width() method:
class MyStencilNode(SimulationNode):
@property
def requires_halo(self) -> bool:
return True
def update_padded(self, state_padded, boundary_inputs, dt, **kwargs):
...
class MyStencilNode(SimulationNode):
def halo_width(self) -> dict[int, int]:
# axis → cells per side. Two-cell halo on axis 0 (a 5-point
# stencil), one-cell halo on axis 1 (a 3-point stencil).
return {0: 2, 1: 1}
def update_padded(self, state_padded, boundary_inputs, dt, **kwargs):
...
Pointwise nodes#
Pointwise nodes (no spatial neighbour access) return the empty dict:
class MyPointwiseNode(SimulationNode):
def halo_width(self) -> dict[int, int]:
return {}
If your node never overrode halo_width or requires_halo, the
base class already returns {} and you don’t need to do anything.
Auto-migration tooling#
The MigrationError raised at class-
definition time carries structured detail rather than just a string
message — err.api_name, err.affected_class, err.replacement,
err.migration_guide. Tools can introspect these without grepping
the message:
import importlib, traceback
from maddening.warnings import MigrationError
try:
importlib.import_module("my_pkg.legacy_node")
except MigrationError as err:
print(f"Migrate {err.affected_class.__qualname__}: "
f"replace requires_halo with {err.replacement}")
Detecting affected subclasses#
A grep over downstream packages catches most cases. For each match,
either replace with halo_width or delete the override entirely if
the parent class already returns the right value:
$ grep -rn "def requires_halo\|requires_halo =\|@property" --include="*.py" | grep -A1 "requires_halo"
MIME’s CI runs a grep gate on this — see the v0.3.0 plan’s §B6.