Package: unit-syntax
unit-syntaxadds support for physical units to the Python language:>>>speed=5meters/second>>>(2seconds)*speed10meterWhy? 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.unit-syntaxworks in Jupyter notebooks, standalone Python scripts, and Python packages.How does it work?Getting StartedInstall the package:$pipinstallunit-syntax... with Jupyter/IPythonTo enable unit-syntax in a Jupyter/IPython session run:%load_extunit_syntaxTip: In Jupyter this must be run in its own cell before any units expressions are evaluated.... with standalone scriptsTo run a standalone script with units:$ python -m unit_syntax <path_to_script.py>Note that this installs a custom import hook that affects all imports performed by the script.... with Python packagesTo use/distribute a package with unit-syntax, add this in your__init__.py:fromunit_syntax.import_hookimportenable_units_for_packageenable_units_for_package(__name__)This applies the transform only to sub-modules of your package.UsageAn interactive notebook to play around with unitsUnits can be applied to any "simple" expression:number:1 metervariables:x parsec,y.z watts,area[id] meters**2lists and tuples:[1., 37.] newton metersunary operators:-x dBmpower:x**2 metersIn expressions mixing units and binary operators, parenthesize:one_lux=(1lumen)/(1meter**2)Units can be used in any place where Python allows expressions, e.g:function arguments:area_of_circle(radius=1 meter)list comprehensions:[x meters for x in range(10)]Quantities can be converted to another measurement system:>>>(88miles/hour)furlongs/fortnight236543.5269120001furlong/fortnight>>>(0degC)degF31.999999999999936degree_FahrenheitCompound units (e.g.newtons/meter**2) are supported and follow the usual precedence rules.Unitsmay notbegin with parentheses (consider the possible
interpretations ofx (meters)). Parentheses are allowed anywhere else:# parsed as a function call, will result in a runtime errorx(newtonmeters)/(second*kg)# a-okxnewtonmeters/(second*kg)Using unknown units produces a syntax error at import time:>>>1smoot...SyntaxError:'smoot'isnotdefinedintheunitregistryHow does it work?unit-syntaxtransforms python-with-units into standard python that calls the excellentpintunits handling library.The parser ispegen, which is a standalone version of the same parser generator used by Python itself. The grammar is alightly modifiedversion the official Python grammar shipped with pegen.Syntax transformation in IPython/Jupyter usesIPython custom input transformers.Syntax transformation of arbitrary Python modules usesimportlib'sMetaPathFinder, seeimport-transformsandunit_syntax.import_hookfor details.Why only allow units on simple expressions?Imagine units were instead parsed as operator with high precedence and you wrote this reasonable looking expression:ppi=300pixels/inchy=xinches*ppiinches * ppiwould 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.Prior ArtThe immediate inspriration ofunit-syntaxis a language calledFortressfrom Sun Microsystems. Fortress was intended as a modern Fortran, and had first-class support for units in both the syntax and type system.F# (an OCaml derivative from Microsoft) alsohas first class support for units.The Julia packageUnitful.jlAlong discussion on the python-ideas mailing listabout literal units in Python.DevelopmentTo regenerate the parser:python -m pegen python_units.gram -o unit_syntax/parser.pyRunning tests:$ poetry install --with dev
$ poetry run pytestFuture work and open questionsTest against various ipython and python versionsEnsure bytecode caching still worksTest with wider range of source files with the wildcard loaderUnit type hints, maybe checked with@runtime_checkable. More Pint typecheckingdiscussionTypography of outputpre-parse unitstalk to pint about interop between UnitRegistriesFix reported location of SyntaxError when paren missing