ucon 0.2.2rc1__tar.gz → 0.3.2rc6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. ucon-0.3.2rc6/.github/workflows/publish.yaml +64 -0
  2. ucon-0.3.2rc6/.github/workflows/tests.yaml +51 -0
  3. ucon-0.3.2rc6/PKG-INFO +219 -0
  4. ucon-0.3.2rc6/README.md +183 -0
  5. ucon-0.3.2rc6/ROADMAP.md +222 -0
  6. ucon-0.3.2rc6/docs/unity-distance-metric-for-nearest-scale.md +72 -0
  7. ucon-0.3.2rc6/noxfile.py +128 -0
  8. ucon-0.3.2rc6/requirements.txt +2 -0
  9. {ucon-0.2.2rc1 → ucon-0.3.2rc6}/setup.py +7 -2
  10. ucon-0.3.2rc6/tests/__init__.py +0 -0
  11. ucon-0.3.2rc6/tests/ucon/__init__.py +0 -0
  12. ucon-0.3.2rc6/tests/ucon/test_core.py +535 -0
  13. ucon-0.3.2rc6/tests/ucon/test_dimension.py +206 -0
  14. ucon-0.3.2rc6/tests/ucon/test_unit.py +143 -0
  15. ucon-0.3.2rc6/tests/ucon/test_units.py +21 -0
  16. ucon-0.3.2rc6/ucon/__init__.py +49 -0
  17. ucon-0.3.2rc6/ucon/core.py +353 -0
  18. ucon-0.3.2rc6/ucon/dimension.py +172 -0
  19. ucon-0.3.2rc6/ucon/unit.py +92 -0
  20. ucon-0.3.2rc6/ucon/units.py +84 -0
  21. ucon-0.3.2rc6/ucon.egg-info/PKG-INFO +219 -0
  22. ucon-0.3.2rc6/ucon.egg-info/SOURCES.txt +25 -0
  23. ucon-0.3.2rc6/ucon.egg-info/top_level.txt +2 -0
  24. ucon-0.2.2rc1/.github/workflows/publish.yaml +0 -59
  25. ucon-0.2.2rc1/.github/workflows/tests.yaml +0 -34
  26. ucon-0.2.2rc1/PKG-INFO +0 -81
  27. ucon-0.2.2rc1/README.md +0 -57
  28. ucon-0.2.2rc1/noxfile.py +0 -54
  29. ucon-0.2.2rc1/requirements.txt +0 -3
  30. ucon-0.2.2rc1/test_ucon.py +0 -193
  31. ucon-0.2.2rc1/ucon.egg-info/PKG-INFO +0 -81
  32. ucon-0.2.2rc1/ucon.egg-info/SOURCES.txt +0 -14
  33. ucon-0.2.2rc1/ucon.egg-info/top_level.txt +0 -1
  34. ucon-0.2.2rc1/ucon.py +0 -194
  35. {ucon-0.2.2rc1 → ucon-0.3.2rc6}/.gitignore +0 -0
  36. {ucon-0.2.2rc1 → ucon-0.3.2rc6}/LICENSE +0 -0
  37. {ucon-0.2.2rc1 → ucon-0.3.2rc6}/setup.cfg +0 -0
  38. {ucon-0.2.2rc1 → ucon-0.3.2rc6}/ucon.egg-info/dependency_links.txt +0 -0
