Physical unit literals for Jupyter and IPython
Project description
unit-syntax
adds support for physical units to the Python language:
>>> speed = 5 meters/second
>>> (2 seconds) * speed
10 meter
Why? I often use Python as an interactive calculator for physical problems, and wished it had the type safety of explicit units along with the readability of normal notation.
Where? unit-syntax
currently supports Jupyter notebooks and the IPython interpreter; support for standalone scripts is planned.
How? A syntax transformer based on the official Python grammar turns these expression into calls to the excellent Pint units library.
Getting Started
Install the package:
$ pip install unit-syntax
To enable unit-syntax in a Jupyter/IPython session run:
import unit_syntax
unit_syntax.enable_ipython()
Tip: In Jupyter this must be run in its own cell before any units expressions are evaluated.
Usage
An interactive notebook to play around with units
Units can be applied to any "simple" expression:
- number:
1 meter
- variables:
x parsec
,y.z watts
,area[id] meters**2
- lists and tuples:
[1., 37.] newton meters
- unary operators:
-x dBm
- power:
x**2 meters
To apply units within a more complex expression, use parentheses:
one_lux = (1 lumen)/(1 meter**2)
Units can be used in other places where Python allows expressions like:
- 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:
>>> (88 miles / hour) furlongs / fortnight
236543.5269120001 furlong / fortnight
>>> (0 degC) degF
31.999999999999936 degree_Fahrenheit
Compound units (e.g. newtons/meter**2
) are supported and follow the usual precedence rules.
Units may not begin with parentheses (consider the possible
interpretations of x (meters)
). Parentheses are allowed anywhere else:
# parsed as a function call, will result in a runtime error
x (newton meters)/(second*kg)
# a-ok
x newton meters/(second*kg)
Why only allow units on simple expressions?
The rule for applying units only to "simple" expressions rather than treating it as a typical operator is to avoid unintentional error. Imagine units were instead parsed as operator with high precedence and you wrote this reasonable looking expression:
ppi = 300 pixels/inch
y = x inches * ppi
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 in general it's too error prone to allow free intermixing of operators and units. (Note: This is not a hypoethical concern, I hit this within 10 minutes of first trying out the idea)
Help!
If you're getting an unexpected result, try using unit_syntax.enable_ipython(debug_transform=True)
. This will log the transformed Python code to the console.
If you're stuck, feel free to open an issue.
How does it work?
The parser is derived from the official Python grammar using the same parser generator (pegen) as Python itself. The transformer hooks into IPython/Jupyter using custom input transformers.
Prior Art
The immediate inspriration of unit-syntax
is a language called Fortress from 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) also has first class support for units.
The Julia package Unitful.jl
A long discussion on the python-ideas mailing list about literal units in Python.
Development
To regenerate the parser:
python -m pegen grammar.txt -o unit_syntax/parser.py
Running tests:
$ poetry install --with dev
$ poetry run pytest
Future work and open questions
- Test against various ipython and python versions
- Support standalone scripts through sys.meta_path
- Check units at parse time
- Unit type hints, maybe checked with @runtime_checkable. More Pint typechecking discussion
- Expand the demo Colab notebook
- Typography of output
- make it work with numba
- understand how numpy interop works
- fail more clearly when units are used in an invalid location
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for unit_syntax-0.2.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | d99afeafbb74553497eb3d925d63a428cd6f4d52ab432ab727b54350af8fc1f9 |
|
MD5 | 9e685c5cd81fec0a4effe978a5895e0a |
|
BLAKE2b-256 | 262835ac49afd6d5aa8210b2472822ca39187aea9d37ee29343e1fa93a48de8d |