Usage#
Installation#
To use Unitstools, install it using pip:
pip install unitstools
Basic Usage#
The fundamental objective of this package is to work in tandem with static type checkers in order to raise warnings in situations that may be violating the expected units involved in operations. Consider the example below as an illustration.
from unitstools import Unit
class Meters(Unit):
...
def double_height(x: Meters) -> Meters:
return 2 * x
def square_height(x: Meters) -> Meters:
# Checkers will raise a warning for this code, since squaring does not
# preserve units.
return x ** 2
def sum_height(x: Meters) -> Meters:
# Checkers will raise a warning for this code, since 10 has no units.
return x + 10
Note
The classes Unit and IntUnit are never actually instantiated
(for more details, see Technical Details), and neither are user-defined
derived classes that establish the units (see the example further below).
Their purpose is entirely for type hinting via their pseudo-instantiation syntax
and/or through strip_unit() and embed_unit().
Keep in mind that the following are equivalent:
class Kilometers(Unit):
...
x = Kilometers(100)
class Kilometers(Unit):
...
x = embed_unit(100, Kilometers)
The user should decide which syntax to use based primarily on preference.
Examples#
The following code does not raise any warnings.
from unitstools import Unit
class Seconds(Unit):
...
def add_one_second(second: Seconds) -> Seconds:
return second + Seconds(1)
def main():
starting_time = Seconds(30)
starting_time_plus_one = add_one_second(starting_time)
On the other hand, Pylance raises a warning for the following code in the
add_one_second function(), saying that you can’t sum a
Seconds with a Literal[1].
from unitstools import Unit
class Seconds(Unit):
...
def add_one_second(second: Seconds) -> Seconds:
return second + 1
def main():
starting_time = Seconds(30)
starting_time_plus_one = add_one_second(starting_time)
An alternative way to write the correct code is as follows:
from unitstools import Unit
class Seconds(Unit):
...
def add_one_second(second: Seconds) -> Seconds:
return second + embed_unit(1, Seconds)
def main():
starting_time = embed_unit(30, Seconds)
starting_time_plus_one = add_one_second(starting_time)
Note
This package makes no runtime conversion. In a sense, it works similarly to the
behavior of functions such as typing.cast(), in that at runtime “nothing”
happens (yes, there is a function call, but it just returns the value and moves
on). This helps keep performance of number operations instead by still using
builtin types, while also allowing the aforementioned advantages.