@@ -0,0 +1,64 @@
1
+ name: publish
2
+
3
+ on:
4
+ push:
5
+ branches: [main] # mainline merges → Test PyPI
6
+ tags: ['*'] # tags → Test & Prod PyPI
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout source
14
+ uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0 # needed for ancestry check
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: '3.14'
22
+
23
+ - name: Install dependencies
24
+ run: pip install -r requirements.txt
25
+
26
+ - name: Build distribution 📦
27
+ run: PYTHONWARNINGS=ignore LOCAL_VERSION_SCHEME=true nox -s build
28
+
29
+ - name: Publish to Test PyPI
30
+ if: github.ref == 'refs/heads/main'
31
+ uses: pypa/gh-action-pypi-publish@release/v1
32
+ with:
33
+ user: __token__
34
+ password: ${{ secrets.test_pypi_password }}
35
+ skip_existing: true
36
+ repository_url: https://test.pypi.org/legacy/
37
+
38
+ - name: Verify tag is on mainline
39
+ if: startsWith(github.ref, 'refs/tags/')
40
+ run: |
41
+ git fetch origin main --depth=1
42
+ TAG=${GITHUB_REF#refs/tags/}
43
+ TAG_COMMIT=$(git rev-list -n 1 "$TAG")
44
+ if ! git merge-base --is-ancestor "$TAG_COMMIT" origin/main; then
45
+ echo "::error::Tag $TAG is not based on main; skipping publish."
46
+ exit 1
47
+ fi
48
+
49
+ - name: 🧪 Publish tag build to Test PyPI
50
+ if: startsWith(github.ref, 'refs/tags/')
51
+ uses: pypa/gh-action-pypi-publish@release/v1
52
+ with:
53
+ user: __token__
54
+ password: ${{ secrets.test_pypi_password }}
55
+ skip_existing: true
56
+ repository_url: https://test.pypi.org/legacy/
57
+
58
+ - name: 🚀 Publish to Prod PyPI
59
+ if: startsWith(github.ref, 'refs/tags/')
60
+ uses: pypa/gh-action-pypi-publish@release/v1
61
+ with:
62
+ user: __token__
63
+ password: ${{ secrets.pypi_password }}
64
+ skip_existing: true
@@ -0,0 +1,51 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: ['**']
8
+
9
+ jobs:
10
+ run_tests:
11
+ runs-on: ubuntu-22.04
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version:
16
+ - '3.7'
17
+ - '3.8'
18
+ - '3.9'
19
+ - '3.10'
20
+ - '3.11'
21
+ - '3.12'
22
+ - '3.13'
23
+ - '3.14'
24
+
25
+ steps:
26
+ - name: Checkout code
27
+ uses: actions/checkout@v5
28
+
29
+ - name: Set up Python ${{ matrix.python-version }}
30
+ uses: actions/setup-python@v6
31
+ with:
32
+ python-version: ${{ matrix.python-version }}
33
+
34
+ - name: Install dependencies
35
+ run: |
36
+ python -m pip install --upgrade pip
37
+ pip install -r requirements.txt
38
+
39
+ - name: Run tests
40
+ run: nox -s test
41
+
42
+ - name: Upload coverage to Codecov
43
+ if: success() && github.repository_owner == 'withtwoemms'
44
+ run: bash <(curl -s https://codecov.io/bash) -t ${{ secrets.CODECOV_TOKEN }}
45
+
46
+ - name: Archive coverage report
47
+ if: always()
48
+ uses: actions/upload-artifact@v4
49
+ with:
50
+ name: python-${{ matrix.python-version }}-coverage-${{ github.run_id }}
51
+ path: ${{ github.workspace }}/coverage.xml
ucon-0.3.2rc6/PKG-INFO ADDED
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: ucon
3
+ Version: 0.3.2rc6
4
+ Summary: a tool for dimensional analysis: a "Unit CONverter"
5
+ Home-page: https://github.com/withtwoemms/ucon
6
+ Author: Emmanuel I. Obi
7
+ Maintainer: Emmanuel I. Obi
8
+ Maintainer-email: withtwoemms@gmail.com
9
+ License: MIT
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Topic :: Software Development :: Build Tools
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Dynamic: author
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: license
32
+ Dynamic: license-file
33
+ Dynamic: maintainer
34
+ Dynamic: maintainer-email
35
+ Dynamic: summary
36
+
37
+ <img src="https://gist.githubusercontent.com/withtwoemms/0cb9e6bc8df08f326771a89eeb790f8e/raw/dde6c7d3b8a7d79eb1006ace03fb834e044cdebc/ucon-logo.png" align="left" width="420" />
38
+
39
+ # ucon
40
+
41
+ > Pronounced: _yoo · cahn_
42
+ > A lightweight, **unit-aware computation library** for Python — built on first-principles.
43
+
44
+ [![tests](https://github.com/withtwoemms/ucon/workflows/tests/badge.svg)](https://github.com/withtwoemms/ucon/actions?query=workflow%3Atests)
45
+ [![codecov](https://codecov.io/gh/withtwoemms/ucon/graph/badge.svg?token=BNONQTRJWG)](https://codecov.io/gh/withtwoemms/ucon)
46
+ [![publish](https://github.com/withtwoemms/ucon/workflows/publish/badge.svg)](https://github.com/withtwoemms/ucon/actions?query=workflow%3Apublish)
47
+
48
+ ---
49
+
50
+ ## Overview
51
+
52
+ `ucon` helps Python understand the *physical meaning* of your numbers.
53
+ It combines **units**, **scales**, and **dimensions** into a composable algebra that supports:
54
+
55
+ - Dimensional analysis through `Number` and `Ratio`
56
+ - Scale-aware arithmetic and conversions
57
+ - Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, ect.)
58
+ - A clean foundation for physics, chemistry, data modeling, and beyond
59
+
60
+ Think of it as **`decimal.Decimal` for the physical world** — precise, predictable, and type-safe.
61
+
62
+ ## Introduction
63
+
64
+ The crux of this tiny library is to provide abstractions that simplify the answering of questions like:
65
+
66
+ > _"If given two milliliters of bromine (liquid Br<sub>2</sub>), how many grams of bromine does one have?"_
67
+
68
+ To best answer this question, we turn to an age-old technique ([dimensional analysis](https://en.wikipedia.org/wiki/Dimensional_analysis)) which essentially allows for the solution to be written as a product of ratios. `ucon` comes equipped with some useful primitives:
69
+ | Type | Defined In | Purpose | Typical Use Cases |
70
+ | ----------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
71
+ | **`Vector`** | `ucon.dimension` | Represents the exponent tuple of a physical quantity’s base dimensions (e.g., T, L, M, I, Θ, J, N). | Internal representation of dimensional algebra; building derived quantities (e.g., area, velocity, force). |
72
+ | **`Dimension`** | `ucon.dimension` | Encapsulates physical dimensions (e.g., length, time, mass) as algebraic combinations of vectors. | Enforcing dimensional consistency; defining relationships between quantities (e.g., length / time = velocity). |
73
+ | **`Unit`** | `ucon.unit` | Represents a named, dimensioned measurement unit (e.g., meter, second, joule). | Attaching human-readable units to quantities; defining or composing new units (`newton = kilogram * meter / second²`). |
74
+ | **`Scale`** | `ucon.core` | Encodes powers of base magnitudes (binary or decimal prefixes like kilo-, milli-, mebi-). | Adjusting numeric scale without changing dimension (e.g., kilometer ↔ meter, byte ↔ kibibyte). |
75
+ | **`Exponent`** | `ucon.core` | Represents base-power pairs (e.g., 10³, 2¹⁰) used by `Scale`. | Performing arithmetic on powers and bases; normalizing scales across conversions. |
76
+ | **`Number`** | `ucon.core` | Combines a numeric quantity with a unit and scale; the primary measurable type. | Performing arithmetic with units; converting between compatible units; representing physical quantities like 5 m/s. |
77
+ | **`Ratio`** | `ucon.core` | Represents the division of two `Number` objects; captures relationships between quantities. | Expressing rates, densities, efficiencies (e.g., energy / time = power, length / time = velocity). |
78
+ | **`units` module** | `ucon.units` | Defines canonical unit instances (SI and common derived units). | Quick access to standard physical units (`units.meter`, `units.second`, `units.newton`, etc.). | |
79
+
80
+ ### Under the Hood
81
+
82
+ `ucon` models unit math through a hierarchy where each layer builds on the last:
83
+
84
+ ```mermaid
85
+ ---
86
+ config:
87
+ layout: elk
88
+ elk:
89
+ mergeEdges: true # Combines parallel edges
90
+ nodePlacementStrategy: SIMPLE # Other options: SIMPLE, NETWORK_SIMPLEX, BRANDES_KOEPF (default)
91
+ ---
92
+ flowchart LR
93
+ %% --- Algebraic substrate ---
94
+ subgraph "Algebraic Substrate"
95
+ A[Exponent] --> B[Scale]
96
+ end
97
+ %% --- Physical ontology ---
98
+ subgraph "Physical Ontology"
99
+ D[Dimension] --> E[Unit]
100
+ end
101
+ %% --- Value layer ---
102
+ subgraph "Value Layer"
103
+ F[Number]
104
+ G[Ratio]
105
+ end
106
+ %% --- Cross-layer relationships ---
107
+ E --> F
108
+ B --> F
109
+ %% Ratio composes Numbers and also evaluates to a Number
110
+ F --> G
111
+ G --> F
112
+ ```
113
+
114
+ ## Why `ucon`?
115
+
116
+ Python already has mature libraries for handling units and physical quantities — Pint, SymPy, and Unum — each solving part of the same problem from different angles:
117
+
118
+ | Library | Focus | Limitation |
119
+ | --------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
120
+ | **Pint** | Runtime unit conversion and compatibility checking | Treats quantities as decorated numbers — conversions work, but the algebra behind them isn’t inspectable or type-safe. |
121
+ | **SymPy** | Symbolic algebra and simplification of unit expressions | Excellent for symbolic reasoning, but not designed for runtime validation, conversion, or serialization. |
122
+ | **Unum** | Unit-aware arithmetic and unit propagation | Tracks units through arithmetic but lacks explicit dimensional algebra, conversion taxonomy, or runtime introspection. |
123
+
124
+ Together, these tools can _use_ units, but none can explicitly represent and verify the relationships between units and dimensions.
125
+
126
+ That’s the gap `ucon` fills.
127
+
128
+ It treats units, dimensions, and scales as first-class objects and builds a composable algebra around them.
129
+ This allows you to:
130
+ - Represent dimensional meaning explicitly (`Dimension`, `Vector`);
131
+ - Compose and compute with type-safe, introspectable quantities (`Unit`, `Number`);
132
+ - Perform reversible, declarative conversions (standard, linear, affine, nonlinear);
133
+ - Serialize and validate measurements with Pydantic integration;
134
+ - Extend the system with custom unit registries and conversion families.
135
+
136
+ Where Pint, Unum, and SymPy focus on _how_ to compute with units,
137
+ `ucon` focuses on why those computations make sense. Every operation checks the dimensional structure, _not just the unit labels_. This means ucon doesn’t just track names: it enforces physics:
138
+ ```python
139
+ from ucon import Number, units
140
+
141
+ length = Number(quantity=5, unit=units.meter)
142
+ time = Number(quantity=2, unit=units.second)
143
+
144
+ speed = length / time # ✅ valid: L / T = velocity
145
+ invalid = length + time # ❌ raises: incompatible dimensions
146
+ ```
147
+
148
+ ## Setup
149
+
150
+ Simple:
151
+ ```bash
152
+ pip install ucon
153
+ ```
154
+
155
+ ## Usage
156
+
157
+ This sort of dimensional analysis:
158
+ ```
159
+ 2 mL bromine | 3.119 g bromine
160
+ --------------x----------------- #=> 6.238 g bromine
161
+ 1 | 1 mL bromine
162
+ ```
163
+ becomes straightforward when you define a measurement:
164
+ ```python
165
+ from ucon import Number, Scale, Units, Ratio
166
+
167
+ # Two milliliters of bromine
168
+ two_mL_bromine = Number(unit=Units.liter, scale=Scale.milli, quantity=2)
169
+
170
+ # Density of bromine: 3.119 g/mL
171
+ bromine_density = Ratio(
172
+ numerator=Number(unit=Units.gram, quantity=3.119),
173
+ denominator=Number(unit=Units.liter, scale=Scale.milli),
174
+ )
175
+
176
+ # Multiply to find mass
177
+ grams_bromine = two_mL_bromine * bromine_density
178
+ print(grams_bromine) # <6.238 gram>
179
+ ```
180
+
181
+ Scale conversion is automatic and precise:
182
+
183
+ ```python
184
+ grams_bromine.to(Scale.milli) # <6238.0 milligram>
185
+ grams_bromine.to(Scale.kibi) # <0.006091796875 kibigram>
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Roadmap Highlights
191
+
192
+ | Version | Theme | Focus |
193
+ |----------|-------|--------|
194
+ | [**0.3.x**](https://github.com/withtwoemms/ucon/milestone/1) | Primitive Type Refinement | Unified algebraic foundation |
195
+ | [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System | Linear & affine conversions |
196
+ | [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH |
197
+ | [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation |
198
+
199
+ See full roadmap: [ROADMAP.md](./ROADMAP.md)
200
+
201
+ ---
202
+
203
+ ## Contributing
204
+
205
+ Contributions, issues, and pull requests are welcome!
206
+ Ensure `nox` is installed.
207
+ ```
208
+ pip install -r requirements.txt
209
+ ```
210
+ Then run the full test suite (agains all supported python versions) before committing:
211
+
212
+ ```bash
213
+ nox -s test
214
+ ```
215
+ ---
216
+
217
+ > “If it can be measured, it can be represented.
218
+ If it can be represented, it can be validated.
219
+ If it can be validated, it can be trusted.”
@@ -0,0 +1,183 @@
1
+ <img src="https://gist.githubusercontent.com/withtwoemms/0cb9e6bc8df08f326771a89eeb790f8e/raw/dde6c7d3b8a7d79eb1006ace03fb834e044cdebc/ucon-logo.png" align="left" width="420" />
2
+
3
+ # ucon
4
+
5
+ > Pronounced: _yoo · cahn_
6
+ > A lightweight, **unit-aware computation library** for Python — built on first-principles.
7
+
8
+ [![tests](https://github.com/withtwoemms/ucon/workflows/tests/badge.svg)](https://github.com/withtwoemms/ucon/actions?query=workflow%3Atests)
9
+ [![codecov](https://codecov.io/gh/withtwoemms/ucon/graph/badge.svg?token=BNONQTRJWG)](https://codecov.io/gh/withtwoemms/ucon)
10
+ [![publish](https://github.com/withtwoemms/ucon/workflows/publish/badge.svg)](https://github.com/withtwoemms/ucon/actions?query=workflow%3Apublish)
11
+
12
+ ---
13
+
14
+ ## Overview
15
+
16
+ `ucon` helps Python understand the *physical meaning* of your numbers.
17
+ It combines **units**, **scales**, and **dimensions** into a composable algebra that supports:
18
+
19
+ - Dimensional analysis through `Number` and `Ratio`
20
+ - Scale-aware arithmetic and conversions
21
+ - Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, ect.)
22
+ - A clean foundation for physics, chemistry, data modeling, and beyond
23
+
24
+ Think of it as **`decimal.Decimal` for the physical world** — precise, predictable, and type-safe.
25
+
26
+ ## Introduction
27
+
28
+ The crux of this tiny library is to provide abstractions that simplify the answering of questions like:
29
+
30
+ > _"If given two milliliters of bromine (liquid Br<sub>2</sub>), how many grams of bromine does one have?"_
31
+
32
+ To best answer this question, we turn to an age-old technique ([dimensional analysis](https://en.wikipedia.org/wiki/Dimensional_analysis)) which essentially allows for the solution to be written as a product of ratios. `ucon` comes equipped with some useful primitives:
33
+ | Type | Defined In | Purpose | Typical Use Cases |
34
+ | ----------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
35
+ | **`Vector`** | `ucon.dimension` | Represents the exponent tuple of a physical quantity’s base dimensions (e.g., T, L, M, I, Θ, J, N). | Internal representation of dimensional algebra; building derived quantities (e.g., area, velocity, force). |
36
+ | **`Dimension`** | `ucon.dimension` | Encapsulates physical dimensions (e.g., length, time, mass) as algebraic combinations of vectors. | Enforcing dimensional consistency; defining relationships between quantities (e.g., length / time = velocity). |
37
+ | **`Unit`** | `ucon.unit` | Represents a named, dimensioned measurement unit (e.g., meter, second, joule). | Attaching human-readable units to quantities; defining or composing new units (`newton = kilogram * meter / second²`). |
38
+ | **`Scale`** | `ucon.core` | Encodes powers of base magnitudes (binary or decimal prefixes like kilo-, milli-, mebi-). | Adjusting numeric scale without changing dimension (e.g., kilometer ↔ meter, byte ↔ kibibyte). |
39
+ | **`Exponent`** | `ucon.core` | Represents base-power pairs (e.g., 10³, 2¹⁰) used by `Scale`. | Performing arithmetic on powers and bases; normalizing scales across conversions. |
40
+ | **`Number`** | `ucon.core` | Combines a numeric quantity with a unit and scale; the primary measurable type. | Performing arithmetic with units; converting between compatible units; representing physical quantities like 5 m/s. |
41
+ | **`Ratio`** | `ucon.core` | Represents the division of two `Number` objects; captures relationships between quantities. | Expressing rates, densities, efficiencies (e.g., energy / time = power, length / time = velocity). |
42
+ | **`units` module** | `ucon.units` | Defines canonical unit instances (SI and common derived units). | Quick access to standard physical units (`units.meter`, `units.second`, `units.newton`, etc.). | |
43
+
44
+ ### Under the Hood
45
+
46
+ `ucon` models unit math through a hierarchy where each layer builds on the last:
47
+
48
+ ```mermaid
49
+ ---
50
+ config:
51
+ layout: elk
52
+ elk:
53
+ mergeEdges: true # Combines parallel edges
54
+ nodePlacementStrategy: SIMPLE # Other options: SIMPLE, NETWORK_SIMPLEX, BRANDES_KOEPF (default)
55
+ ---
56
+ flowchart LR
57
+ %% --- Algebraic substrate ---
58
+ subgraph "Algebraic Substrate"
59
+ A[Exponent] --> B[Scale]
60
+ end
61
+ %% --- Physical ontology ---
62
+ subgraph "Physical Ontology"
63
+ D[Dimension] --> E[Unit]
64
+ end
65
+ %% --- Value layer ---
66
+ subgraph "Value Layer"
67
+ F[Number]
68
+ G[Ratio]
69
+ end
70
+ %% --- Cross-layer relationships ---
71
+ E --> F
72
+ B --> F
73
+ %% Ratio composes Numbers and also evaluates to a Number
74
+ F --> G
75
+ G --> F
76
+ ```
77
+
78
+ ## Why `ucon`?
79
+
80
+ Python already has mature libraries for handling units and physical quantities — Pint, SymPy, and Unum — each solving part of the same problem from different angles:
81
+
82
+ | Library | Focus | Limitation |
83
+ | --------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
84
+ | **Pint** | Runtime unit conversion and compatibility checking | Treats quantities as decorated numbers — conversions work, but the algebra behind them isn’t inspectable or type-safe. |
85
+ | **SymPy** | Symbolic algebra and simplification of unit expressions | Excellent for symbolic reasoning, but not designed for runtime validation, conversion, or serialization. |
86
+ | **Unum** | Unit-aware arithmetic and unit propagation | Tracks units through arithmetic but lacks explicit dimensional algebra, conversion taxonomy, or runtime introspection. |
87
+
88
+ Together, these tools can _use_ units, but none can explicitly represent and verify the relationships between units and dimensions.
89
+
90
+ That’s the gap `ucon` fills.
91
+
92
+ It treats units, dimensions, and scales as first-class objects and builds a composable algebra around them.
93
+ This allows you to:
94
+ - Represent dimensional meaning explicitly (`Dimension`, `Vector`);
95
+ - Compose and compute with type-safe, introspectable quantities (`Unit`, `Number`);
96
+ - Perform reversible, declarative conversions (standard, linear, affine, nonlinear);
97
+ - Serialize and validate measurements with Pydantic integration;
98
+ - Extend the system with custom unit registries and conversion families.
99
+
100
+ Where Pint, Unum, and SymPy focus on _how_ to compute with units,
101
+ `ucon` focuses on why those computations make sense. Every operation checks the dimensional structure, _not just the unit labels_. This means ucon doesn’t just track names: it enforces physics:
102
+ ```python
103
+ from ucon import Number, units
104
+
105
+ length = Number(quantity=5, unit=units.meter)
106
+ time = Number(quantity=2, unit=units.second)
107
+
108
+ speed = length / time # ✅ valid: L / T = velocity
109
+ invalid = length + time # ❌ raises: incompatible dimensions
110
+ ```
111
+
112
+ ## Setup
113
+
114
+ Simple:
115
+ ```bash
116
+ pip install ucon
117
+ ```
118
+
119
+ ## Usage
120
+
121
+ This sort of dimensional analysis:
122
+ ```
123
+ 2 mL bromine | 3.119 g bromine
124
+ --------------x----------------- #=> 6.238 g bromine
125
+ 1 | 1 mL bromine
126
+ ```
127
+ becomes straightforward when you define a measurement:
128
+ ```python
129
+ from ucon import Number, Scale, Units, Ratio
130
+
131
+ # Two milliliters of bromine
132
+ two_mL_bromine = Number(unit=Units.liter, scale=Scale.milli, quantity=2)
133
+
134
+ # Density of bromine: 3.119 g/mL
135
+ bromine_density = Ratio(
136
+ numerator=Number(unit=Units.gram, quantity=3.119),
137
+ denominator=Number(unit=Units.liter, scale=Scale.milli),
138
+ )
139
+
140
+ # Multiply to find mass
141
+ grams_bromine = two_mL_bromine * bromine_density
142
+ print(grams_bromine) # <6.238 gram>
143
+ ```
144
+
145
+ Scale conversion is automatic and precise:
146
+
147
+ ```python
148
+ grams_bromine.to(Scale.milli) # <6238.0 milligram>
149
+ grams_bromine.to(Scale.kibi) # <0.006091796875 kibigram>
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Roadmap Highlights
155
+
156
+ | Version | Theme | Focus |
157
+ |----------|-------|--------|
158
+ | [**0.3.x**](https://github.com/withtwoemms/ucon/milestone/1) | Primitive Type Refinement | Unified algebraic foundation |
159
+ | [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System | Linear & affine conversions |
160
+ | [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH |
161
+ | [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation |
162
+
163
+ See full roadmap: [ROADMAP.md](./ROADMAP.md)
164
+
165
+ ---
166
+
167
+ ## Contributing
168
+
169
+ Contributions, issues, and pull requests are welcome!
170
+ Ensure `nox` is installed.
171
+ ```
172
+ pip install -r requirements.txt
173
+ ```
174
+ Then run the full test suite (agains all supported python versions) before committing:
175
+
176
+ ```bash
177
+ nox -s test
178
+ ```
179
+ ---
180
+
181
+ > “If it can be measured, it can be represented.
182
+ If it can be represented, it can be validated.
183
+ If it can be validated, it can be trusted.”