unit-syntax 0.3.1__tar.gz → 0.4.0__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.
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ ## 0.4.0
4
+
5
+ ### Added
6
+
7
+ - Optional static dimensional checking with `unit-syntax check` and
8
+ `python -m unit_syntax check`. Install it with `unit-syntax[check]`.
9
+ The checker uses ty, checks project imports without executing user code,
10
+ and reports diagnostics at original filenames, lines, and columns.
11
+ - Dimension-aware `Quantity[D]` annotations and named physical dimensions in
12
+ `unit_syntax.typing`, generated arithmetic overloads, and a `py.typed` marker.
13
+ Common dimensional products and quotients are normalized; other combinations
14
+ retain symbolic dimensions and may be conservatively rejected.
15
+ - Static checks for incompatible arithmetic and unit conversions, dimensional
16
+ function arguments, and numeric conversion of physical quantities. Only
17
+ dimensionless quantities support implicit numeric conversion in the typing
18
+ contract; angles and solid angles remain distinct dimensions. Runtime values
19
+ remain ordinary Pint quantities.
20
+ - Grammar and dimensional-checking regression tests, a typed-units example,
21
+ grammar provenance and regeneration instructions, and validation notes.
22
+ - CI coverage for Python 3.11, 3.14, and 3.15, including parser and typing-stub
23
+ regeneration checks and distribution builds.
24
+
25
+ ### Changed
26
+
27
+ - Updated the parser to target Python 3.15 syntax, including generic declarations,
28
+ type parameter defaults, modern f-strings, template strings, unparenthesized
29
+ exception lists, lazy imports, and unpacking comprehensions. Executing newer
30
+ syntax requires an interpreter that supports it.
31
+ - Replaced `import-transforms` with a loader using Python's public `importlib`
32
+ interfaces. Imported modules and standalone scripts compile the transformed
33
+ AST directly, with unit-construction globals supplied by unit-syntax.
34
+ - Raised the minimum Python version from 3.8 to 3.10 and updated Pint, IPython,
35
+ and pegen requirements, development dependencies, and the dependency lockfile.
36
+ - Updated release tooling to build and test with the optional checker enabled.
37
+
38
+ ### Fixed
39
+
40
+ - Preserved CPython UTF-8 byte-column locations and the separate source spans
41
+ of unit values, suffixes, and complete applications, including parentheses
42
+ in nested conversions such as `(1 inch) mm`.
43
+ - Preserved original source locations when lowering unit expressions to calls.
44
+ - Prevented Unicode separators inside strings from introducing false source
45
+ lines in parser locations or dimensional-checking diagnostics.
46
+ - Preserved template interpolation expression text across Python 3.14 and 3.15
47
+ differences in trailing whitespace handling.
@@ -0,0 +1,271 @@
1
+ Metadata-Version: 2.4
2
+ Name: unit-syntax
3
+ Version: 0.4.0
4
+ Summary: Physical unit literals for Jupyter and IPython
5
+ Author: Adam Hupp
6
+ Author-email: adam@hupp.org
7
+ Requires-Python: >=3.10,<4
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Programming Language :: Python :: 3.15
15
+ Provides-Extra: check
16
+ Requires-Dist: Pint (>=0.24.4) ; python_version < "3.11"
17
+ Requires-Dist: Pint (>=0.25.3) ; python_version == "3.11"
18
+ Requires-Dist: Pint (>=0.26.2) ; python_version >= "3.12"
19
+ Requires-Dist: ipython (>=8) ; python_version < "3.11"
20
+ Requires-Dist: ipython (>=9) ; python_version >= "3.11"
21
+ Requires-Dist: pegen (>=0.3,<0.4)
22
+ Requires-Dist: ty (>=0.0.95) ; extra == "check"
23
+ Project-URL: Repository, https://github.com/ahupp/unit-syntax
24
+ Description-Content-Type: text/markdown
25
+
26
+ `unit-syntax` adds support for physical units to the Python language:
27
+
28
+ ```python
29
+ >>> speed = 5 meters/second
30
+ >>> (2 seconds) * speed
31
+ 10 meter
32
+ ```
33
+
34
+ Why? I like to use Python as a calculator for physical problems and wished it had the type safety of explicit units along with the readability of normal notation.
35
+
36
+ `unit-syntax` works in Jupyter notebooks, standalone Python scripts, and Python packages.
37
+
38
+ [How does it work?](https://github.com/ahupp/unit-syntax#how-does-it-work)
39
+
40
+ ## Getting Started
41
+
42
+ Install the package:
43
+
44
+ ```shell
45
+ $ pip install unit-syntax
46
+ ```
47
+
48
+ ### ... with Jupyter/IPython
49
+
50
+ To enable unit-syntax in a Jupyter/IPython session run:
51
+
52
+ ```python
53
+ %load_ext unit_syntax
54
+ ```
55
+
56
+ Tip: In Jupyter this must be run in its own cell before any units expressions are evaluated.
57
+
58
+ ### ... with standalone scripts
59
+
60
+ To run a standalone script with units:
61
+
62
+ ```
63
+ $ python -m unit_syntax <path_to_script.py>
64
+ ```
65
+
66
+ Note that this installs a custom import hook that affects all imports performed by the script.
67
+
68
+ ### ... with Python packages
69
+
70
+ To use/distribute a package with unit-syntax, add this in your `__init__.py`:
71
+
72
+ ```python
73
+ from unit_syntax.import_hook import enable_units_for_package
74
+ enable_units_for_package(__name__)
75
+ ```
76
+
77
+ This applies the transform only to sub-modules of your package.
78
+
79
+ ## Usage
80
+
81
+ [An interactive notebook to play around with units](https://colab.research.google.com/drive/1PInyLGZHnUzEuUVgMsLrUUNdCurXK7v1#scrollTo=JszzXmATY0TV)
82
+
83
+ Units can be applied to any "simple" expression:
84
+
85
+ - number: `1 meter`
86
+ - variables: `x parsec`, `y.z watts`, `area[id] meters**2`
87
+ - lists and tuples: `[1., 37.] newton meters`
88
+ - unary operators: `-x dBm`
89
+ - power: `x**2 meters`
90
+
91
+ In expressions mixing units and binary operators, parenthesize:
92
+
93
+ ```python
94
+ one_lux = (1 lumen)/(1 meter**2)
95
+ ```
96
+
97
+ Units can be used in any place where Python allows expressions, e.g:
98
+
99
+ - function arguments: `area_of_circle(radius=1 meter)`
100
+ - list comprehensions: `[x meters for x in range(10)]`
101
+
102
+ Quantities can be converted to another measurement system:
103
+
104
+ ```python
105
+ >>> (88 miles / hour) furlongs / fortnight
106
+ 236543.5269120001 furlong / fortnight
107
+ >>> (0 degC) degF
108
+ 31.999999999999936 degree_Fahrenheit
109
+ ```
110
+
111
+ Compound units (e.g. `newtons/meter**2`) are supported and follow the usual precedence rules.
112
+
113
+ Units _may not_ begin with parentheses (consider the possible
114
+ interpretations of `x (meters)`). Parentheses are allowed anywhere else:
115
+
116
+ ```python
117
+ # parsed as a function call, will result in a runtime error
118
+ x (newton meters)/(second*kg)
119
+ # a-ok
120
+ x newton meters/(second*kg)
121
+ ```
122
+
123
+ Using unknown units produces a syntax error at import time:
124
+
125
+ ```python
126
+ >>> 1 smoot
127
+ ...
128
+ SyntaxError: 'smoot' is not defined in the unit registry
129
+ ```
130
+
131
+ ## How does it work?
132
+
133
+ `unit-syntax` transforms python-with-units into standard python that calls the excellent [pint](https://pint.readthedocs.io/en/stable/) units handling library.
134
+
135
+ The parser is [pegen](https://we-like-parsers.github.io/pegen/), which is a standalone version of the same parser generator used by Python itself. The grammar is a [lightly modified](https://github.com/ahupp/unit-syntax/compare/base-grammar..main#diff-7405fdc26614e4d2e7f8f37c9b559ccb3a7f7c619d41e207dda28afdfae20f83) version the official Python grammar shipped with pegen.
136
+
137
+ The grammar now targets **Python 3.15**, including generic declarations, type
138
+ parameter defaults, f-strings and template strings, unparenthesized exception
139
+ types, unpacking comprehensions, and lazy imports. Execution of newer syntax
140
+ requires a Python interpreter that supports it. Python 3.10 is the minimum
141
+ supported interpreter. See [GRAMMAR.md](GRAMMAR.md) for the pinned upstream
142
+ sources and regeneration instructions.
143
+
144
+ Syntax transformation in IPython/Jupyter uses [IPython custom input transformers](https://ipython.readthedocs.io/en/stable/config/inputtransforms.html).
145
+
146
+ Syntax transformation of arbitrary Python modules uses Python's public
147
+ `importlib` finder and loader interfaces; see `unit_syntax.import_hook`.
148
+
149
+ ## Static dimension checking
150
+
151
+ Install the optional checker, then check a script or package before executing it:
152
+
153
+ ```sh
154
+ pip install 'unit-syntax[check]'
155
+ python -m unit_syntax check model.py
156
+ # Equivalent console command:
157
+ unit-syntax check my_package/
158
+ ```
159
+
160
+ The checker uses **ty**. It creates a temporary Python view of the source, gives
161
+ every unit literal its physical dimension, checks project imports, and maps
162
+ diagnostics back to the original filenames and positions. It never executes the
163
+ checked code or writes transformed code into the project. Running `ty check`
164
+ directly on a file containing unit suffixes will still produce syntax errors;
165
+ use the `unit-syntax check` entry point. `--project PATH` explicitly selects the
166
+ root for imports. Syntax-only errors in unrelated, unselected files do not
167
+ prevent checking the selected files.
168
+
169
+ ```python
170
+ from math import sqrt
171
+
172
+ width = 18 mm
173
+ height = 27 mm
174
+ ratio = width / height
175
+ ear = width * sqrt(1 - ratio * ratio) # accepted: ratio is dimensionless
176
+
177
+ sqrt(width) # error: Quantity[Length] is not dimensionless
178
+ width + (2 seconds) # error: Length and Time are incompatible
179
+ converted = (2 seconds) mm # error: conversion requires compatible dimensions
180
+ ```
181
+
182
+ Function annotations can express the same contract:
183
+
184
+ ```python
185
+ from unit_syntax.typing import Quantity, Length, Area
186
+
187
+ def surface_area(radius: Quantity[Length]) -> Quantity[Area]:
188
+ return radius * radius
189
+
190
+ surface_area(5 mm) # accepted
191
+ surface_area(5 seconds) # rejected by the checker
192
+ ```
193
+
194
+ In `unit_syntax.typing`, `Quantity[D]` carries a **dimension**. This differs
195
+ from Pint's `pint.Quantity[T]`, whose type parameter describes the **magnitude**
196
+ (such as `float` or an array), not whether the value is a length or time. Pint's
197
+ existing annotations do not perform this dimensional checking. At runtime
198
+ `unit_syntax.typing.Quantity` is an alias for Pint's real `Quantity`; values,
199
+ registries, formatting, and arithmetic continue to use Pint.
200
+
201
+ Only `Quantity[Dimensionless]` supports `__float__` in the typing contract.
202
+ Pint already performs dimensionless numeric conversion at runtime, including
203
+ scale normalization: `float((1 m) / (1 mm))` is `1000`. Angles remain a distinct
204
+ static dimension, even though Pint treats radians as dimensionless. Convert an
205
+ angle to radians and explicitly extract its magnitude when calling Python's
206
+ plain numeric trigonometric functions.
207
+
208
+ The operator annotations normalize common dimensions, including area, volume,
209
+ velocity, acceleration, force, pressure, energy, power, and density. Matching
210
+ dimensions divide to a dimensionless quantity, including arbitrary compound
211
+ units written as literals. Other products and quotients retain symbolic
212
+ dimension types. Python typing cannot generally simplify arbitrary exponent
213
+ algebra, so an equivalent expression outside the normalized set can be
214
+ conservatively rejected. Physical powers currently require a supported literal
215
+ integer exponent (`-3` through `4`); dimensionless powers accept ordinary
216
+ numbers. Magnitude dtype and shape constraints, runtime-selected units,
217
+ arbitrary `.to()` strings, and Pint contexts remain runtime checks. Untyped
218
+ Python values retain the usual limits of gradual type checking.
219
+
220
+ The checker resolves literal unit names with its supplied registry (the Pint
221
+ application registry by default). Python API callers can pass their own registry
222
+ to `unit_syntax.check.checking_source`; checking does not execute package
223
+ initializers to discover custom registry definitions.
224
+
225
+ ## Why only allow units on simple expressions?
226
+
227
+ Imagine units were instead parsed as operator with high precedence and you wrote this reasonable looking expression:
228
+
229
+ ```python
230
+ ppi = 300 pixels/inch
231
+ y = x inches * ppi
232
+ ```
233
+
234
+ `inches * ppi` would be parsed as the unit, leading to (at best) a runtime error sometime later and at worst an incorrect calculation. This could be avoided by parenthesizing the expression (e.g. `(x inches) * ppi`, but if that's optional it's easy to forget. So the intent of this restriction is to make these risky forms uncommon and thus more obvious. This is not a hypothetical concern, I hit this within 10 minutes of first using the initial syntax.
235
+
236
+ ## Prior Art
237
+
238
+ The immediate inspriration of `unit-syntax` is a language called [Fortress](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.180.6323&rep=rep1&type=pdf) from Sun Microsystems. Fortress was intended as a modern Fortran, and had first-class support for units in both the syntax and type system.
239
+
240
+ F# (an OCaml derivative from Microsoft) also [has first class support for units](https://en.wikibooks.org/wiki/F_Sharp_Programming/Units_of_Measure).
241
+
242
+ The Julia package [Unitful.jl](http://painterqubits.github.io/Unitful.jl/stable/)
243
+
244
+ A [long discussion on the python-ideas mailing list](https://lwn.net/Articles/900739/) about literal units in Python.
245
+
246
+ ## Development
247
+
248
+ To regenerate the parser:
249
+
250
+ `python -m pegen python_units.gram -o unit_syntax/parser.py`
251
+
252
+ Running tests:
253
+
254
+ ```
255
+ $ poetry install --with dev --extras check
256
+ $ poetry run pytest
257
+ $ poetry run python scripts/generate_typing.py
258
+ $ poetry run ty check unit_syntax/typing.pyi
259
+ ```
260
+
261
+ ## Future work and open questions
262
+
263
+ - Test against various ipython and python versions
264
+ - Ensure bytecode caching still works
265
+ - Test with wider range of source files with the wildcard loader
266
+ - Extend static normalization to more dimensional products and exponent expressions.
267
+ - Typography of output
268
+ - pre-parse units
269
+ - talk to pint about interop between UnitRegistries
270
+ - Fix reported location of SyntaxError when paren missing
271
+
@@ -1,23 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: unit-syntax
3
- Version: 0.3.1
4
- Summary: Physical unit literals for Jupyter and IPython
5
- Home-page: https://github.com/ahupp/unit-syntax
6
- Author: Adam Hupp
7
- Author-email: adam@hupp.org
8
- Requires-Python: >=3.8,<4
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.8
11
- Classifier: Programming Language :: Python :: 3.9
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Requires-Dist: Pint (>0.20)
15
- Requires-Dist: import-transforms (>=0.6,<0.7)
16
- Requires-Dist: ipython (>=7)
17
- Requires-Dist: pegen (>=0.2,<0.3)
18
- Project-URL: Repository, https://github.com/ahupp/unit-syntax
19
- Description-Content-Type: text/markdown
20
-
21
1
  `unit-syntax` adds support for physical units to the Python language:
22
2
 
23
3
  ```python
@@ -129,9 +109,93 @@ SyntaxError: 'smoot' is not defined in the unit registry
129
109
 
130
110
  The parser is [pegen](https://we-like-parsers.github.io/pegen/), which is a standalone version of the same parser generator used by Python itself. The grammar is a [lightly modified](https://github.com/ahupp/unit-syntax/compare/base-grammar..main#diff-7405fdc26614e4d2e7f8f37c9b559ccb3a7f7c619d41e207dda28afdfae20f83) version the official Python grammar shipped with pegen.
131
111
 
112
+ The grammar now targets **Python 3.15**, including generic declarations, type
113
+ parameter defaults, f-strings and template strings, unparenthesized exception
114
+ types, unpacking comprehensions, and lazy imports. Execution of newer syntax
115
+ requires a Python interpreter that supports it. Python 3.10 is the minimum
116
+ supported interpreter. See [GRAMMAR.md](GRAMMAR.md) for the pinned upstream
117
+ sources and regeneration instructions.
118
+
132
119
  Syntax transformation in IPython/Jupyter uses [IPython custom input transformers](https://ipython.readthedocs.io/en/stable/config/inputtransforms.html).
133
120
 
134
- Syntax transformation of arbitrary Python modules uses [importlib](https://docs.python.org/3/library/importlib.html)'s [MetaPathFinder](https://docs.python.org/3/library/importlib.html#importlib.abc.MetaPathFinder), see [import-transforms](https://github.com/ahupp/import-transformss) and [unit_syntax.import_hook](https://github.com/ahupp/unit-syntax/blob/main/unit_syntax/import_hook.py) for details.
121
+ Syntax transformation of arbitrary Python modules uses Python's public
122
+ `importlib` finder and loader interfaces; see `unit_syntax.import_hook`.
123
+
124
+ ## Static dimension checking
125
+
126
+ Install the optional checker, then check a script or package before executing it:
127
+
128
+ ```sh
129
+ pip install 'unit-syntax[check]'
130
+ python -m unit_syntax check model.py
131
+ # Equivalent console command:
132
+ unit-syntax check my_package/
133
+ ```
134
+
135
+ The checker uses **ty**. It creates a temporary Python view of the source, gives
136
+ every unit literal its physical dimension, checks project imports, and maps
137
+ diagnostics back to the original filenames and positions. It never executes the
138
+ checked code or writes transformed code into the project. Running `ty check`
139
+ directly on a file containing unit suffixes will still produce syntax errors;
140
+ use the `unit-syntax check` entry point. `--project PATH` explicitly selects the
141
+ root for imports. Syntax-only errors in unrelated, unselected files do not
142
+ prevent checking the selected files.
143
+
144
+ ```python
145
+ from math import sqrt
146
+
147
+ width = 18 mm
148
+ height = 27 mm
149
+ ratio = width / height
150
+ ear = width * sqrt(1 - ratio * ratio) # accepted: ratio is dimensionless
151
+
152
+ sqrt(width) # error: Quantity[Length] is not dimensionless
153
+ width + (2 seconds) # error: Length and Time are incompatible
154
+ converted = (2 seconds) mm # error: conversion requires compatible dimensions
155
+ ```
156
+
157
+ Function annotations can express the same contract:
158
+
159
+ ```python
160
+ from unit_syntax.typing import Quantity, Length, Area
161
+
162
+ def surface_area(radius: Quantity[Length]) -> Quantity[Area]:
163
+ return radius * radius
164
+
165
+ surface_area(5 mm) # accepted
166
+ surface_area(5 seconds) # rejected by the checker
167
+ ```
168
+
169
+ In `unit_syntax.typing`, `Quantity[D]` carries a **dimension**. This differs
170
+ from Pint's `pint.Quantity[T]`, whose type parameter describes the **magnitude**
171
+ (such as `float` or an array), not whether the value is a length or time. Pint's
172
+ existing annotations do not perform this dimensional checking. At runtime
173
+ `unit_syntax.typing.Quantity` is an alias for Pint's real `Quantity`; values,
174
+ registries, formatting, and arithmetic continue to use Pint.
175
+
176
+ Only `Quantity[Dimensionless]` supports `__float__` in the typing contract.
177
+ Pint already performs dimensionless numeric conversion at runtime, including
178
+ scale normalization: `float((1 m) / (1 mm))` is `1000`. Angles remain a distinct
179
+ static dimension, even though Pint treats radians as dimensionless. Convert an
180
+ angle to radians and explicitly extract its magnitude when calling Python's
181
+ plain numeric trigonometric functions.
182
+
183
+ The operator annotations normalize common dimensions, including area, volume,
184
+ velocity, acceleration, force, pressure, energy, power, and density. Matching
185
+ dimensions divide to a dimensionless quantity, including arbitrary compound
186
+ units written as literals. Other products and quotients retain symbolic
187
+ dimension types. Python typing cannot generally simplify arbitrary exponent
188
+ algebra, so an equivalent expression outside the normalized set can be
189
+ conservatively rejected. Physical powers currently require a supported literal
190
+ integer exponent (`-3` through `4`); dimensionless powers accept ordinary
191
+ numbers. Magnitude dtype and shape constraints, runtime-selected units,
192
+ arbitrary `.to()` strings, and Pint contexts remain runtime checks. Untyped
193
+ Python values retain the usual limits of gradual type checking.
194
+
195
+ The checker resolves literal unit names with its supplied registry (the Pint
196
+ application registry by default). Python API callers can pass their own registry
197
+ to `unit_syntax.check.checking_source`; checking does not execute package
198
+ initializers to discover custom registry definitions.
135
199
 
136
200
  ## Why only allow units on simple expressions?
137
201
 
@@ -163,8 +227,10 @@ To regenerate the parser:
163
227
  Running tests:
164
228
 
165
229
  ```
166
- $ poetry install --with dev
230
+ $ poetry install --with dev --extras check
167
231
  $ poetry run pytest
232
+ $ poetry run python scripts/generate_typing.py
233
+ $ poetry run ty check unit_syntax/typing.pyi
168
234
  ```
169
235
 
170
236
  ## Future work and open questions
@@ -172,9 +238,8 @@ $ poetry run pytest
172
238
  - Test against various ipython and python versions
173
239
  - Ensure bytecode caching still works
174
240
  - Test with wider range of source files with the wildcard loader
175
- - Unit type hints, maybe checked with [@runtime_checkable](https://docs.python.org/3/library/typing.html#typing.runtime_checkable). More Pint typechecking [discussion](https://github.com/hgrecco/pint/issues/1166)
241
+ - Extend static normalization to more dimensional products and exponent expressions.
176
242
  - Typography of output
177
243
  - pre-parse units
178
244
  - talk to pint about interop between UnitRegistries
179
245
  - Fix reported location of SyntaxError when paren missing
180
-
@@ -0,0 +1,47 @@
1
+ [tool.poetry]
2
+ name = "unit-syntax"
3
+ version = "0.4.0"
4
+ authors = ["Adam Hupp <adam@hupp.org>"]
5
+ packages = [{ include = "unit_syntax" }]
6
+ readme = "README.md"
7
+ include = [{ path = "CHANGELOG.md", format = "sdist" }]
8
+ repository = "https://github.com/ahupp/unit-syntax"
9
+ description = "Physical unit literals for Jupyter and IPython"
10
+
11
+ [tool.poetry.dependencies]
12
+ python = ">=3.10, < 4"
13
+ Pint = [
14
+ {version = ">=0.24.4", python = "<3.11"},
15
+ {version = ">=0.25.3", python = ">=3.11,<3.12"},
16
+ {version = ">=0.26.2", python = ">=3.12"},
17
+ ]
18
+ ipython = [
19
+ {version = ">=8", python = "<3.11"},
20
+ {version = ">=9", python = ">=3.11"},
21
+ ]
22
+ pegen = "^0.3"
23
+ ty = {version = ">=0.0.95", optional = true}
24
+
25
+ [tool.poetry.extras]
26
+ check = ["ty"]
27
+
28
+ [tool.poetry.scripts]
29
+ unit-syntax = "unit_syntax.check:main"
30
+
31
+ [tool.poetry.group.dev.dependencies]
32
+ pytest = ">=8"
33
+ black = ">=25"
34
+ numpy = [
35
+ {version = ">=1.24.3", python = "<3.11"},
36
+ {version = ">=2.4", python = ">=3.11,<3.12"},
37
+ {version = ">=2.5.4", python = ">=3.12"},
38
+ ]
39
+ ty = ">=0.0.95"
40
+
41
+ [build-system]
42
+ requires = ["poetry-core>=1.0.0"]
43
+ build-backend = "poetry.core.masonry.api"
44
+
45
+ [tool.black]
46
+ include = '\.pyi?$'
47
+ extend-exclude = 'parser.py$'
@@ -0,0 +1,27 @@
1
+ import sys
2
+ from .transform import UnitSourceTransform
3
+
4
+ if len(sys.argv) < 2:
5
+ sys.exit("usage: python -m unit_syntax <script.py>")
6
+
7
+ if sys.argv[1] == "check":
8
+ from .check import main
9
+
10
+ sys.exit(main(sys.argv[1:]))
11
+
12
+ from pathlib import Path
13
+ from .import_hook import enable_units_everywhere
14
+
15
+ filename = sys.argv[1]
16
+ sys.argv = sys.argv[1:]
17
+ sys.path.insert(0, str(Path(filename).resolve().parent))
18
+ transform = UnitSourceTransform(None)
19
+ enable_units_everywhere()
20
+ namespace = {
21
+ "__name__": "__main__",
22
+ "__file__": filename,
23
+ "__package__": None,
24
+ **transform.injected_globals(),
25
+ }
26
+ source = Path(filename).read_text(encoding="utf-8")
27
+ exec(compile(transform.transform(source), filename, "exec"), namespace)