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 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.

unit-syntax currently supports Jupyter notebooks and the IPython interpreter; maybe someday support for standalone scripts.

How does it work?

Getting Started

Install the package:

$ pip install unit-syntax

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

import unit_syntax.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

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 invalid units produces a syntax error at import time:

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

How does it work?

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

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

An IPython [custom input transformer](https://ipython.readthedocs.io/en/stable/config/inputtransforms.html) parses the new syntax using a modified version of the official Python grammar, and translates it into calls to the excellent [Pint](https://pint.readthedocs.io/) units library.

## 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:

```python
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 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
  • Support standalone scripts through sys.meta_path
  • 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

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.1.tar.gz (36.0 kB view details)

Uploaded Source

Built Distribution

unit_syntax-0.2.1-py3-none-any.whl (34.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: unit_syntax-0.2.1.tar.gz
  • Upload date:
  • Size: 36.0 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.1.tar.gz
Algorithm Hash digest
SHA256 3e435e4308c33e5ee413d2c8513be9c66f5ed72b232561657ea2a474e68e139d
MD5 e221d19bfaadf09ea88860d0c796afc4
BLAKE2b-256 fdea78b31e2d0614a41760934c8ca217d2abe0580c3a5869acf87cf754c8a666

See more details on using hashes here.

File details

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

File metadata

  • Download URL: unit_syntax-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 34.9 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fe26382fe0571f28ee57baa8cae8265e4fdd8bfa0dd925e37a2e0fc3739cd4a7
MD5 adaceac9e91ba7b2154631bd4730a068
BLAKE2b-256 8f5ee394b50fedd50b6ffd9bbbec3c6b125321b6c4d03979bb12da05be9a1bfe

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