This repository implements a pure-Python library for representing quantities with units, preserving dimensional correctness during arithmetic, and rendering results using SI base or preferred derived units.
The preferred public API is:
from units import Quantity
from units.si import metre, second, newtonThe package also preserves a temporary legacy compatibility path through Unit = Quantity and top-level unit re-exports.
The repository has already completed a substantial transition from the original flat package:
- runtime package code lives in
src/units - layered implementation now lives in
src/api,src/core,src/models, andsrc/utils - tests live in
tests/unit - integration tests live in
tests/integration - packaging is managed through
pyproject.toml - CI and publish workflows exist under
.github/workflows - the public API is typed
- scalar-by-unit construction is supported as the preferred quantity-construction style
- the dimensional model uses immutable
Dimensionobjects - SI canonicalization is backed by a static deterministic registry
- custom unit systems are supported through
DimensionSystemandCustomUnitBase - the README is now the primary user-facing documentation
The implementation is coherent in purpose and significantly improved over the original package. The main remaining gaps are now higher-level unit capabilities rather than core structure.
- deterministic arithmetic and explicit failure behavior
- minimal runtime dependencies
- typed public interfaces
- dedicated error module
- Python 3-only packaging policy
srcplustestsrepository layout- layered implementation structure under
src/api,src/core,src/models, andsrc/utils pytest-driven unit and integration test suites- CI-based build and test verification
- no known structural gaps are currently tracked here
src/services and src/adapters are intentionally present but empty.
That is acceptable for this library today because:
- there is no orchestration layer distinct from the core unit algebra
- there are no external integrations, I/O boundaries, or replaceable infrastructure adapters
Those packages should remain available for future growth, but they are not an active gap.
The target architecture should preserve the current public API while reorganizing the implementation to match AGENTS.md.
from units import Quantity
from units.si import metre, second, newtonCompatibility to preserve during the transition:
Unit = Quantityas a deprecated compatibility alias- top-level re-exports such as
metre,second, andnewton - legacy conversion helpers until a deliberate breaking release
The package should support two coherent quantity-construction paths:
length = Quantity(3, metre)
length = 3 * metreThe explicit constructor should remain available as the low-level, fully explicit form.
The preferred ergonomic form should become scalar-by-unit multiplication, because it is more natural for engineering and scientific calculations and reads more clearly in longer expressions.
Design rule:
scalar * unit -> Quantityunit * scalar -> Quantityunit * unit -> unit definitionquantity * unit -> Quantityquantity / unit -> Quantity
Planning implication:
- unit definitions should behave as multiplicative basis elements with conceptual magnitude
1 - the README should eventually prefer examples such as
3 * metreoverQuantity(3, metre) - the naming distinction between
Quantityand unit-definition classes remains important
DimensionSystem: defines an ordered base-dimension familyDimension: immutable exponent tuple within aDimensionSystemUnitdefinitions:SIUnit,DerivedUnit,CustomUnitBaseQuantity: numeric value paired with a unit definition- canonical SI resolution for unambiguous derived dimensions
- dimensional correctness belongs in the core model
- domain constraints such as non-negative length belong in higher-level types or validators, not in the base
Quantitytype - SI canonicalization should remain strong and deterministic
- custom unit systems should stay easy to define but separate from SI canonicalization
Completed work:
- replaced
assert-based validation with explicit typed errors - corrected fragile arithmetic paths
- normalized dimensionless handling
- expanded automated tests for success and failure cases
Completed work:
- introduced
Quantityas the preferred value type - added
units.si - kept
Unitas a compatibility path - split implementation across focused modules
- added scalar-by-unit construction so expressions such as
3 * metrecreateQuantityobjects - added exponent support for unit and quantity expressions such as
5 * metre ** 3
Completed work:
- added GitHub Actions CI and publish workflows
- moved packaging metadata into
pyproject.toml - dropped Python 2 from supported packaging and CI policy
Completed work:
- added typing to the public API
- made
README.mdthe main documentation - documented migration from the legacy API
- added real-world usage examples
Completed work:
- introduced immutable
DimensionandDimensionSystem - added registry-backed SI canonicalization
- restored support for custom unit systems without weakening the SI path
Completed work:
- migrated runtime code to
src/units - migrated tests to
tests/unit - updated packaging and CI to the new layout
This is the current baseline. Remaining phases now focus on closing the gap between that baseline and the stricter AGENTS.md structure.
Completed work:
- introduced
src/corefor quantity logic, unit algebra, and error types - introduced
src/modelsfor immutable dimension types - introduced
src/apifor curated exports and SI definitions - introduced
src/utilsfor reusable numeric helpers - preserved
src/unitsas a thin public compatibility facade - added placeholder
src/servicesandsrc/adapterspackages to satisfy the repository structure
Completed work:
- rewrote the unit tests in
pyteststyle - introduced
tests/integration - added integration coverage for public imports and compatibility behavior
- updated CI and tox to run both test layers
Goal:
Honor the migration plan deliberately instead of keeping compatibility aliases indefinitely.
Completed work:
- added low-noise
DeprecationWarningbehavior for legacy helper functions - added tests for warning behavior
- published removal criteria in the README and release notes
- set
1.0.0as the target breaking release for removing deprecated paths - bumped the transition release to
0.3.0
Release policy:
- Keep deprecated compatibility paths available through the remaining pre-1.0 releases.
- Emit warnings only when deprecated helper functions are called.
- Keep
Unit is Quantitytrue until1.0.0;Unitis documented as deprecated but does not emit a call-time warning because doing so would break type identity before the breaking release. - Remove deprecated paths only in the deliberate
1.0.0breaking release unless the project explicitly changes that policy.
Goal:
Add optional higher-level semantics without weakening the core model.
Candidate work:
- constrained domain types such as
Length,Distance,Duration,Mass,AbsoluteTemperature,Displacement, andTemperatureInterval - explicit conversion APIs between compatible units
- support for metric prefixes and prefixed units
- support for additional named unit systems such as imperial units
- top-level extractor helpers for values, multipliers, and unit definitions
- richer canonicalization policies
- more engineering and scientific examples
This phase is optional and should not start until the compatibility policy is settled.
The phrase "constrained domain types" should be read as semantic quantity types layered on top of Quantity.
These types would not replace dimensional correctness. They would add semantic constraints that dimensions alone cannot express.
Examples:
Length- uses a length-compatible unit
- rejects negative values where the domain requires non-negativity
Displacement- uses a length-compatible unit
- allows signed values
Duration- uses a time-compatible unit
- typically rejects negative values
AbsoluteTemperature- distinguishes absolute scales such as kelvin from relative intervals
TemperatureInterval- represents a difference in temperature rather than an absolute reading
The design principle is:
- the core
Quantitytype remains general and dimensionally correct - constrained types add semantic rules and safer domain-specific APIs
Unit conversion should be treated as a first-class feature, not as part of constrained domain types.
Completed in 0.4.0:
- same-dimension scale-only conversions within SI scales
metre <-> kilometresecond <-> minutegram <-> kilogram
- prefixed-unit support
milli,kilo,mega,micro,nano,pico
- explicit conversion entry points
- method-based APIs such as
quantity.to(kilometre) - helper-based APIs such as
convert(quantity, kilometre)
- method-based APIs such as
Remaining conversion roadmap:
- affine conversions where offsets matter
kelvin <-> degree_celcius
- additional named unit systems
- imperial units such as
inch,foot,mile,pound,fahrenheit
- imperial units such as
This is now modeled explicitly with scale metadata on unit definitions. Future affine conversions should add offset metadata rather than relying on dimensional equivalence alone.
The package exposes small top-level helpers that let users deliberately strip structure from a quantity.
Completed helpers:
value(quantity)-> numeric magnitudeunit(quantity)-> unit definitionmultiplier(quantity)-> scalar factor relative to a canonical base unit or chosen display unit
These helpers should be explicit and unsurprising. They should not silently convert units or discard offsets without documented rules.
The remaining work should be distributed across releases rather than packed into 1.0.0.
Recommended scope:
- implement Phase 9 warnings for legacy helper functions
- document the migration path and
1.0.0removal target - keep preferred APIs warning-free
- preserve
Unit = Quantityuntil the breaking release
Completed scope:
- introduce conversion metadata on unit definitions
- add explicit same-dimension conversions for straightforward multiplicative units
- add prefixed SI units and prefixes
- add top-level extractor helpers such as
value()andunit() - document the conversion model clearly
Non-goals for 0.4.0:
- full constrained semantic type hierarchy
- large imperial coverage
- aggressive deprecation of legacy API
Recommended scope:
- support affine conversions such as
kelvin <-> degree_celcius - add the first stable non-SI named conversions, especially commonly used imperial units
- refine conversion APIs based on
0.4.0feedback - add tests and documentation for mixed conversion scenarios
Recommended scope:
- introduce the first constrained domain types
- define the boundary between general
Quantityand semantic wrappers such asLengthandDisplacement - stabilize extractor semantics where offsets and conversion scales are involved
- expand real-world documentation around safe use of semantic quantity types
Recommended scope:
- modern
Quantityplusunits.siAPI is stable - conversion APIs are stable
- prefixed units and supported non-SI systems are stable
- constrained semantic types, if included, are stable
- extractor helpers are stable
- deprecated legacy compatibility paths are removed unless explicitly retained as part of the supported surface
- release notes clearly state what is guaranteed going forward
The next implementation work should happen in this order:
- Phase 10B: affine and cross-system conversions
- Phase 10C: constrained semantic types
1.0.0: remove or explicitly retain deprecated compatibility paths
The plan is complete only when all of the following are true:
- the repository structure materially matches
AGENTS.md - public interfaces remain typed and documented
- tests are primarily
pytest-based and include integration coverage - deprecation behavior is explicit and documented
- demo behavior has been removed from runtime modules
- the README reflects the actual supported API and migration path
The repository should continue to optimize for:
- correctness over cleverness
- explicit contracts over convenience
- a small, dependable public API
- strong SI behavior without sacrificing ease of custom unit-system definition