Skip to content

units: build PhysicalQuantity with a factory as a frozen result, and split quantity.py into modules - #4

Merged
m-messer merged 2 commits into
mainfrom
refactor/physical-quantity
Oct 16, 2026
Merged

m-messer merged 2 commits into
mainfrom
refactor/physical-quantity

Conversation

@m-messer

Copy link
Copy Markdown
Member

Stacked on #3: merge that first, and this PR's base will then switch to main.

PhysicalQuantity becomes an immutable result built by a factory, and the code that reads a quantity is split out of quantity.py. Parsing results are unchanged.

Changes

  • A factory and a frozen result.
    • PhysicalQuantity.from_tree(name, params, ast_root, parser) runs each step as a separate function: split value from unit, restore the value's text, LaTeX, then the SymPy forms in SI base units. parse_quantity calls it.
    • PhysicalQuantity is a @dataclass(frozen=True, eq=False) with the same attributes and repr. Quantities still compare by identity.
    • The rotations return the new root instead of setting self.ast_root.
    • The quantity no longer holds its parser, so the custom __getstate__/__setstate__ are gone. Pickle and deepcopy just work, and an existing pickle still loads.
  • Breaking (CHANGELOG):
    • Setting an attribute raises FrozenInstanceError.
    • reverted_units is a tuple.
    • The constructor takes every attribute; use from_tree, or parse_quantity as before.
    • quantity.parser is deprecated (removed in 1.0). It warns and returns the same cached parser as build_quantity_parser(params.unit_sets, params.strictness).
  • Modules. quantity.py keeps the public PhysicalQuantity, RevertedUnit, parse_quantity and same_dimension. The steps move to private modules:
    • _split.py: the rotations, split_value_and_unit, the unit checks and MAX_UNIT_EXPONENT.
    • _latex.py: latex_for_unit and latex_for_value.
    • _conversion.py: quantity_parsing_config and sympy_forms.
    • unsplittable_names(unit_sets, all_forms) moves to data.py, so the new modules don't import each other.
    • Public names are unchanged from lambdafeedback.units. Only lambdafeedback.units.quantity.MAX_UNIT_EXPONENT no longer resolves; nothing here or in compareExpressions uses it.

Downstream
compareExpressions only reads value, unit, unit_latex and latex, and loops over reverted_units, so it isn't affected.

Verification

  • ruff, format and mypy are clean, and all four test suites pass. New tests cover:
    • each reading step directly;
    • that the quantity is frozen;
    • that every field survives a pickle round trip;
    • the deprecated parser.
  • tools/parity/check.py: 0 unexpected changes.
  • A dump of every field (tag trees, repr, LaTeX, SymPy forms, reverted_units) over the parity corpus plus extra juxtaposition inputs, in both strict and natural mode, is byte-identical before and after both commits.
  • scripts/check_dist.py passes.

🤖 Generated with Claude Code

…lone functions, make it immutable and picklable, update tests, README, and changelog.
…y`, and `_split.py` for SI unit conversions, LaTeX rendering, and parse tree splitting. Update `quantity.py` to delegate responsibilities to the new modules.
Base automatically changed from refactor/design-patterns to main October 16, 2026 08:32
@m-messer
m-messer merged commit 62e159d into main Oct 16, 2026
8 checks passed
@m-messer
m-messer deleted the refactor/physical-quantity branch October 16, 2026 08:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant