ucon 0.3.1__py3-none-any.whl
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.
- tests/ucon/__init__.py +0 -0
- tests/ucon/test_core.py +433 -0
- tests/ucon/test_dimension.py +206 -0
- tests/ucon/test_unit.py +143 -0
- tests/ucon/test_units.py +21 -0
- ucon/__init__.py +49 -0
- ucon/core.py +293 -0
- ucon/dimension.py +172 -0
- ucon/unit.py +92 -0
- ucon/units.py +84 -0
- ucon-0.3.1.dist-info/METADATA +192 -0
- ucon-0.3.1.dist-info/RECORD +15 -0
- ucon-0.3.1.dist-info/WHEEL +5 -0
- ucon-0.3.1.dist-info/licenses/LICENSE +21 -0
- ucon-0.3.1.dist-info/top_level.txt +2 -0
ucon/unit.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ucon.unit
|
|
3
|
+
==========
|
|
4
|
+
|
|
5
|
+
Defines the **Unit** abstraction — the symbolic and algebraic representation of
|
|
6
|
+
a measurable quantity associated with a :class:`ucon.dimension.Dimension`.
|
|
7
|
+
|
|
8
|
+
A :class:`Unit` pairs a human-readable name and aliases with its underlying
|
|
9
|
+
dimension.
|
|
10
|
+
|
|
11
|
+
Units are composable:
|
|
12
|
+
|
|
13
|
+
>>> from ucon import units
|
|
14
|
+
>>> units.meter / units.second
|
|
15
|
+
<velocity | (m/s)>
|
|
16
|
+
|
|
17
|
+
They can be multiplied or divided to form compound units, and their dimensional
|
|
18
|
+
relationships are preserved algebraically.
|
|
19
|
+
"""
|
|
20
|
+
from ucon.dimension import Dimension
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Unit:
|
|
24
|
+
"""
|
|
25
|
+
Represents a **unit of measure** associated with a :class:`Dimension`.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
*aliases : str
|
|
30
|
+
Optional shorthand symbols (e.g., "m", "sec").
|
|
31
|
+
name : str
|
|
32
|
+
Canonical name of the unit (e.g., "meter").
|
|
33
|
+
dimension : Dimension
|
|
34
|
+
The physical dimension this unit represents.
|
|
35
|
+
|
|
36
|
+
Notes
|
|
37
|
+
-----
|
|
38
|
+
Units participate in algebraic operations that produce new compound units:
|
|
39
|
+
|
|
40
|
+
>>> density = units.gram / units.liter
|
|
41
|
+
>>> density.dimension
|
|
42
|
+
<Dimension.density: Vector(T=0, L=-3, M=1, I=0, Θ=0, J=0, N=0)>
|
|
43
|
+
|
|
44
|
+
The combination rules follow the same algebra as :class:`Dimension`.
|
|
45
|
+
"""
|
|
46
|
+
def __init__(self, *aliases: str, name: str = '', dimension: Dimension = Dimension.none):
|
|
47
|
+
self.dimension = dimension
|
|
48
|
+
self.name = name
|
|
49
|
+
self.aliases = aliases
|
|
50
|
+
self.shorthand = aliases[0] if aliases else self.name
|
|
51
|
+
|
|
52
|
+
def __repr__(self):
|
|
53
|
+
addendum = f' | {self.name}' if self.name else ''
|
|
54
|
+
return f'<{self.dimension.name}{addendum}>'
|
|
55
|
+
|
|
56
|
+
# TODO -- limit `operator` param choices
|
|
57
|
+
def generate_name(self, unit: 'Unit', operator: str):
|
|
58
|
+
if (self.dimension is Dimension.none) and not (unit.dimension is Dimension.none):
|
|
59
|
+
return unit.name
|
|
60
|
+
if not (self.dimension is Dimension.none) and (unit.dimension is Dimension.none):
|
|
61
|
+
return self.name
|
|
62
|
+
|
|
63
|
+
if not self.shorthand and not unit.shorthand:
|
|
64
|
+
name = ''
|
|
65
|
+
elif self.shorthand and not unit.shorthand:
|
|
66
|
+
name = f'({self.shorthand}{operator}?)'
|
|
67
|
+
elif not self.shorthand and unit.shorthand:
|
|
68
|
+
name = f'(?{operator}{unit.shorthand})'
|
|
69
|
+
else:
|
|
70
|
+
name = f'({self.shorthand}{operator}{unit.shorthand})'
|
|
71
|
+
return name
|
|
72
|
+
|
|
73
|
+
def __truediv__(self, unit: 'Unit') -> 'Unit':
|
|
74
|
+
# TODO -- define __eq__ for simplification, here
|
|
75
|
+
if (self.name == unit.name) and (self.dimension == unit.dimension):
|
|
76
|
+
return Unit()
|
|
77
|
+
|
|
78
|
+
if (unit.dimension is Dimension.none):
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
return Unit(name=self.generate_name(unit, '/'), dimension=self.dimension / unit.dimension)
|
|
82
|
+
|
|
83
|
+
def __mul__(self, unit: 'Unit') -> 'Unit':
|
|
84
|
+
return Unit(name=self.generate_name(unit, '*'), dimension=self.dimension * unit.dimension)
|
|
85
|
+
|
|
86
|
+
def __eq__(self, unit: 'Unit') -> bool:
|
|
87
|
+
if not isinstance(unit, Unit):
|
|
88
|
+
raise TypeError(f'Cannot compare Unit to non-Unit type: {type(unit)}')
|
|
89
|
+
return (self.name == unit.name) and (self.dimension == unit.dimension)
|
|
90
|
+
|
|
91
|
+
def __hash__(self) -> int:
|
|
92
|
+
return hash(tuple([self.name, self.dimension,]))
|
ucon/units.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ucon.units
|
|
3
|
+
===========
|
|
4
|
+
|
|
5
|
+
Defines and registers the canonical **unit set** for the *ucon* library.
|
|
6
|
+
|
|
7
|
+
This module exports the standard SI base and derived units, along with a few
|
|
8
|
+
common non-SI units. Each unit is a pre-constructed :class:`ucon.unit.Unit`
|
|
9
|
+
object associated with a :class:`ucon.dimension.Dimension`.
|
|
10
|
+
|
|
11
|
+
Example
|
|
12
|
+
-------
|
|
13
|
+
>>> from ucon import units
|
|
14
|
+
>>> units.meter.dimension
|
|
15
|
+
<Dimension.length>
|
|
16
|
+
>>> units.newton.dimension
|
|
17
|
+
<Dimension.force>
|
|
18
|
+
|
|
19
|
+
Includes convenience utilities such as :func:`have(name)` for unit membership
|
|
20
|
+
checks.
|
|
21
|
+
|
|
22
|
+
Notes
|
|
23
|
+
-----
|
|
24
|
+
The design allows for future extensibility: users can register their own units,
|
|
25
|
+
systems, or aliases dynamically, without modifying the core definitions.
|
|
26
|
+
"""
|
|
27
|
+
from ucon.dimension import Dimension
|
|
28
|
+
from ucon.unit import Unit
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
none = Unit()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# -- International System of Units (SI) --------------------------------
|
|
35
|
+
ampere = Unit('I', 'amp', name='ampere', dimension=Dimension.current)
|
|
36
|
+
becquerel = Unit('Bq', name='becquerel', dimension=Dimension.frequency)
|
|
37
|
+
celsius = Unit('°C', name='celsius', dimension=Dimension.temperature)
|
|
38
|
+
coulomb = Unit('C', name='coulomb', dimension=Dimension.charge)
|
|
39
|
+
farad = Unit('F', name='farad', dimension=Dimension.capacitance)
|
|
40
|
+
gram = Unit('g', 'G', name='gram', dimension=Dimension.mass)
|
|
41
|
+
gray = Unit('Gy', name='gray', dimension=Dimension.energy)
|
|
42
|
+
henry = Unit('H', name='henry', dimension=Dimension.inductance)
|
|
43
|
+
hertz = Unit('Hz', name='hertz', dimension=Dimension.frequency)
|
|
44
|
+
hour = Unit('h', 'H', name='hour', dimension=Dimension.time)
|
|
45
|
+
joule = Unit('J', name='joule', dimension=Dimension.energy)
|
|
46
|
+
joule_per_kelvin = Unit('J/K', name='joule_per_kelvin', dimension=Dimension.entropy)
|
|
47
|
+
kelvin = Unit('K', name='kelvin', dimension=Dimension.temperature)
|
|
48
|
+
liter = Unit('L', 'l', name='liter', dimension=Dimension.volume)
|
|
49
|
+
lumen = Unit('lm', name='lumen', dimension=Dimension.luminous_intensity)
|
|
50
|
+
lux = Unit('lx', name='lux', dimension=Dimension.illuminance)
|
|
51
|
+
meter = Unit('m', 'M', name='meter', dimension=Dimension.length)
|
|
52
|
+
mole = Unit('mol', 'n', name='mole', dimension=Dimension.amount_of_substance)
|
|
53
|
+
newton = Unit('N', name='newton', dimension=Dimension.force)
|
|
54
|
+
ohm = Unit('Ω', name='ohm', dimension=Dimension.resistance)
|
|
55
|
+
pascal = Unit('Pa', name='pascal', dimension=Dimension.pressure)
|
|
56
|
+
radian = Unit('rad', name='radian', dimension=Dimension.none)
|
|
57
|
+
second = Unit('s', 'sec', name='second', dimension=Dimension.time)
|
|
58
|
+
sievert = Unit('Sv', name='sievert', dimension=Dimension.energy)
|
|
59
|
+
siemens = Unit('S', name='siemens', dimension=Dimension.conductance)
|
|
60
|
+
steradian = Unit('sr', name='steradian', dimension=Dimension.none)
|
|
61
|
+
tesla = Unit('T', name='tesla', dimension=Dimension.magnetic_flux_density)
|
|
62
|
+
volt = Unit('V', name='volt', dimension=Dimension.voltage)
|
|
63
|
+
watt = Unit('W', name='watt', dimension=Dimension.power)
|
|
64
|
+
webers = Unit('Wb', name='weber', dimension=Dimension.magnetic_flux)
|
|
65
|
+
webers_per_meter = Unit('Wb/m', name='webers_per_meter', dimension=Dimension.magnetic_permeability)
|
|
66
|
+
# ----------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def have(name: str) -> bool:
|
|
70
|
+
assert name, "Must provide a unit name to check"
|
|
71
|
+
assert isinstance(name, str), "Unit name must be a string"
|
|
72
|
+
target = name.lower()
|
|
73
|
+
for attr, val in globals().items():
|
|
74
|
+
if isinstance(val, Unit):
|
|
75
|
+
# match the variable name (e.g., "none", "meter")
|
|
76
|
+
if attr.lower() == target:
|
|
77
|
+
return True
|
|
78
|
+
# match the declared unit name
|
|
79
|
+
if val.name and val.name.lower() == target:
|
|
80
|
+
return True
|
|
81
|
+
# match any alias
|
|
82
|
+
if any((alias or "").lower() == target for alias in getattr(val, "aliases", ())):
|
|
83
|
+
return True
|
|
84
|
+
return False
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ucon
|
|
3
|
+
Version: 0.3.1
|
|
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="200" />
|
|
38
|
+
|
|
39
|
+
# ucon
|
|
40
|
+
|
|
41
|
+
> Pronounced: _yoo · cahn_
|
|
42
|
+
|
|
43
|
+
[](https://github.com/withtwoemms/ucon/actions?query=workflow%3Atests)
|
|
44
|
+
[](https://codecov.io/gh/withtwoemms/ucon)
|
|
45
|
+
[](https://github.com/withtwoemms/ucon/actions?query=workflow%3Apublish)
|
|
46
|
+
|
|
47
|
+
> A lightweight, **unit-aware computation library** for Python — built on first-principles.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Overview
|
|
52
|
+
|
|
53
|
+
`ucon` helps Python understand the *physical meaning* of your numbers.
|
|
54
|
+
It combines **units**, **scales**, and **dimensions** into a composable algebra that supports:
|
|
55
|
+
|
|
56
|
+
- Dimensional analysis through `Number` and `Ratio`
|
|
57
|
+
- Scale-aware arithmetic and conversions
|
|
58
|
+
- Metric and binary prefixes (`kilo`, `kibi`, `micro`, `mebi`, ect.)
|
|
59
|
+
- A clean foundation for physics, chemistry, data modeling, and beyond
|
|
60
|
+
|
|
61
|
+
Think of it as **`decimal.Decimal` for the physical world** — precise, predictable, and type-safe.
|
|
62
|
+
|
|
63
|
+
## Introduction
|
|
64
|
+
|
|
65
|
+
The crux of this tiny library is to provide abstractions that simplify the answering of questions like:
|
|
66
|
+
|
|
67
|
+
> _"If given two milliliters of bromine (liquid Br<sub>2</sub>), how many grams of bromine does one have?"_
|
|
68
|
+
|
|
69
|
+
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:
|
|
70
|
+
| Type | Defined In | Purpose | Typical Use Cases |
|
|
71
|
+
| ----------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| **`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). |
|
|
73
|
+
| **`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). |
|
|
74
|
+
| **`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²`). |
|
|
75
|
+
| **`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). |
|
|
76
|
+
| **`Exponent`** | `ucon.core` | Represents base-power pairs (e.g., 10³, 2¹⁰) used by `Scale`. | Performing arithmetic on powers and bases; normalizing scales across conversions. |
|
|
77
|
+
| **`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. |
|
|
78
|
+
| **`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). |
|
|
79
|
+
| **`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.). | |
|
|
80
|
+
|
|
81
|
+
### Under the Hood
|
|
82
|
+
|
|
83
|
+
`ucon` models unit math through a hierarchy where each layer builds on the last:
|
|
84
|
+
|
|
85
|
+
<img src=https://gist.githubusercontent.com/withtwoemms/429d2ca1f979865aa80a2658bf9efa32/raw/f3518d37445301950026fc9ffd1bd062768005fe/ucon.data-model.png align="center" alt="ucon Data Model" width=600/>
|
|
86
|
+
|
|
87
|
+
## Why `ucon`?
|
|
88
|
+
|
|
89
|
+
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:
|
|
90
|
+
|
|
91
|
+
| Library | Focus | Limitation |
|
|
92
|
+
| --------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| **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. |
|
|
94
|
+
| **SymPy** | Symbolic algebra and simplification of unit expressions | Excellent for symbolic reasoning, but not designed for runtime validation, conversion, or serialization. |
|
|
95
|
+
| **Unum** | Unit-aware arithmetic and unit propagation | Tracks units through arithmetic but lacks explicit dimensional algebra, conversion taxonomy, or runtime introspection. |
|
|
96
|
+
|
|
97
|
+
Together, these tools can _use_ units, but none can explicitly represent and verify the relationships between units and dimensions.
|
|
98
|
+
|
|
99
|
+
That’s the gap `ucon` fills.
|
|
100
|
+
|
|
101
|
+
It treats units, dimensions, and scales as first-class objects and builds a composable algebra around them.
|
|
102
|
+
This allows you to:
|
|
103
|
+
- Represent dimensional meaning explicitly (`Dimension`, `Vector`);
|
|
104
|
+
- Compose and compute with type-safe, introspectable quantities (`Unit`, `Number`);
|
|
105
|
+
- Perform reversible, declarative conversions (standard, linear, affine, nonlinear);
|
|
106
|
+
- Serialize and validate measurements with Pydantic integration;
|
|
107
|
+
- Extend the system with custom unit registries and conversion families.
|
|
108
|
+
|
|
109
|
+
Where Pint, Unum, and SymPy focus on _how_ to compute with units,
|
|
110
|
+
`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:
|
|
111
|
+
```python
|
|
112
|
+
from ucon import Number, units
|
|
113
|
+
|
|
114
|
+
length = Number(quantity=5, unit=units.meter)
|
|
115
|
+
time = Number(quantity=2, unit=units.second)
|
|
116
|
+
|
|
117
|
+
speed = length / time # ✅ valid: L / T = velocity
|
|
118
|
+
invalid = length + time # ❌ raises: incompatible dimensions
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Setup
|
|
122
|
+
|
|
123
|
+
Simple:
|
|
124
|
+
```bash
|
|
125
|
+
pip install ucon
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Usage
|
|
129
|
+
|
|
130
|
+
This sort of dimensional analysis:
|
|
131
|
+
```
|
|
132
|
+
2 mL bromine | 3.119 g bromine
|
|
133
|
+
--------------x----------------- #=> 6.238 g bromine
|
|
134
|
+
1 | 1 mL bromine
|
|
135
|
+
```
|
|
136
|
+
becomes straightforward when you define a measurement:
|
|
137
|
+
```python
|
|
138
|
+
from ucon import Number, Scale, Units, Ratio
|
|
139
|
+
|
|
140
|
+
# Two milliliters of bromine
|
|
141
|
+
two_mL_bromine = Number(unit=Units.liter, scale=Scale.milli, quantity=2)
|
|
142
|
+
|
|
143
|
+
# Density of bromine: 3.119 g/mL
|
|
144
|
+
bromine_density = Ratio(
|
|
145
|
+
numerator=Number(unit=Units.gram, quantity=3.119),
|
|
146
|
+
denominator=Number(unit=Units.liter, scale=Scale.milli),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Multiply to find mass
|
|
150
|
+
grams_bromine = two_mL_bromine * bromine_density
|
|
151
|
+
print(grams_bromine) # <6.238 gram>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Scale conversion is automatic and precise:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
grams_bromine.to(Scale.milli) # <6238.0 milligram>
|
|
158
|
+
grams_bromine.to(Scale.kibi) # <0.006091796875 kibigram>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Roadmap Highlights
|
|
164
|
+
|
|
165
|
+
| Version | Theme | Focus |
|
|
166
|
+
|----------|-------|--------|
|
|
167
|
+
| [**0.3.x**](https://github.com/withtwoemms/ucon/milestone/1) | Primitive Type Refinement | Unified algebraic foundation |
|
|
168
|
+
| [**0.4.x**](https://github.com/withtwoemms/ucon/milestone/2) | Conversion System | Linear & affine conversions |
|
|
169
|
+
| [**0.6.x**](https://github.com/withtwoemms/ucon/milestone/4) | Nonlinear / Specialized Units | Decibel, Percent, pH |
|
|
170
|
+
| [**0.8.x**](https://github.com/withtwoemms/ucon/milestone/6) | Pydantic Integration | Type-safe quantity validation |
|
|
171
|
+
|
|
172
|
+
See full roadmap: [ROADMAP.md](./ROADMAP.md)
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Contributing
|
|
177
|
+
|
|
178
|
+
Contributions, issues, and pull requests are welcome!
|
|
179
|
+
Ensure `nox` is installed.
|
|
180
|
+
```
|
|
181
|
+
pip install -r requirements.txt
|
|
182
|
+
```
|
|
183
|
+
Then run the full test suite (agains all supported python versions) before committing:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
nox -s test
|
|
187
|
+
```
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
> “If it can be measured, it can be represented.
|
|
191
|
+
If it can be represented, it can be validated.
|
|
192
|
+
If it can be validated, it can be trusted.”
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
tests/ucon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
tests/ucon/test_core.py,sha256=fD4c6K35RxCyaR5pHqBSr8DEHTqmMvYX9ZuVS6JdSwo,16408
|
|
3
|
+
tests/ucon/test_dimension.py,sha256=JyA9ySFvohs2l6oK77ehCQ7QvvFVqB_9t0iC7CUErjw,7296
|
|
4
|
+
tests/ucon/test_unit.py,sha256=vEPOeSxFBqcRBAUczCN9KPo_dTmLk4LQExPSt6UGVa4,5712
|
|
5
|
+
tests/ucon/test_units.py,sha256=248JZbo8RVvG_q3T0IhKG43vxM4F_2Xgf4_RjGZNsFM,704
|
|
6
|
+
ucon/__init__.py,sha256=ZWWLodIiG17OgCfoAm532wpwmJzdRXlUGX3w6OBxFeQ,1743
|
|
7
|
+
ucon/core.py,sha256=DRymayBlXfVZMSHIt_Hi-xe8GPs9WYHOCCJ1QDaKn7Q,10643
|
|
8
|
+
ucon/dimension.py,sha256=uUP05bPE8r15oFeD36DrclNIfBsugV7uFhvtJRYy4qI,6598
|
|
9
|
+
ucon/unit.py,sha256=KxOBcQNxciljGskhZCfktLhRF5u-rWgrTg565Flo3eI,3213
|
|
10
|
+
ucon/units.py,sha256=e1j7skYMghlMZi7l94EAgxq4_lNRDC7FcSooJoE_U50,3689
|
|
11
|
+
ucon-0.3.1.dist-info/licenses/LICENSE,sha256=-Djjiq2wM8Cc6fzTsdMbr_T2_uaX6Yorxcemr3GGkqc,1072
|
|
12
|
+
ucon-0.3.1.dist-info/METADATA,sha256=kchGc0Q4C3RSbQdT1WxGlFMEsRqp1ZNnptzy1VF4SQo,10603
|
|
13
|
+
ucon-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
+
ucon-0.3.1.dist-info/top_level.txt,sha256=zZYRJiQrVUtN32ziJD2YEq7ClSvDmVYHYy5ArRAZGxI,11
|
|
15
|
+
ucon-0.3.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Emmanuel I. Obi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|