You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The suffix is not descriptive. It exists because validate.py and visualize.py are modules, so importing from them in __init__.py binds the
module as a package attribute and takes the short name. om.validate today is
a module, and om.validate(pool) raises TypeError: 'module' object is not callable.
_allocate.py already solves this: the module is private, so allocate is
free to be the function.
Proposal
Module
New name
omnimalloc/validate.py
omnimalloc/_validate.py
omnimalloc/visualize.py
omnimalloc/_visualize.py
omnimalloc/io.py
unchanged
Now
Proposed
om.validate_allocation(entity)
om.validate(entity)
om.plot_allocation(entity, path)
om.plot(entity, path)
om.io.save_allocation(entity, path)
om.io.save(entity, path)
om.io.load_allocation(path)
om.io.load(path)
io.py stays public
The first draft of this issue proposed _io.py re-exported as io. That is
wrong on both halves.
It is unnecessary: the suffix problem comes from __init__.py importing the
module, and __init__.py never imported .io — #49 removed save_allocation
and load_allocation from the top level. Nothing shadows the short names
there.
It is also breaking. from omnimalloc.io import load resolves a real
submodule, not a package attribute, so from . import _io as io would raise ModuleNotFoundError for every caller using that spelling, including benchmark/sources/minimalloc.py.
Instead __init__.py imports the module, so om.io resolves after a plain import omnimalloc, and all three spellings work:
om.io.save(pool, path) # via the package attributefromomnimalloc.ioimportsave, load# via the submoduleimportomnimalloc.io# directly
Resulting top level, nine names (#49 already established the other eight;
this issue renames two of them and adds io):
Three call sites take a validate: bool parameter and call the validator in
the body, so a plain find-and-replace makes the parameter shadow the function
and the call site invokes a bool:
_allocate.allocate
benchmark._benchmark_result
scripts/generate_readme_assets._solve
All three alias the import (from ._validate import validate as _validate). ty catches the miss as call-non-callable, which is how the third one
surfaced — it is not reachable from the test suite.
Two more spots a mechanical sweep misses:
LANE_CAVEAT and PANEL_CAVEAT in _visualize.py embed validate_allocation() in a string that is rendered as the figure suptitle. They now read omnimalloc.validate(), qualified, because a bare validate() in a figure caption says nothing.
CLAUDE.md names all four functions and is untracked, so it appears in no
diff and no git grep.
Scope
Occurrence counts on main (88f8303):
Name
Files
Occurrences
validate_allocation
33
171
plot_allocation
12
45
save_allocation
2
21
load_allocation
3
14
One module import needs redirecting: tests/unit/test_visualize.py does from omnimalloc import visualize and monkeypatches through it; it now
monkeypatches by dotted path instead.
Follow-on cleanups
The rename establishes one rule — the module is an implementation detail, the
package __init__ is the API — and the same rule applies in four more places.
Each is a separate commit.
__all__ in every package.Shrink the top-level namespace to eight names #49 gave the top level an __all__; the
eight subpackage __init__ files still use from .x import Y as Y. Two
spellings for one job. Also surfaced two gaps where omnimalloc.benchmark
did not mirror omnimalloc.benchmark.sources: SkewedSource, TwoPlusTwoSource, SIZE_DISTRIBUTIONS, sample_sizes.
analysis/ speaks one dialect._conflicts.py and _pressure.py were
private, clock.py and linearize.py were not. clock.py was the only
module the package did not re-export, so seven modules across four packages
reached past __init__ for time_components and uniform_dim. Both are
now exported; the modules become _clock.py and _linearize.py.
This surfaced a latent import cycle. primitives/pool.py bound analysis._pressure at module scope for Pool.pressure — the one upward
reference in the layering — while analysis imports primitives. Nothing
broke only because _conflicts happened to sort first in the __init__ and
pulled primitives in before _clock ran. Putting _clock first closed analysis -> _clock -> primitives -> pool -> _pressure -> _clock on a
half-initialized module. The upward reference is now deferred into Pool.pressure, so no import order can close it.
_benchmark is the same suffix.run_benchmark/plot_benchmark/ save_benchmark become om.benchmark.run/.plot/.save, and benchmark/benchmark.py and results/visualize.py become _benchmark.py
and _visualize.py. Timer joins the exports — four scripts imported it
from omnimalloc.benchmark.timer, the same reach-past-__init__ that clock.py invited.
benchmark no longer imports the root package.benchmark/benchmark.py
did from omnimalloc import allocate, validate_allocation, the one import
that made a subpackage depend on the package __init__. Folded into the
rename commit since the names change there anyway.
Considered and dropped
Normalizing absolute vs relative imports. The 135/168 split is not drift:
ruff runs select = ["ALL"], so TID252 is active with ban-relative-imports = "parents". The rule is already enforced — . for
same-package siblings, absolute where .. would be needed.
common -> _common.common/__init__.py is empty, so nothing leaks and
the change is cosmetic. It would touch ~30 files and conflict with all five
open PRs. Worth doing when the queue drains, not now.
Notes
No deprecation aliases. The project is pre-1.0 and there are no external
consumers to carry.
Four public names carry an
_allocationsuffix that repeats what the packagename and the argument already say:
The suffix is not descriptive. It exists because
validate.pyandvisualize.pyare modules, so importing from them in__init__.pybinds themodule as a package attribute and takes the short name.
om.validatetoday isa module, and
om.validate(pool)raisesTypeError: 'module' object is not callable._allocate.pyalready solves this: the module is private, soallocateisfree to be the function.
Proposal
omnimalloc/validate.pyomnimalloc/_validate.pyomnimalloc/visualize.pyomnimalloc/_visualize.pyomnimalloc/io.pyom.validate_allocation(entity)om.validate(entity)om.plot_allocation(entity, path)om.plot(entity, path)om.io.save_allocation(entity, path)om.io.save(entity, path)om.io.load_allocation(path)om.io.load(path)io.py stays public
The first draft of this issue proposed
_io.pyre-exported asio. That iswrong on both halves.
It is unnecessary: the suffix problem comes from
__init__.pyimporting themodule, and
__init__.pynever imported.io— #49 removedsave_allocationand
load_allocationfrom the top level. Nothing shadows the short namesthere.
It is also breaking.
from omnimalloc.io import loadresolves a realsubmodule, not a package attribute, so
from . import _io as iowould raiseModuleNotFoundErrorfor every caller using that spelling, includingbenchmark/sources/minimalloc.py.Instead
__init__.pyimports the module, soom.ioresolves after a plainimport omnimalloc, and all three spellings work:Resulting top level, nine names (#49 already established the other eight;
this issue renames two of them and adds
io):The rename is not purely mechanical
Three call sites take a
validate: boolparameter and call the validator inthe body, so a plain find-and-replace makes the parameter shadow the function
and the call site invokes a
bool:_allocate.allocatebenchmark._benchmark_resultscripts/generate_readme_assets._solveAll three alias the import (
from ._validate import validate as _validate).tycatches the miss ascall-non-callable, which is how the third onesurfaced — it is not reachable from the test suite.
Two more spots a mechanical sweep misses:
LANE_CAVEATandPANEL_CAVEATin_visualize.pyembedvalidate_allocation()in a string that is rendered as the figuresuptitle. They now readomnimalloc.validate(), qualified, because a barevalidate()in a figure caption says nothing.CLAUDE.mdnames all four functions and is untracked, so it appears in nodiff and no
git grep.Scope
Occurrence counts on
main(88f8303):validate_allocationplot_allocationsave_allocationload_allocationOne module import needs redirecting:
tests/unit/test_visualize.pydoesfrom omnimalloc import visualizeand monkeypatches through it; it nowmonkeypatches by dotted path instead.
Follow-on cleanups
The rename establishes one rule — the module is an implementation detail, the
package
__init__is the API — and the same rule applies in four more places.Each is a separate commit.
__all__in every package. Shrink the top-level namespace to eight names #49 gave the top level an__all__; theeight subpackage
__init__files still usefrom .x import Y as Y. Twospellings for one job. Also surfaced two gaps where
omnimalloc.benchmarkdid not mirror
omnimalloc.benchmark.sources:SkewedSource,TwoPlusTwoSource,SIZE_DISTRIBUTIONS,sample_sizes.analysis/speaks one dialect._conflicts.pyand_pressure.pywereprivate,
clock.pyandlinearize.pywere not.clock.pywas the onlymodule the package did not re-export, so seven modules across four packages
reached past
__init__fortime_componentsanduniform_dim. Both arenow exported; the modules become
_clock.pyand_linearize.py.This surfaced a latent import cycle.
primitives/pool.pyboundanalysis._pressureat module scope forPool.pressure— the one upwardreference in the layering — while
analysisimportsprimitives. Nothingbroke only because
_conflictshappened to sort first in the__init__andpulled
primitivesin before_clockran. Putting_clockfirst closedanalysis -> _clock -> primitives -> pool -> _pressure -> _clockon ahalf-initialized module. The upward reference is now deferred into
Pool.pressure, so no import order can close it._benchmarkis the same suffix.run_benchmark/plot_benchmark/save_benchmarkbecomeom.benchmark.run/.plot/.save, andbenchmark/benchmark.pyandresults/visualize.pybecome_benchmark.pyand
_visualize.py.Timerjoins the exports — four scripts imported itfrom
omnimalloc.benchmark.timer, the same reach-past-__init__thatclock.pyinvited.benchmarkno longer imports the root package.benchmark/benchmark.pydid
from omnimalloc import allocate, validate_allocation, the one importthat made a subpackage depend on the package
__init__. Folded into therename commit since the names change there anyway.
Considered and dropped
ruff runs
select = ["ALL"], so TID252 is active withban-relative-imports = "parents". The rule is already enforced —.forsame-package siblings, absolute where
..would be needed.common->_common.common/__init__.pyis empty, so nothing leaks andthe change is cosmetic. It would touch ~30 files and conflict with all five
open PRs. Worth doing when the queue drains, not now.
Notes
No deprecation aliases. The project is pre-1.0 and there are no external
consumers to carry.