Skip to main content

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 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-syntax works in Jupyter notebooks, standalone Python scripts, and Python packages.

How does it work?

Getting Started

Install the package:

$ pip install unit-syntax

... with Jupyter/IPython

To enable unit-syntax in a Jupyter/IPython session run:

%load_ext unit_syntax

Tip: In Jupyter this must be run in its own cell before any units expressions are evaluated.

... with standalone scripts

To 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 packages

To use/distribute a package with unit-syntax, add this in your __init__.py:

from unit_syntax.import_hook import enable_units_for_package
enable_units_for_package(__name__)

This applies the transform only to sub-modules of your package.

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

In expressions mixing units and binary operators, parenthesize:

one_lux = (1 lumen)/(1 meter**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:

>>> (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)

Using unknown units produces a syntax error at import time:

>>> 1 smoot
...
SyntaxError: 'smoot' is not defined in the unit registry

How does it work?

unit-syntax transforms python-with-units into standard python that calls the excellent pint units handling library.

The parser is pegen, which is a standalone version of the same parser generator used by Python itself. The grammar is a lightly modified version the official Python grammar shipped with pegen.

Syntax transformation in IPython/Jupyter uses IPython custom input transformers.

Syntax transformation of arbitrary Python modules uses importlib's MetaPathFinder, see import-transforms and unit_syntax.loader for 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 = 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 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 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 python_units.gram -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
  • Ensure bytecode caching still works
  • Test with wider range of source files with the wildcard loader
  • Unit type hints, maybe checked with @runtime_checkable. More Pint typechecking discussion
  • Typography of output
  • pre-parse units
  • talk to pint about interop between UnitRegistries

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

unit_syntax-0.2.6.tar.gz (36.7 kB view details)

Uploaded Source

Built Distribution

unit_syntax-0.2.6-py3-none-any.whl (36.0 kB view details)

Uploaded Python 3

File details

Details for the file unit_syntax-0.2.6.tar.gz.

File metadata

  • Download URL: unit_syntax-0.2.6.tar.gz
  • Upload date:
  • Size: 36.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for unit_syntax-0.2.6.tar.gz
Algorithm Hash digest
SHA256 ae961c9a3f530803765c42e21affaadb0e03bb472944b9791d7562619183ac98
MD5 ca1fe41d7ccfc68f96776f51809f286e
BLAKE2b-256 3bf52d8bbee3c46e9f585e106a87fda17e305c74ef7c88026053eb5d69dee888

See more details on using hashes here.

File details

Details for the file unit_syntax-0.2.6-py3-none-any.whl.

File metadata

  • Download URL: unit_syntax-0.2.6-py3-none-any.whl
  • Upload date:
  • Size: 36.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for unit_syntax-0.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 bf332751c81af1b43c7131736bd3c4ea89dca45fc9cea208aa1e5604773e50b4
MD5 7aa176dd5f6c28dc04add30ded0811b9
BLAKE2b-256 2dfe580385624281d52ab5db8a22f323d91a681a2cf86c2dfe54806a2d3f59e8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page