summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml10
-rw-r--r--AUTHORS1
-rw-r--r--README5
-rw-r--r--README.rst5
-rw-r--r--docs/contexts.rst20
-rw-r--r--docs/defining.rst47
-rw-r--r--docs/developers_reference.rst123
-rw-r--r--docs/faq.rst5
-rw-r--r--docs/getting.rst3
-rw-r--r--docs/index.rst3
-rw-r--r--docs/nonmult.rst6
-rw-r--r--docs/numpy.rst10
-rw-r--r--docs/plotting.rst2
-rw-r--r--docs/serialization.rst21
-rw-r--r--docs/tutorial.rst34
-rw-r--r--docs/wrapping.rst7
-rw-r--r--pint/__init__.py95
-rw-r--r--pint/compat/tokenize.py5
-rw-r--r--pint/constants_en.txt98
-rw-r--r--pint/context.py2
-rw-r--r--pint/converters.py2
-rw-r--r--pint/default_en.txt999
-rw-r--r--pint/definitions.py32
-rw-r--r--pint/errors.py3
-rw-r--r--pint/formatting.py5
-rw-r--r--pint/matplotlib.py8
-rw-r--r--pint/measurement.py46
-rw-r--r--pint/quantity.py113
-rw-r--r--pint/registry.py83
-rw-r--r--pint/registry_helpers.py4
-rw-r--r--pint/systems.py26
-rw-r--r--pint/testsuite/__init__.py10
-rw-r--r--pint/testsuite/parameterized.py7
-rw-r--r--pint/testsuite/test_application_registry.py233
-rw-r--r--pint/testsuite/test_contexts.py44
-rw-r--r--pint/testsuite/test_definitions.py18
-rw-r--r--pint/testsuite/test_errors.py3
-rw-r--r--pint/testsuite/test_infer_base_unit.py8
-rw-r--r--pint/testsuite/test_issues.py498
-rw-r--r--pint/testsuite/test_numpy.py12
-rw-r--r--pint/testsuite/test_quantity.py21
-rw-r--r--pint/testsuite/test_unit.py29
-rw-r--r--pint/testsuite/test_util.py26
-rw-r--r--pint/unit.py37
-rw-r--r--pint/util.py153
-rw-r--r--pint/xtranslated.txt10
46 files changed, 2046 insertions, 886 deletions
diff --git a/.travis.yml b/.travis.yml
index a759926..8442774 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,7 +18,7 @@ env:
- UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=0 PANDAS=0
# Test with the latest numpy version
- UNCERTAINTIES="N" PYTHON="2.7" NUMPY_VERSION=1.14 PANDAS=0
- - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.14 PANDAS=0
+ #- UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.14 PANDAS=0
- UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.14 PANDAS=0
- UNCERTAINTIES="Y" PYTHON="3.5" NUMPY_VERSION=1.14 PANDAS=0
- UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.14 PANDAS=0
@@ -35,12 +35,16 @@ before_install:
- hash -r
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
+ - if [[ "$PYTHON" != "2.7" ]]; then
+ conda config --set restore_free_channel yes;
+ fi
# Useful for debugging any issues with conda
- conda info -a
# The next couple lines fix a crash with multiprocessing on Travis and are not specific to using Miniconda
- - sudo rm -rf /dev/shm
- - sudo ln -s /run/shm /dev/shm
+ # But broke travis 2019-08
+ # - sudo rm -rf /dev/shm
+ # - sudo ln -s /run/shm /dev/shm
- export ENV_NAME=travis
diff --git a/AUTHORS b/AUTHORS
index a89af23..6bb6358 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -20,6 +20,7 @@ Other contributors, listed alphabetically, are:
* Felix Hummel <felix@felixhummel.de>
* Francisco Couzo <franciscouzo@gmail.com>
* Giel van Schijndel <me@mortis.eu>
+* Ignacio Fdez. Galván <jellby@yahoo.com>
* James Rowe <jnrowe@gmail.com>
* Jim Turner <jturner314@gmail.com>
* Joel B. Mohler <joel@kiwistrawberry.us>
diff --git a/README b/README
index 9a0812d..3e3d3dc 100644
--- a/README
+++ b/README
@@ -57,6 +57,11 @@ Documentation
Full documentation is available at http://pint.readthedocs.org/
+GUI Website
+-----------
+
+This [website](www.dimensionalanalysis.org) wraps Pint's "dimensional analysis" methods to provide a GUI.
+
Design principles
-----------------
diff --git a/README.rst b/README.rst
index 1f7dfc0..c696e19 100644
--- a/README.rst
+++ b/README.rst
@@ -86,6 +86,11 @@ Documentation
Full documentation is available at http://pint.readthedocs.org/
+GUI Website
+-----------
+
+This [website](www.dimensionalanalysis.org) wraps Pint's "dimensional analysis" methods to provide a GUI.
+
Design principles
-----------------
diff --git a/docs/contexts.rst b/docs/contexts.rst
index 66ec5e6..4221fe7 100644
--- a/docs/contexts.rst
+++ b/docs/contexts.rst
@@ -7,7 +7,7 @@ If you work frequently on certain topics, you will probably find the need to
convert between dimensions based on some pre-established (physical)
relationships. For example, in spectroscopy you need to transform from
wavelength to frequency. These are incompatible units and therefore Pint will
-raise an error if your do this directly:
+raise an error if you do this directly:
.. doctest::
@@ -17,7 +17,7 @@ raise an error if your do this directly:
>>> q.to('Hz')
Traceback (most recent call last):
...
- pint.errors.DimensionalityError: Cannot convert from 'nanometer' ([length]) to 'hertz' (1 / [time])
+ DimensionalityError: Cannot convert from 'nanometer' ([length]) to 'hertz' (1 / [time])
You probably want to use the relation `frequency = speed_of_light / wavelength`:
@@ -138,7 +138,8 @@ context and the parameters that you wish to set.
398.496240602
-This decorator can be combined with **wraps** or **check** decorators described in `wrapping`_
+This decorator can be combined with **wraps** or **check** decorators described in
+:doc:`wrapping`.
Defining contexts in a file
@@ -188,7 +189,16 @@ functions. For example:
>>> ureg = pint.UnitRegistry()
>>> c = pint.Context('ab')
>>> c.add_transformation('[length]', '[time]',
- ... lambda ureg, x: ureg.speed_of_light / x)
+ ... lambda ureg, x: x / ureg.speed_of_light)
>>> c.add_transformation('[time]', '[length]',
- ... lambda ureg, x: ureg.speed_of_light * x)
+ ... lambda ureg, x: x * ureg.speed_of_light)
>>> ureg.add_context(c)
+ >>> ureg("1 s").to("km", "ab")
+ 299792.458 kilometer
+
+It is also possible to create anonymous contexts without invoking add_context:
+
+ >>> c = pint.Context()
+ ...
+ >>> ureg("1 s").to("km", c)
+ 299792.458 kilometer
diff --git a/docs/defining.rst b/docs/defining.rst
index 0a02147..7aba5bb 100644
--- a/docs/defining.rst
+++ b/docs/defining.rst
@@ -16,9 +16,16 @@ other units. For example this is how the minute and the hour are defined in
minute = 60 * second = min
It is quite straightforward, isn't it? We are saying that `minute` is
-`60 seconds` and is also known as `min`. The first word is always the canonical
-name. Next comes the definition (based on other units). Finally, a list of
-aliases, separated by equal signs.
+`60 seconds` and is also known as `min`.
+
+1. The first word is always the canonical name.
+2. Next comes the definition (based on other units).
+3. Next, optionally, there is the unit symbol.
+4. Finally, again optionally, a list of aliases, separated by equal signs.
+ If one wants to specify aliases but not a symbol, the symbol should be
+ conventionally set to ``_``; e.g.::
+
+ millennium = 1e3 * year = _ = millennia
The order in which units are defined does not matter, Pint will resolve the
dependencies to define them in the right order. What is important is that if
@@ -68,12 +75,28 @@ unit, including non-metric ones (e.g. kiloinch is valid for Pint). This
simplifies definitions files enormously without introducing major problems.
Pint, like Python, believes that we are all consenting adults.
+Derived dimensions are defined as follows::
+
+ [density] = [mass] / [volume]
+
+Note that primary dimensions don't need to be declared; they can be
+defined for the first time as part of a unit definition.
+
+Finally, one may add aliases to an already existing unit definition::
+
+ @alias meter = metro = metr
+
+This is particularly useful when one wants to enrich definitions from defaults_en.txt
+with new aliases from a custom file. It can also be used for translations (like in the
+example above) as long as one is happy to have the localized units automatically
+converted to English when they are parsed.
+
Programmatically
----------------
-You can easily add units to the registry programmatically. Let's add a dog_year
-(sometimes written as dy) equivalent to 52 (human) days:
+You can easily add units, dimensions, or aliases to the registry programmatically.
+Let's add a dog_year (sometimes written as dy) equivalent to 52 (human) days:
.. doctest::
@@ -93,6 +116,8 @@ You can easily add units to the registry programmatically. Let's add a dog_year
Note that we have used the name `dog_years` even though we have not defined the
plural form as an alias. Pint takes care of that, so you don't have to.
+Plural forms that aren't simply built by adding a 's' suffix to the singular form
+should be explicitly stated as aliases (see for example ``millennia`` above).
You can also add prefixes programmatically:
@@ -102,4 +127,14 @@ You can also add prefixes programmatically:
where the number indicates the multiplication factor.
-.. warning:: Units and prefixes added programmatically are forgotten when the program ends.
+Same for aliases and derived dimensions:
+
+.. doctest::
+
+ >>> ureg.define('@alias meter = metro = metr')
+ >>> ureg.define('[hypervolume] = [length ** 4]')
+
+
+.. warning::
+ Units, prefixes, aliases and dimensions added programmatically are forgotten when the
+ program ends.
diff --git a/docs/developers_reference.rst b/docs/developers_reference.rst
new file mode 100644
index 0000000..0ba6679
--- /dev/null
+++ b/docs/developers_reference.rst
@@ -0,0 +1,123 @@
+===================
+Developer reference
+===================
+
+Pint
+====
+
+.. automodule:: pint
+ :members:
+
+.. automodule:: pint.babel_names
+ :members:
+
+.. automodule:: pint.context
+ :members:
+
+.. automodule:: pint.converters
+ :members:
+
+.. automodule:: pint.definitions
+ :members:
+
+.. automodule:: pint.errors
+ :members:
+
+.. automodule:: pint.formatting
+ :members:
+
+.. automodule:: pint.matplotlib
+ :members:
+
+.. automodule:: pint.measurement
+ :members:
+
+.. automodule:: pint.pint_eval
+ :members:
+
+.. automodule:: pint.quantity
+ :members:
+
+.. automodule:: pint.registry
+ :members:
+
+.. automodule:: pint.registry_helpers
+ :members:
+
+.. automodule:: pint.systems
+ :members:
+
+.. automodule:: pint.unit
+ :members:
+
+.. automodule:: pint.util
+ :members:
+
+.. automodule:: pint.compat.chainmap
+ :members:
+
+.. automodule:: pint.compat.lrucache
+ :members:
+
+.. automodule:: pint.compat.meta
+ :members:
+
+.. automodule:: pint.compat.tokenize
+ :members:
+
+.. automodule:: pint.testsuite.helpers
+ :members:
+
+.. automodule:: pint.testsuite.parameterized
+ :members:
+
+.. automodule:: pint.testsuite.test_babel
+ :members:
+
+.. automodule:: pint.testsuite.test_contexts
+ :members:
+
+.. automodule:: pint.testsuite.test_converters
+ :members:
+
+.. automodule:: pint.testsuite.test_definitions
+ :members:
+
+.. automodule:: pint.testsuite.test_errors
+ :members:
+
+.. automodule:: pint.testsuite.test_formatter
+ :members:
+
+.. automodule:: pint.testsuite.test_infer_base_unit
+ :members:
+
+.. automodule:: pint.testsuite.test_issues
+ :members:
+
+.. automodule:: pint.testsuite.test_measurement
+ :members:
+
+.. automodule:: pint.testsuite.test_numpy
+ :members:
+
+.. automodule:: pint.testsuite.test_pint_eval
+ :members:
+
+.. automodule:: pint.testsuite.test_pitheorem
+ :members:
+
+.. automodule:: pint.testsuite.test_quantity
+ :members:
+
+.. automodule:: pint.testsuite.test_systems
+ :members:
+
+.. automodule:: pint.testsuite.test_umath
+ :members:
+
+.. automodule:: pint.testsuite.test_unit
+ :members:
+
+.. automodule:: pint.testsuite.test_util
+ :members: \ No newline at end of file
diff --git a/docs/faq.rst b/docs/faq.rst
index e92a630..d160518 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -31,13 +31,12 @@ You mention other similar Python libraries. Can you point me to those?
`SymPy <http://docs.sympy.org/dev/modules/physics/units.html>`_
-`Units <https://bitbucket.org/adonohue/units/>`_
-
`cf units <https://github.com/SciTools/cf_units>`_
`astropy units <https://github.com/astropy/astropy>`_
`yt <https://github.com/yt-project/yt>`_
+`measurement <https://github.com/coddingtonbear/python-measurement>`_
-If your are aware of another one, please contribute a patch to the docs.
+If you're aware of another one, please contribute a patch to the docs.
diff --git a/docs/getting.rst b/docs/getting.rst
index 75b4bae..939b4b4 100644
--- a/docs/getting.rst
+++ b/docs/getting.rst
@@ -19,7 +19,8 @@ That's all! You can check that Pint is correctly installed by starting up python
.. note:: If you have an old system installation of Python and you don't want to
mess with it, you can try `Anaconda CE`_. It is a free Python distribution by
Continuum Analytics that includes many scientific packages. To install pint
- from the conda-forge channel instead of through pip use:
+ from the conda-forge channel instead of through pip use::
+
$ conda install -c conda-forge pint
You can check the installation with the following command:
diff --git a/docs/index.rst b/docs/index.rst
index 4f796b0..b6eac5d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -138,6 +138,7 @@ More information
.. toctree::
:maxdepth: 1
+ developers_reference
contributing
faq
@@ -150,7 +151,7 @@ One last thing
The MCO MIB has determined that the root cause for the loss of the MCO spacecraft was the failure to use metric units in the coding of a ground software file, “Small Forces,” used in trajectory models. Specifically, thruster performance data in English units instead of metric units was used in the software application code titled SM_FORCES (small forces). The output from the SM_FORCES application code as required by a MSOP Project Software Interface Specification (SIS) was to be in metric units of Newtonseconds (N-s). Instead, the data was reported in English units of pound-seconds (lbf-s). The Angular Momentum Desaturation (AMD) file contained the output data from the SM_FORCES software. The SIS, which was not followed, defines both the format and units of the AMD file generated by ground-based computers. Subsequent processing of the data from AMD file by the navigation software algorithm therefore, underestimated the effect on the spacecraft trajectory by a factor of 4.45, which is the required conversion factor from force in pounds to Newtons. An erroneous trajectory was computed using this incorrect data.
`Mars Climate Orbiter Mishap Investigation Phase I Report`
- `PDF <ftp://ftp.hq.nasa.gov/pub/pao/reports/1999/MCO_report.pdf>`_
+ `PDF <https://llis.nasa.gov/llis_lib/pdf/1009464main1_0641-mr.pdf>`_
diff --git a/docs/nonmult.rst b/docs/nonmult.rst
index 6d83da9..4d792c1 100644
--- a/docs/nonmult.rst
+++ b/docs/nonmult.rst
@@ -79,7 +79,7 @@ If you want to add a quantity with absolute unit to one with offset unit, like h
>>> Q_(10., ureg.degC) + heating_rate * Q_(30, ureg.min)
Traceback (most recent call last):
...
- pint.errors.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC, kelvin).
+ OffsetUnitCalculusError: Ambiguous operation with offset unit (degC, kelvin).
you have to avoid the ambiguity by either converting the offset unit to the
absolute unit before addition
@@ -123,7 +123,7 @@ to be explicitly created:
>>> home = 25.4 * ureg.degC
Traceback (most recent call last):
...
- pint.errors.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).
+ OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).
>>> Q_(25.4, ureg.degC)
<Quantity(25.4, 'degC')>
@@ -157,7 +157,7 @@ You can change the behaviour at any time:
>>> 1/T
Traceback (most recent call last):
...
- pint.errors.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).
+ OffsetUnitCalculusError: Ambiguous operation with offset unit (degC).
The parser knows about *delta* units and uses them when a temperature unit
is found in a multiplicative context. For example, here:
diff --git a/docs/numpy.rst b/docs/numpy.rst
index 9c351b1..e5b040b 100644
--- a/docs/numpy.rst
+++ b/docs/numpy.rst
@@ -51,7 +51,7 @@ All usual Pint methods can be used with this quantity. For example:
>>> legs1.to('joule')
Traceback (most recent call last):
...
- pint.errors.DimensionalityError: Cannot convert from 'meter' ([length]) to 'joule' ([length] ** 2 * [mass] / [time] ** 2)
+ DimensionalityError: Cannot convert from 'meter' ([length]) to 'joule' ([length] ** 2 * [mass] / [time] ** 2)
NumPy functions are supported by Pint. For example if we define:
@@ -96,7 +96,7 @@ results in an error:
>>> np.arccos(legs2)
Traceback (most recent call last):
...
- pint.errors.DimensionalityError: Cannot convert from 'centimeter' ([length]) to 'dimensionless' (dimensionless)
+ DimensionalityError: Cannot convert from 'centimeter' ([length]) to 'dimensionless' (dimensionless)
Support
@@ -109,7 +109,7 @@ The following ufuncs_ can be applied to a Quantity object:
- **Comparison functions**: greater, greater_equal, less, less_equal, not_equal, equal
- **Floating functions**: isreal,iscomplex, isfinite, isinf, isnan, signbit, copysign, nextafter, modf, ldexp, frexp, fmod, floor, ceil, trunc
-And the following `ndarrays methods`_ and functions:
+And the following `NumPy ndarray methods`_ and functions:
- sum, fill, reshape, transpose, flatten, ravel, squeeze, take, put, repeat, sort, argsort, diagonal, compress, nonzero, searchsorted, max, argmax, min, argmin, ptp, clip, round, trace, cumsum, mean, var, std, prod, cumprod, conj, conjugate, flatten
@@ -142,7 +142,7 @@ memory and CPU cycles. On top of this, all `ufuncs` are implemented in the
after the calculation and before returning the value. To our knowledge, there
is no way to signal back to NumPy that our code will take care of the
calculation. For this reason the calculation is actually done twice:
-first in the original ndarray and then in then in the one that has been
+first in the original ndarray and then in the one that has been
converted to the right units. Therefore, for numerically intensive code, you
might want to convert the objects first and then use directly the magnitude.
@@ -151,4 +151,4 @@ might want to convert the objects first and then use directly the magnitude.
.. _`NumPy ndarray`: http://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html
.. _ufuncs: http://docs.scipy.org/doc/numpy/reference/ufuncs.html
-.. _`ndarrays methods`: http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html#array-methods
+.. _`NumPy ndarray methods`: http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html#array-methods
diff --git a/docs/plotting.rst b/docs/plotting.rst
index 70f5306..a008d45 100644
--- a/docs/plotting.rst
+++ b/docs/plotting.rst
@@ -27,7 +27,7 @@ This support can also be disabled with:
>>> ureg.setup_matplotlib(False)
-This allows then plotting quantities with different units:
+This allows plotting quantities with different units:
.. plot::
:include-source: true
diff --git a/docs/serialization.rst b/docs/serialization.rst
index 3e4cf9e..fe09f9d 100644
--- a/docs/serialization.rst
+++ b/docs/serialization.rst
@@ -51,9 +51,24 @@ UnitRegistry dependent.
In certain cases, you want a binary representation of the data. Python's standard algorithm
for serialization is called Pickle_. Pint quantities implement the magic `__reduce__`
method and therefore can be *Pickled* and *Unpickled*. However, you have to bear in mind, that
-the **DEFAULT_REGISTRY** is used for unpickling and this might be different from the one
-that was used during pickling. If you want to have control over the deserialization, the
-best way is to create a tuple with the magnitude and the units:
+the **application registry** is used for unpickling and this might be different from the one
+that was used during pickling.
+
+By default, the application registry is one initialized with :file:`defaults_en.txt`; in
+other words, the same as what you get when creating a :class:`pint.UnitRegistry` without
+arguments and without adding any definitions afterwards.
+
+If your application is fine just using :file:`defaults_en.txt`, you don't need to worry
+further.
+
+If your application needs a single, global registry with custom definitions, you must
+make sure that it is registered using :func:`pint.set_application_registry` before
+unpickling anything. You may use :func:`pint.get_application_registry` to get the
+current instance of the application registry.
+
+Finally, if you need multiple custom registries, it's impossible to correctly unpickle
+:class:`pint.Quantity` or :class:`pint.Unit` objects.The best way is to create a tuple
+with the magnitude and the units:
.. doctest::
diff --git a/docs/tutorial.rst b/docs/tutorial.rst
index b11e608..2894c0b 100644
--- a/docs/tutorial.rst
+++ b/docs/tutorial.rst
@@ -89,7 +89,7 @@ If you ask Pint to perform an invalid conversion:
>>> speed.to(ureg.joule)
Traceback (most recent call last):
...
- pint.errors.DimensionalityError: Cannot convert from 'inch / minute' ([length] / [time]) to 'joule' ([length] ** 2 * [mass] / [time] ** 2)
+ DimensionalityError: Cannot convert from 'inch / minute' ([length] / [time]) to 'joule' ([length] ** 2 * [mass] / [time] ** 2)
Sometimes, the magnitude of the quantity will be very large or very small.
The method 'to_compact' can adjust the units to make the quantity more
@@ -170,7 +170,7 @@ If you try to use a unit which is not in the registry:
>>> speed = 23 * ureg.snail_speed
Traceback (most recent call last):
...
- pint.errors.UndefinedUnitError: 'snail_speed' is not defined in the unit registry
+ UndefinedUnitError: 'snail_speed' is not defined in the unit registry
You can add your own units to the registry or build your own list. More info on
that :ref:`defining`
@@ -303,6 +303,22 @@ Pint supports float formatting for numpy arrays as well:
>>> # scientific form formatting with unit pretty printing
>>> print('The array is {:+.2E~P}'.format(accel))
The array is [-1.10E+00 +1.00E-06 +1.25E+00 +1.30E+00] m/s²
+
+Pint also supports 'f-strings'_ from python>=3.6 :
+
+.. doctest::
+
+ >>> accel = 1.3 * ureg['meter/second**2']
+ >>> print(f'The str is {accel}')
+ The str is 1.3 meter / second ** 2
+ >>> print(f'The str is {accel:.3e}')
+ The str is 1.300e+00 meter / second ** 2
+ >>> print(f'The str is {accel:~}')
+ The str is 1.3 m / s ** 2
+ >>> print(f'The str is {accel:~.3e}')
+ The str is 1.300e+00 m / s ** 2
+ >>> print(f'The str is {accel:~H}')
+ The str is 1.3 m/s²
But Pint also extends the standard formatting capabilities for unicode and
LaTeX representations:
@@ -336,6 +352,18 @@ If you want to use abbreviated unit names, prefix the specification with `~`:
The same is true for latex (`L`) and HTML (`H`) specs.
+.. note::
+ The abbreviated unit is drawn from the unit registry where the 3rd item in the
+ equivalence chain (ie 1 = 2 = **3**) will be returned when the prefix '~' is
+ used. The 1st item in the chain is the canonical name of the unit.
+
+The formatting specs (ie 'L', 'H', 'P') can be used with Python string 'formatting
+syntax'_ for custom float representations. For example, scientific notation:
+
+..doctest::
+ >>> 'Scientific notation: {:.3e~L}'.format(accel)
+ 'Scientific notation: 1.300\\times 10^{0}\\ \\frac{\\mathrm{m}}{\\mathrm{s}^{2}}'
+
Pint also supports the LaTeX siunitx package:
.. doctest::
@@ -409,3 +437,5 @@ also define the registry as the application registry::
.. _eval: http://docs.python.org/3/library/functions.html#eval
.. _`serious security problems`: http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
.. _`Babel`: http://babel.pocoo.org/
+.. _'formatting syntax': https://docs.python.org/3/library/string.html#format-specification-mini-language
+.. _'f-strings': https://www.python.org/dev/peps/pep-0498/ \ No newline at end of file
diff --git a/docs/wrapping.rst b/docs/wrapping.rst
index 31eb10b..7b56dcc 100644
--- a/docs/wrapping.rst
+++ b/docs/wrapping.rst
@@ -144,7 +144,7 @@ the extra outputs. For example, given the NREL SOLPOS calculator that outputs
solar zenith, azimuth and air mass, the following wrapper assumes no units for
airmass::
- @UREG.wraps(('deg', 'deg'), ('deg', 'deg', 'millibar', 'degC'))
+ @ureg.wraps(('deg', 'deg'), ('deg', 'deg', 'millibar', 'degC'))
def solar_position(lat, lon, press, tamb, timestamp):
return zenith, azimuth, airmass
@@ -183,8 +183,9 @@ arguments:
Specifying relations between arguments
--------------------------------------
-In certain cases the actual units but just their relation. This is done using string
-starting with the equal sign `=`:
+In certain cases, you may not be concerned with the actual units and only care about the unit relations among arguments.
+
+This is done using a string starting with the equal sign `=`:
.. doctest::
diff --git a/pint/__init__.py b/pint/__init__.py
index 08ac132..0189f08 100644
--- a/pint/__init__.py
+++ b/pint/__init__.py
@@ -15,13 +15,21 @@ from __future__ import with_statement
import pkg_resources
+
+from .context import Context
+from .errors import (
+ DimensionalityError,
+ OffsetUnitCalculusError,
+ UndefinedUnitError,
+ UnitStrippedWarning
+)
from .formatting import formatter
-from .registry import (UnitRegistry, LazyRegistry)
-from .errors import (DimensionalityError, OffsetUnitCalculusError,
- UndefinedUnitError, UnitStrippedWarning)
+from .measurement import Measurement
+from .quantity import Quantity
+from .registry import UnitRegistry, LazyRegistry
+from .unit import Unit
from .util import pi_theorem, logger
-from .context import Context
import sys
try:
@@ -46,53 +54,49 @@ _DEFAULT_REGISTRY = LazyRegistry()
_APP_REGISTRY = _DEFAULT_REGISTRY
-def _build_quantity(value, units):
- """Build Quantity using the Application registry.
- Used only for unpickling operations.
- """
- from .unit import UnitsContainer
-
- global _APP_REGISTRY
-
- # Prefixed units are defined within the registry
- # on parsing (which does not happen here).
- # We need to make sure that this happens before using.
- if isinstance(units, UnitsContainer):
- for name in units.keys():
- _APP_REGISTRY.parse_units(name)
-
- return _APP_REGISTRY.Quantity(value, units)
-
+def _unpickle(cls, *args):
+ """Rebuild object upon unpickling.
+ All units must exist in the application registry.
-def _build_unit(units):
- """Build Unit using the Application registry.
- Used only for unpickling operations.
+ :param cls:
+ Quantity, Magnitude, or Unit
"""
from .unit import UnitsContainer
- global _APP_REGISTRY
-
- # Prefixed units are defined within the registry
- # on parsing (which does not happen here).
- # We need to make sure that this happens before using.
- if isinstance(units, UnitsContainer):
- for name in units.keys():
- _APP_REGISTRY.parse_units(name)
+ for arg in args:
+ # Prefixed units are defined within the registry
+ # on parsing (which does not happen here).
+ # We need to make sure that this happens before using.
+ if isinstance(arg, UnitsContainer):
+ for name in arg:
+ _APP_REGISTRY.parse_units(name)
- return _APP_REGISTRY.Unit(units)
+ return cls(*args)
def set_application_registry(registry):
- """Set the application registry which is used for unpickling operations.
+ """Set the application registry, which is used for unpickling operations
+ and when invoking pint.Quantity or pint.Unit directly.
:param registry: a UnitRegistry instance.
"""
- assert isinstance(registry, UnitRegistry)
+ if not isinstance(registry, (LazyRegistry, UnitRegistry)):
+ raise TypeError("Expected UnitRegistry; got %s" % type(registry))
global _APP_REGISTRY
logger.debug('Changing app registry from %r to %r.', _APP_REGISTRY, registry)
_APP_REGISTRY = registry
+def get_application_registry():
+ """Return the application registry. If :func:`set_application_registry` was never
+ invoked, return a registry built using :file:`defaults_en.txt` embedded in the pint
+ package.
+
+ :param registry: a UnitRegistry instance.
+ """
+ return _APP_REGISTRY
+
+
def test():
"""Run all tests.
@@ -100,3 +104,24 @@ def test():
"""
from .testsuite import run
return run()
+
+
+# Enumerate all user-facing objects
+# Hint to intersphinx that, when building objects.inv, these objects must be registered
+# under the top-level module and not in their original submodules
+__all__ = (
+ 'Context',
+ 'Measurement',
+ 'Quantity',
+ 'Unit',
+ 'UnitRegistry',
+
+ 'DimensionalityError',
+ 'OffsetUnitCalculusError',
+ 'UndefinedUnitError',
+ 'UnitStrippedWarning',
+
+ 'get_application_registry',
+ 'set_application_registry',
+ '__version__',
+)
diff --git a/pint/compat/tokenize.py b/pint/compat/tokenize.py
index 3166224..8d28b4f 100644
--- a/pint/compat/tokenize.py
+++ b/pint/compat/tokenize.py
@@ -1,7 +1,7 @@
"""Tokenization help for Python programs.
tokenize(readline) is a generator that breaks a stream of bytes into
-Python tokens. It decodes the bytes according to PEP-0263 for
+Python tokens. It decodes the bytes according to PEP-0263 for
determining source file encoding.
It accepts a readline-like method which is called repeatedly to get the
@@ -462,7 +462,8 @@ def tokenize(readline):
must be a callable object which provides the same interface as the
readline() method of built-in file objects. Each call to the function
should return one line of input as bytes. Alternately, readline
- can be a callable function terminating with StopIteration:
+ can be a callable function terminating with StopIteration::
+
readline = open(myfile, 'rb').__next__ # Example of alternate readline
The generator produces 5-tuples with these members: the token type; the
diff --git a/pint/constants_en.txt b/pint/constants_en.txt
index 2b67545..a7c22fc 100644
--- a/pint/constants_en.txt
+++ b/pint/constants_en.txt
@@ -1,51 +1,73 @@
# Default Pint constants definition file
# Based on the International System of Units
# Language: english
-# Source: http://physics.nist.gov/cuu/Constants/Table/allascii.txt
-# :copyright: 2013 by Pint Authors, see AUTHORS for more details.
+# Source: https://physics.nist.gov/cuu/Constants/
+# https://physics.nist.gov/PhysRefData/XrayTrans/Html/search.html
+# :copyright: 2013,2019 by Pint Authors, see AUTHORS for more details.
-speed_of_light = 299792458 * meter / second = c
-standard_gravity = 9.806650 * meter / second ** 2 = g_0 = g_n = gravity
-vacuum_permeability = 4 * pi * 1e-7 * newton / ampere ** 2 = mu_0 = magnetic_constant
-vacuum_permittivity = 1 / (mu_0 * c **2 ) = epsilon_0 = electric_constant
-Z_0 = mu_0 * c = impedance_of_free_space = characteristic_impedance_of_vacuum
+#### MATHEMATICAL CONSTANTS ####
+# As computed by Maxima with fpprec:50
-# 0.000 000 29 e-34
-planck_constant = 6.62606957e-34 J s = h
-hbar = planck_constant / (2 * pi) = ħ
+#pi = 3.1415926535897932384626433832795028841971693993751 = π # pi
+tansec = 4.8481368111333441675396429478852851658848753880815e-6 # tangent of 1 arc-second ~ arc_second/radian
+ln10 = 2.3025850929940456840179914546843642076011014886288 # natural logarithm of 10
+wien_x = 4.9651142317442763036987591313228939440555849867973 # solution to (x-5)*exp(x)+5 = 0 => x = W(5/exp(5))+5
+wien_u = 2.8214393721220788934031913302944851953458817440731 # solution to (u-3)*exp(u)+3 = 0 => u = W(3/exp(3))+3
-# 0.000 80 e-11
-newtonian_constant_of_gravitation = 6.67384e-11 m^3 kg^-1 s^-2
+#### DEFINED EXACT CONSTANTS ####
-# 0.000 000 035 e-19
-# elementary_charge = 1.602176565e-19 C = e
+speed_of_light = 299792458 m/s = c = c_0 # since 1983
+planck_constant = 6.62607015e-34 J s = h # since May 2019
+elementary_charge = 1.602176634e-19 C = e # since May 2019
+avogadro_number = 6.02214076e23 # since May 2019
+boltzmann_constant = 1.380649e-23 J K^-1 = k = k_B # since May 2019
+standard_gravity = 9.80665 m/s^2 = g_0 = g0 = g_n = gravity # since 1901
+standard_atmosphere = 1.01325e5 Pa = atm = atmosphere # since 1954
+conventional_josephson_constant = 4.835979e14 Hz / V = K_J90 # since Jan 1990
+conventional_von_klitzing_constant = 2.5812807e4 ohm = R_K90 # since Jan 1990
-# 0.000 0075
-molar_gas_constant = 8.3144621 J mol^-1 K^-1 = R
+#### DERIVED EXACT CONSTANTS ####
+# Floating-point conversion may introduce inaccuracies
-# 0.000 000 0024 e-3
-fine_structure_constant = 7.2973525698e-3
+zeta = c / (cm/s) = ζ
+dirac_constant = h / (2 * π) = ħ = hbar = atomic_unit_of_action = a_u_action
+avogadro_constant = avogadro_number * mol^-1 = N_A
+molar_gas_constant = k * N_A = R
+faraday_constant = e * N_A
+conductance_quantum = 2 * e ** 2 / h = G_0
+magnetic_flux_quantum = h / (2 * e) = Φ_0 = Phi_0
+josephson_constant = 2 * e / h = K_J
+von_klitzing_constant = h / e ** 2 = R_K
+stefan_boltzmann_constant = 2 / 15 * π ** 5 * k ** 4 / (h ** 3 * c ** 2) = σ = sigma
+first_radiation_constant = 2 * π * h * c ** 2 = c_1
+second_radiation_constant = h * c / k = c_2
+wien_wavelength_displacement_law_constant = h * c / (k * wien_x)
+wien_frequency_displacement_law_constant = wien_u * k / h
-# 0.000 000 27 e23
-avogadro_number = 6.02214129e23 mol^-1 =N_A
+#### MEASURED CONSTANTS ####
+# Recommended CODATA-2018 values
+# To some extent, what is measured and what is derived is a bit arbitrary.
+# The choice of measured constants is based on convenience and on available uncertainty.
+# The uncertainty in the last significant digits is given in parentheses as a comment.
-# 0.000 0013 e-23
-boltzmann_constant = 1.3806488e-23 J K^-1 = k
+newtonian_constant_of_gravitation = 6.67430e-11 m^3/(kg s^2) = _ = gravitational_constant # (15)
+rydberg_constant = 1.0973731568160e7 * m^-1 = R_∞ = R_inf # (21)
+electron_g_factor = -2.00231930436256 = g_e # (35)
+atomic_mass_constant = 1.66053906660e-27 kg = m_u # (50)
+electron_mass = 9.1093837015e-31 kg = m_e = atomic_unit_of_mass = a_u_mass # (28)
+proton_mass = 1.67262192369e-27 kg = m_p # (51)
+neutron_mass = 1.67492749804e-27 kg = m_n # (95)
+lattice_spacing_of_Si = 1.920155716e-10 m = d_220 # (32)
+K_alpha_Cu_d_220 = 0.80232719 # (22)
+K_alpha_Mo_d_220 = 0.36940604 # (19)
+K_alpha_W_d_220 = 0.108852175 # (98)
-# 0.000 021 e-8
-stefan_boltzmann_constant = 5.670373e-8 W m^-2 K^-4 = σ
+#### DERIVED CONSTANTS ####
-# 0.000 0053 e10
-wien_frequency_displacement_law_constant = 5.8789254e10 Hz K^-1
-
-# 0.000 055
-rydberg_constant = 10973731.568539 m^-1
-
-# 0.000 000 40 e-31
-electron_mass = 9.10938291e-31 kg = m_e
-
-# 0.000 000 074 e-27
-neutron_mass = 1.674927351e-27 kg = m_n
-
-# 0.000 000 074 e-27
-proton_mass = 1.672621777e-27 kg = m_p
+fine_structure_constant = (2 * h * R_inf / (m_e * c)) ** 0.5 = α = alpha
+vacuum_permeability = 2 * α * h / (e ** 2 * c) = µ_0 = mu_0 = mu0 = magnetic_constant
+vacuum_permittivity = e ** 2 / (2 * α * h * c) = ε_0 = epsilon_0 = eps_0 = eps0 = electric_constant
+impedance_of_free_space = 2 * α * h / e ** 2 = Z_0 = characteristic_impedance_of_vacuum
+coulomb_constant = α * hbar * c / e ** 2 = k_C
+classical_electron_radius = α * hbar / (m_e * c) = r_e
+thomson_cross_section = 8 / 3 * π * r_e ** 2 = σ_e = sigma_e
diff --git a/pint/context.py b/pint/context.py
index 6a9169e..3f082bc 100644
--- a/pint/context.py
+++ b/pint/context.py
@@ -62,7 +62,7 @@ class Context(object):
"""
- def __init__(self, name, aliases=(), defaults=None):
+ def __init__(self, name=None, aliases=(), defaults=None):
self.name = name
self.aliases = aliases
diff --git a/pint/converters.py b/pint/converters.py
index 41e89af..6dbf4b4 100644
--- a/pint/converters.py
+++ b/pint/converters.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
pint.converters
- ~~~~~~~~~
+ ~~~~~~~~~~~~~~~
Functions and classes related to unit conversions.
diff --git a/pint/default_en.txt b/pint/default_en.txt
index 8c658e4..c860a30 100644
--- a/pint/default_en.txt
+++ b/pint/default_en.txt
@@ -1,13 +1,66 @@
# Default Pint units definition file
# Based on the International System of Units
# Language: english
-# :copyright: 2013 by Pint Authors, see AUTHORS for more details.
+# :copyright: 2013,2019 by Pint Authors, see AUTHORS for more details.
+
+# Syntax
+# ======
+# Units
+# -----
+# <canonical name> = <relation to another unit or dimension> [= <symbol>] [= <alias>] [ = <alias> ] [...]
+#
+# The canonical name and aliases should be expressed in singular form.
+# Pint automatically deals with plurals built by adding 's' to the singular form; plural
+# forms that don't follow this rule should be instead explicitly listed as aliases.
+#
+# If a unit has no symbol and one wants to define aliases, then the symbol should be
+# conventionally set to _.
+#
+# Example:
+# millennium = 1e3 * year = _ = millennia
+#
+#
+# Prefixes
+# --------
+# <prefix>- = <amount> [= <symbol>] [= <alias>] [ = <alias> ] [...]
+#
+# Example:
+# deca- = 1e+1 = da- = deka-
+#
+#
+# Derived dimensions
+# ------------------
+# [dimension name] = <relation to other dimensions>
+#
+# Example:
+# [density] = [mass] / [volume]
+#
+# Note that primary dimensions don't need to be declared; they can be
+# defined or the first time in a unit definition.
+# E.g. see below `meter = [length]`
+#
+#
+# Additional aliases
+# ------------------
+# @alias <canonical name or previous alias> = <alias> [ = <alias> ] [...]
+#
+# Used to add aliases to already existing unit definitions.
+# Particularly useful when one wants to enrich definitions
+# from defaults_en.txt with custom aliases.
+#
+# Example:
+# @alias meter = my_meter
+
+# See also: https://pint.readthedocs.io/en/latest/defining.html
@defaults
group = international
system = mks
@end
+
+#### PREFIXES ####
+
# decimal prefixes
yocto- = 1e-24 = y-
zepto- = 1e-21 = z-
@@ -19,7 +72,7 @@ micro- = 1e-6 = µ- = u-
milli- = 1e-3 = m-
centi- = 1e-2 = c-
deci- = 1e-1 = d-
-deca- = 1e+1 = da- = deka
+deca- = 1e+1 = da- = deka-
hecto- = 1e2 = h-
kilo- = 1e3 = k-
mega- = 1e6 = M-
@@ -40,89 +93,149 @@ exbi- = 2**60 = Ei-
zebi- = 2**70 = Zi-
yobi- = 2**80 = Yi-
-# reference
+# extra_prefixes
+semi- = 0.5 = _ = demi-
+sesqui- = 1.5
+
+
+#### BASE UNITS ####
+
meter = [length] = m = metre
second = [time] = s = sec
ampere = [current] = A = amp
candela = [luminosity] = cd = candle
gram = [mass] = g
mole = [substance] = mol
-kelvin = [temperature]; offset: 0 = K = degK
+kelvin = [temperature]; offset: 0 = K = degK = °K = degree_Kelvin = degreeK # older names supported for compatibility
radian = [] = rad
+neper = [] = Np
bit = []
count = []
+
+#### CONSTANTS ####
+
@import constants_en.txt
-# acceleration
-[acceleration] = [length] / [time] ** 2
+
+#### UNITS ####
+# Common and less common, grouped by quantity.
+# Conversion factors are exact (except when noted),
+# although floating-point conversion may introduce inaccuracies
# Angle
-turn = 2 * pi * radian = revolution = cycle = circle
-degree = pi / 180 * radian = deg = arcdeg = arcdegree = angular_degree
-arcminute = arcdeg / 60 = arcmin = arc_minute = angular_minute
-arcsecond = arcmin / 60 = arcsec = arc_second = angular_second
+turn = 2 * π * radian = _ = revolution = cycle = circle
+degree = π / 180 * radian = deg = arcdeg = arcdegree = angular_degree
+arcminute = degree / 60 = arcmin = arc_minute = angular_minute
+arcsecond = arcminute / 60 = arcsec = arc_second = angular_second
+milliarcsecond = 1e-3 * arcsecond = mas
+grade = π / 200 * radian = grad = gon
+mil = π / 32000 * radian
+
+# Solid angle
steradian = radian ** 2 = sr
+square_degree = (π / 180) ** 2 * sr = sq_deg = sqdeg
+
+# Logarithmic ratio
+bel = 0.5 * ln10 * neper
+
+# Information
+byte = 8 * bit = B = octet
+baud = bit / second = Bd = bps
+
+# Length
+angstrom = 1e-10 * meter = Å = ångström = Å
+micron = micrometer = µ
+fermi = femtometer = fm
+light_year = speed_of_light * julian_year = ly = lightyear
+astronomical_unit = 149597870700 * meter = au # since Aug 2012
+parsec = 1 / tansec * astronomical_unit = pc
+nautical_mile = 1852 * meter = nmi
+bohr = hbar / (alpha * m_e * c) = a_0 = a0 = bohr_radius = atomic_unit_of_length = a_u_length
+x_unit_Cu = K_alpha_Cu_d_220 * d_220 / 1537.4 = Xu_Cu
+x_unit_Mo = K_alpha_Mo_d_220 * d_220 / 707.831 = Xu_Mo
+angstrom_star = K_alpha_W_d_220 * d_220 / 0.2090100 = Å_star
+planck_length = (hbar * gravitational_constant / c ** 3) ** 0.5
+
+# Mass
+metric_ton = 1e3 * kilogram = t = tonne
+unified_atomic_mass_unit = atomic_mass_constant = u = amu
+dalton = atomic_mass_constant = Da
+grain = 64.79891 * milligram = gr
+gamma_mass = microgram
+carat = 200 * milligram = ct = karat
+planck_mass = (hbar * c / gravitational_constant) ** 0.5
+
+# Time
+minute = 60 * second = min
+hour = 60 * minute = hr
+day = 24 * hour = d
+week = 7 * day
+fortnight = 2 * week
+year = 365.25 * day = a = yr = julian_year
+month = year / 12
+decade = 10 * year
+century = 100 * year = _ = centuries
+millennium = 1e3 * year = _ = millennia
+eon = 1e9 * year
+shake = 1e-8 * second
+svedberg = 1e-13 * second
+atomic_unit_of_time = hbar / E_h = a_u_time
+gregorian_year = 365.2425 * day
+sidereal_year = 365.256363004 * day # approximate, as of J2000 epoch
+tropical_year = 365.242190402 * day # approximate, as of J2000 epoch
+common_year = 365 * day
+leap_year = 366 * day
+sidereal_day = day / 1.00273790935079524 # approximate
+sidereal_month = 27.32166155 * day # approximate
+tropical_month = 27.321582 * day # approximate
+synodic_month = 29.530589 * day = _ = lunar_month # approximate
+planck_time = (hbar * gravitational_constant / c ** 5) ** 0.5
+
+# Temperature
+degree_Celsius = kelvin; offset: 273.15 = °C = celsius = degC = degreeC
+degree_Rankine = 5 / 9 * kelvin; offset: 0 = °R = rankine = degR = degreeR
+degree_Fahrenheit = 5 / 9 * kelvin; offset: 233.15 + 200 / 9 = °F = fahrenheit = degF = degreeF
+degree_Reaumur = 4 / 5 * kelvin; offset: 273.15 = °Re = reaumur = degRe = degreeRe = degree_Réaumur = réaumur
+atomic_unit_of_temperature = E_h / k = a_u_temp
+planck_temperature = (hbar * c ** 5 / gravitational_constant / k ** 2) ** 0.5
# Area
[area] = [length] ** 2
-are = 100 * m**2
-barn = 1e-28 * m ** 2 = b
-cmil = 5.067075e-10 * m ** 2 = circular_mils
-darcy = 9.869233e-13 * m ** 2
+are = 100 * meter ** 2
+barn = 1e-28 * meter ** 2 = b
+darcy = centipoise * centimeter ** 2 / (second * atmosphere)
hectare = 100 * are = ha
-# Concentration
-[concentration] = [substance] / [volume]
-molar = mol / (1e-3 * m ** 3) = M
+# Volume
+[volume] = [length] ** 3
+liter = decimeter ** 3 = l = L = litre
+cubic_centimeter = centimeter ** 3 = cc
+lambda = microliter = λ
+stere = meter ** 3
-# Activity
-[activity] = [substance] / [time]
-katal = mole / second = kat
+# Frequency
+[frequency] = 1 / [time]
+hertz = 1 / second = Hz
+revolutions_per_minute = revolution / minute = rpm
+counts_per_second = count / second = cps
-# EM
-esu = 1 * erg**0.5 * centimeter**0.5 = statcoulombs = statC = franklin = Fr
-esu_per_second = 1 * esu / second = statampere
-ampere_turn = 1 * A
-gilbert = 10 / (4 * pi ) * ampere_turn
-coulomb = ampere * second = C
-volt = joule / coulomb = V
-farad = coulomb / volt = F
-ohm = volt / ampere = Ω
-siemens = ampere / volt = S = mho
-weber = volt * second = Wb
-tesla = weber / meter ** 2 = T
-henry = weber / ampere = H
-elementary_charge = 1.602176487e-19 * coulomb = e
-chemical_faraday = 9.64957e4 * coulomb
-physical_faraday = 9.65219e4 * coulomb
-faraday = 96485.3399 * coulomb = C12_faraday
-gamma = 1e-9 * tesla
-gauss = 1e-4 * tesla
-maxwell = 1e-8 * weber = mx
-oersted = 1000 / (4 * pi) * A / m = Oe
-statfarad = 1.112650e-12 * farad = statF = stF
-stathenry = 8.987554e11 * henry = statH = stH
-statmho = 1.112650e-12 * siemens = statS = stS
-statohm = 8.987554e11 * ohm
-statvolt = 2.997925e2 * volt = statV = stV
-unit_pole = 1.256637e-7 * weber
+# Wavenumber
+[wavenumber] = 1 / [length]
+reciprocal_centimeter = 1 / cm = cm_1 = kayser
-# Energy
-[energy] = [force] * [length]
-joule = newton * meter = J
-erg = dyne * centimeter
-btu = 1.05505585262e3 * joule = Btu = BTU = british_thermal_unit
-electron_volt = 1.60217653e-19 * J = eV
-quadrillion_btu = 10**15 * btu = quad
-thm = 100000 * BTU = therm = EC_therm
-calorie = 4.184 * joule = cal = thermochemical_calorie
-international_steam_table_calorie = 4.1868 * joule
-ton_TNT = 4.184e9 * joule = tTNT
-US_therm = 1.054804e8 * joule
-watt_hour = watt * hour = Wh = watthour
-hartree = 4.35974394e-18 * joule = = Eh = E_h = hartree_energy
-toe = 41.868e9 * joule = tonne_of_oil_equivalent
+# Velocity
+[velocity] = [length] / [time] = [speed]
+knot = nautical_mile / hour = kt = knot_international = international_knot
+mile_per_hour = mile / hour = mph = MPH
+kilometer_per_hour = kilometer / hour = kph = KPH
+kilometer_per_second = kilometer / second = kps
+meter_per_second = meter / second = mps
+foot_per_second = foot / second = fps
+
+# Acceleration
+[acceleration] = [velocity] / [time]
+galileo = centimeter / second ** 2 = Gal
# Force
[force] = [mass] * [acceleration]
@@ -130,265 +243,298 @@ newton = kilogram * meter / second ** 2 = N
dyne = gram * centimeter / second ** 2 = dyn
force_kilogram = g_0 * kilogram = kgf = kilogram_force = pond
force_gram = g_0 * gram = gf = gram_force
-force_ounce = g_0 * ounce = ozf = ounce_force
-force_pound = g_0 * lb = lbf = pound_force
-force_metric_ton = g_0 * t = metric_ton_force = force_t = t_force
-poundal = lb * feet / second ** 2 = pdl
-kip = 1000*lbf
+force_metric_ton = g_0 * metric_ton = tf = metric_ton_force = force_t = t_force
+atomic_unit_of_force = E_h / a_0 = a_u_force
-# Frequency
-[frequency] = 1 / [time]
-hertz = 1 / second = Hz = rps
-revolutions_per_minute = revolution / minute = rpm
-counts_per_second = count / second = cps
+# Energy
+[energy] = [force] * [length]
+joule = newton * meter = J
+erg = dyne * centimeter
+watt_hour = watt * hour = Wh = watthour
+electron_volt = e * volt = eV
+rydberg = h * c * R_inf = Ry
+hartree = 2 * rydberg = E_h = Eh = hartree_energy = atomic_unit_of_energy = a_u_energy
+calorie = 4.184 * joule = cal = thermochemical_calorie = cal_th
+international_calorie = 4.1868 * joule = cal_it = international_steam_table_calorie
+fifteen_degree_calorie = 4.1855 * joule = cal_15
+british_thermal_unit = 1055.056 * joule = Btu = BTU = Btu_iso
+international_british_thermal_unit = 1e3 * pound / kilogram * degR / kelvin * international_calorie = Btu_it
+thermochemical_british_thermal_unit = 1e3 * pound / kilogram * degR / kelvin * calorie = Btu_th
+quadrillion_Btu = 1e15 * Btu = quad
+therm = 1e5 * Btu = thm = EC_therm
+US_therm = 1.054804e8 * joule # approximate, no exact definition
+ton_TNT = 1e9 * calorie = tTNT
+tonne_of_oil_equivalent = 1e10 * international_calorie = toe
+atmosphere_liter = atmosphere * liter = atm_l
-# Heat
-#RSI = degK * meter ** 2 / watt
-#clo = 0.155 * RSI = clos
-#R_value = foot ** 2 * degF * hour / btu
+# Power
+[power] = [energy] / [time]
+watt = joule / second = W
+volt_ampere = volt * ampere = VA
+horsepower = 550 * foot * force_pound / second = hp = UK_horsepower = hydraulic_horsepower
+boiler_horsepower = 33475 * Btu / hour # unclear which Btu
+metric_horsepower = 75 * force_kilogram * meter / second
+electrical_horsepower = 746 * watt
+refrigeration_ton = 12e3 * Btu / hour = _ = ton_of_refrigeration # approximate, no exact definition
+standard_liter_per_minute = atmosphere * liter / minute = slpm = slm
+conventional_watt_90 = K_J90 ** 2 * R_K90 / (K_J ** 2 * R_K) * watt = W_90
+
+# Density (as auxiliary for pressure)
+[density] = [mass] / [volume]
+mercury = 13.5951 * kilogram / liter = Hg = Hg_0C = Hg_32F = conventional_mercury
+water = 1.0 * kilogram / liter = H2O = conventional_water
+mercury_60F = 13.5568 * kilogram / liter = Hg_60F # approximate
+water_39F = 0.999972 * kilogram / liter = water_4C # approximate
+water_60F = 0.999001 * kilogram / liter # approximate
-# Information
-byte = 8 * bit = B = octet
-baud = bit / second = Bd = bps
+# Pressure
+[pressure] = [force] / [area]
+pascal = newton / meter ** 2 = Pa
+barye = dyne / centimeter ** 2 = Ba = barie = barad = barrie = baryd
+bar = 1e5 * pascal
+technical_atmosphere = kilogram * g_0 / centimeter ** 2 = at
+torr = atm / 760
+pound_force_per_square_inch = force_pound / inch ** 2 = psi
+kip_per_square_inch = kip / inch ** 2 = ksi
+millimeter_Hg = millimeter * Hg * g_0 = mmHg = mm_Hg = millimeter_Hg_0C
+centimeter_Hg = centimeter * Hg * g_0 = cmHg = cm_Hg = centimeter_Hg_0C
+inch_Hg = inch * Hg * g_0 = inHg = in_Hg = inch_Hg_32F
+inch_Hg_60F = inch * Hg_60F * g_0
+inch_H2O_39F = inch * water_39F * g_0
+inch_H2O_60F = inch * water_60F * g_0
+foot_H2O = foot * water * g_0 = ftH2O = feet_H2O
+centimeter_H2O = centimeter * water * g_0 = cmH2O = cm_H2O
+
+# Torque
+[torque] = [force] * [length]
+foot_pound = foot * force_pound = ft_lb = footpound
-# Irradiance
-peak_sun_hour = 1000 * watt_hour / meter**2 = PSH
-langley = thermochemical_calorie / centimeter**2 = Langley
+# Viscosity
+[viscosity] = [pressure] * [time]
+poise = 0.1 * Pa * second = P
+reyn = psi * second
-# Length
-angstrom = 1e-10 * meter = Å = ångström = Å
-parsec = 3.08568025e16 * meter = pc
-light_year = speed_of_light * julian_year = ly = lightyear
-astronomical_unit = 149597870691 * meter = au
+# Kinematic viscosity
+[kinematic_viscosity] = [area] / [time]
+stokes = centimeter ** 2 / second = St
-# Mass
-carat = 200 * milligram
-metric_ton = 1000 * kilogram = t = tonne
-atomic_mass_unit = 1.660538782e-27 * kilogram = u = amu = dalton = Da
-bag = 94 * lb
+# Fluidity
+[fluidity] = 1 / [viscosity]
+rhe = 1 / poise
-# Textile
-denier = gram / (9000 * meter)
-tex = gram / (1000 * meter)
-dtex = decitex
+# Amount of substance
+particle = 1 / N_A = _ = molec = molecule
-# These are Indirect yarn numbering systems (length/unit mass)
-jute = lb / (14400 * yd) = Tj
-Ne = 1/590.5 / tex
-Nm = 1/1000 / tex
+# Concentration
+[concentration] = [substance] / [volume]
+molar = mole / liter = M
-@context textile
- # Allow switching between Direct count system (i.e. tex) and
- # Indirect count system (i.e. Ne, Nm)
- [mass] / [length] <-> [length] / [mass]: 1 / value
-@end
+# Catalytic activity
+[activity] = [substance] / [time]
+katal = mole / second = kat
+enzyme_unit = micromole / minute = U = enzymeunit
+
+# Entropy
+[entropy] = [energy] / [temperature]
+clausius = calorie / kelvin = Cl
+
+# Molar entropy
+[molar_entropy] = [entropy] / [substance]
+entropy_unit = calorie / kelvin / mole = eu
-# Photometry
+# Radiation
+becquerel = counts_per_second = Bq
+curie = 3.7e10 * becquerel = Ci
+rutherford = 1e6 * becquerel = Rd
+gray = joule / kilogram = Gy
+sievert = joule / kilogram = Sv
+rads = 0.01 * gray
+rem = 0.01 * sievert
+roentgen = 2.58e-4 * coulomb / kilogram = _ = röntgen # approximate, depends on medium
+
+# Heat transimission
+[heat_transmission] = [energy] / [area]
+peak_sun_hour = 1e3 * watt_hour / meter ** 2 = PSH
+langley = thermochemical_calorie / centimeter ** 2 = Ly
+
+# Luminance
+[luminance] = [luminosity] / [area]
+nit = candela / meter ** 2
+stilb = candela / centimeter ** 2
+lambert = 1 / π * candela / centimeter ** 2
+
+# Luminous flux
+[luminous_flux] = [luminosity]
lumen = candela * steradian = lm
+
+# Illuminance
+[illuminance] = [luminous_flux] / [area]
lux = lumen / meter ** 2 = lx
-# Power
-[power] = [energy] / [time]
-watt = joule / second = W = volt_ampere = VA
-horsepower = 33000 * ft * lbf / min = hp = UK_horsepower = British_horsepower
-boiler_horsepower = 33475 * btu / hour
-metric_horsepower = 75 * force_kilogram * meter / second
-electric_horsepower = 746 * watt
-hydraulic_horsepower = 550 * feet * lbf / second
-refrigeration_ton = 12000 * btu / hour = ton_of_refrigeration
+# Intensity
+[intensity] = [power] / [area]
+atomic_unit_of_intensity = 0.5 * ε_0 * c * atomic_unit_of_electric_field ** 2 = a_u_intensity
+
+# Current
+biot = 10 * ampere = Bi
+abampere = biot = abA
+atomic_unit_of_current = e / atomic_unit_of_time = a_u_current
+mean_international_ampere = mean_international_volt / mean_international_ohm = A_it
+US_international_ampere = US_international_volt / US_international_ohm = A_US
+conventional_ampere_90 = K_J90 * R_K90 / (K_J * R_K) * ampere = A_90
+planck_current = (c ** 6 / gravitational_constant / k_C) ** 0.5
+
+# Charge
+[charge] = [current] * [time]
+coulomb = ampere * second = C
+abcoulomb = 10 * C = abC
+faraday = e * N_A * mole
+conventional_coulomb_90 = K_J90 * R_K90 / (K_J * R_K) * coulomb = C_90
-# Pressure
-[pressure] = [force] / [area]
-Hg = gravity * 13.59510 * gram / centimeter ** 3 = mercury = conventional_mercury
-mercury_60F = gravity * 13.5568 * gram / centimeter ** 3
-H2O = gravity * 1000 * kilogram / meter ** 3 = h2o = water = conventional_water
-water_4C = gravity * 999.972 * kilogram / meter ** 3 = water_39F
-water_60F = gravity * 999.001 * kilogram / m ** 3
-pascal = newton / meter ** 2 = Pa
-bar = 100000 * pascal
-atmosphere = 101325 * pascal = atm = standard_atmosphere
-technical_atmosphere = kilogram * gravity / centimeter ** 2 = at
-torr = atm / 760
-pound_force_per_square_inch = pound * gravity / inch ** 2 = psi
-kip_per_square_inch = kip / inch ** 2 = ksi
-barye = 0.1 * newton / meter ** 2 = barie = barad = barrie = baryd = Ba
-mm_Hg = millimeter * Hg = mmHg = millimeter_Hg = millimeter_Hg_0C
-cm_Hg = centimeter * Hg = cmHg = centimeter_Hg
-in_Hg = inch * Hg = inHg = inch_Hg = inch_Hg_32F
-inch_Hg_60F = inch * mercury_60F
-inch_H2O_39F = inch * water_39F
-inch_H2O_60F = inch * water_60F
-footH2O = ft * water
-cmH2O = centimeter * water
-foot_H2O = ft * water = ftH2O
-standard_liter_per_minute = 1.68875 * Pa * m ** 3 / s = slpm = slm
+# Electric potential
+[electric_potential] = [energy] / [charge]
+volt = joule / coulomb = V
+abvolt = 1e-8 * volt = abV
+mean_international_volt = 1.00034 * volt = V_it # approximate
+US_international_volt = 1.00033 * volt = V_US # approximate
+conventional_volt_90 = K_J90 / K_J * volt = V_90
-# Radiation
-Bq = Hz = becquerel
-curie = 3.7e10 * Bq = Ci
-rutherford = 1e6*Bq = Rd
-Gy = joule / kilogram = gray = Sv = sievert
-rem = 1e-2 * sievert
-rads = 1e-2 * gray
-roentgen = 2.58e-4 * coulomb / kilogram
+# Electric field
+[electric_field] = [electric_potential] / [length]
+atomic_unit_of_electric_field = e * k_C / a_0 ** 2 = a_u_electric_field
-# Temperature
-degC = kelvin; offset: 273.15 = celsius
-degR = 5 / 9 * kelvin; offset: 0 = rankine
-degF = 5 / 9 * kelvin; offset: 255.372222 = fahrenheit
+# Electric displacement field
+[electric_displacement_field] = [charge] / [area]
-# Time
-minute = 60 * second = min
-hour = 60 * minute = hr
-day = 24 * hour
-week = 7 * day
-fortnight = 2 * week
-year = 31556925.9747 * second
-month = year / 12
-shake = 1e-8 * second
-sidereal_day = day / 1.00273790935079524
-sidereal_hour = sidereal_day / 24
-sidereal_minute = sidereal_hour / 60
-sidereal_second = sidereal_minute / 60
-sidereal_year = 366.25636042 * sidereal_day
-sidereal_month = 27.321661 * sidereal_day
-tropical_month = 27.321661 * day
-synodic_month = 29.530589 * day = lunar_month
-common_year = 365 * day
-leap_year = 366 * day
-julian_year = 365.25 * day
-gregorian_year = 365.2425 * day
-millenium = 1000 * year = millenia = milenia = milenium
-eon = 1e9 * year
-work_year = 2056 * hour
-work_month = work_year / 12
+# Resistance
+[resistance] = [electric_potential] / [current]
+ohm = volt / ampere = Ω
+abohm = 1e-9 * ohm = abΩ
+mean_international_ohm = 1.00049 * ohm = Ω_it = ohm_it # approximate
+US_international_ohm = 1.000495 * ohm = Ω_US = ohm_US # approximate
+conventional_ohm_90 = R_K / R_K90 * ohm = Ω_90 = ohm_90
-# Velocity
-[speed] = [length] / [time]
-nautical_mile = 1852 m = nmi # exact
-knot = nautical_mile / hour = kt = knot_international = international_knot = nautical_miles_per_hour
-mph = mile / hour = MPH
-kph = kilometer / hour = KPH
+# Resistivity
+[resistivity] = [resistance] * [length]
-# Viscosity
-[viscosity] = [pressure] * [time]
-poise = 1e-1 * Pa * second = P
-stokes = 1e-4 * meter ** 2 / second = St
-rhe = 10 / (Pa * s)
+# Conductance
+[conductance] = [current] / [electric_potential]
+siemens = ampere / volt = S = mho
+absiemens = 1e9 * siemens = abS = abmho
-# Volume
-[volume] = [length] ** 3
-liter = 1e-3 * m ** 3 = l = L = litre
-cc = centimeter ** 3 = cubic_centimeter
-stere = meter ** 3
+# Capacitance
+[capacitance] = [charge] / [electric_potential]
+farad = coulomb / volt = F
+abfarad = 1e9 * farad = abF
+conventional_farad_90 = R_K90 / R_K * farad = F_90
+# Inductance
+[inductance] = [magnetic_flux] / [current]
+henry = weber / ampere = H
+abhenry = 1e-9 * henry = abH
+conventional_henry_90 = R_K / R_K90 * henry = H_90
-@context(n=1) spectroscopy = sp
- # n index of refraction of the medium.
- [length] <-> [frequency]: speed_of_light / n / value
- [frequency] -> [energy]: planck_constant * value
- [energy] -> [frequency]: value / planck_constant
- # allow wavenumber / kayser
- 1 / [length] <-> [length]: 1 / value
-@end
+# Magnetic flux
+[magnetic_flux] = [electric_potential] * [time]
+weber = volt * second = Wb
+unit_pole = µ_0 * biot * centimeter
-@context boltzmann
- [temperature] -> [energy]: boltzmann_constant * value
- [energy] -> [temperature]: value / boltzmann_constant
-@end
+# Magnetic field
+[magnetic_field] = [magnetic_flux] / [area]
+tesla = weber / meter ** 2 = T
+gamma = 1e-9 * tesla = γ
-@context(mw=0,volume=0,solvent_mass=0) chemistry = chem
- # mw is the molecular weight of the species
- # volume is the volume of the solution
- # solvent_mass is the mass of solvent in the solution
+# Magnetomotive force
+[magnetomotive_force] = [current]
+ampere_turn = ampere = At
+biot_turn = biot
+gilbert = 1 / (4 * π) * biot_turn = Gb
- # moles -> mass require the molecular weight
- [substance] -> [mass]: value * mw
- [mass] -> [substance]: value / mw
+# Magnetic field strength
+[magnetic_field_strength] = [current] / [length]
- # moles/volume -> mass/volume and moles/mass -> mass / mass
- # require the molecular weight
- [substance] / [volume] -> [mass] / [volume]: value * mw
- [mass] / [volume] -> [substance] / [volume]: value / mw
- [substance] / [mass] -> [mass] / [mass]: value * mw
- [mass] / [mass] -> [substance] / [mass]: value / mw
+# Electric dipole moment
+[electric_dipole] = [charge] * [length]
+debye = 1e-9 / ζ * coulomb * angstrom = D # formally 1 D = 1e-10 Fr*Å, but we generally want to use it outside the Gaussian context
- # moles/volume -> moles requires the solution volume
- [substance] / [volume] -> [substance]: value * volume
- [substance] -> [substance] / [volume]: value / volume
+# Electric quadrupole moment
+[electric_quadrupole] = [charge] * [area]
+buckingham = debye * angstrom
- # moles/mass -> moles requires the solvent (usually water) mass
- [substance] / [mass] -> [substance]: value * solvent_mass
- [substance] -> [substance] / [mass]: value / solvent_mass
+# Magnetic dipole moment
+[magnetic_dipole] = [current] * [area]
+bohr_magneton = e * hbar / (2 * m_e) = µ_B = mu_B
+nuclear_magneton = e * hbar / (2 * m_p) = µ_N = mu_N
- # moles/mass -> moles/volume require the solvent mass and the volume
- [substance] / [mass] -> [substance]/[volume]: value * solvent_mass / volume
- [substance] / [volume] -> [substance] / [mass]: value / solvent_mass * volume
-@end
+#### UNIT GROUPS ####
+# Mostly for length, area, volume, mass, force
+# (customary or specialized units)
-# Most of the definitions that follows are derived from:
-# See http://www.nist.gov/pml/wmd/pubs/hb44.cfm
@group USCSLengthInternational
+ thou = 1e-3 * inch = th = mil_length
inch = yard / 36 = in = international_inch = inches = international_inches
+ hand = 4 * inch
foot = yard / 3 = ft = international_foot = feet = international_feet
- yard = 0.9144 metres = yd = international_yard
- mile = 1760 yard = mi = international_mile
-
- square_inch = 1 inch ** 2 = sq_in = square_inches
- square_foot = 1 foot ** 2 = sq_ft = square_feet
- square_yard = 1 yard ** 2 = sq_yd
- square_mile = 1 mile ** 2 = sq_mi
-
- cubic_inch = 1 in ** 3 = cu_in
- cubic_foot = 1 ft ** 3 = cu_ft = cubic_feet
- cubic_yard = 1 yd ** 3 = cu_yd
-
- acre_foot = acre * foot = acre_feet
+ yard = 0.9144 * meter = yd = international_yard # since Jul 1959
+ mile = 1760 * yard = mi = international_mile
+
+ circular_mil = π / 4 * mil_length ** 2 = cmil
+ square_inch = inch ** 2 = sq_in = square_inches
+ square_foot = foot ** 2 = sq_ft = square_feet
+ square_yard = yard ** 2 = sq_yd
+ square_mile = mile ** 2 = sq_mi
+
+ cubic_inch = in ** 3 = cu_in
+ cubic_foot = ft ** 3 = cu_ft = cubic_feet
+ cubic_yard = yd ** 3 = cu_yd
@end
@group USCSLengthSurvey
- link = 0.66 survey_foot = li = survey_link
- survey_foot = foot / 0.999998 = sft
- rod = 16.5 survey_foot = rd = pole = perch
- chain = 66 survey_foot
- survey_mile = 5280 survey_foot
-
- acre = 43560 survey_foot ** 2
- square_rod = 1 rod ** 2 = sq_rod = sq_pole = sq_perch
-
- fathom = 6 survey_foot
- us_statute_mile = 5280 survey_foot
- league = 3 us_statute_mile
- furlong = us_statute_mile / 8
+ link = 1e-2 * chain = li = survey_link
+ survey_foot = 1200 / 3937 * meter = sft
+ fathom = 6 * survey_foot
+ rod = 16.5 * survey_foot = rd = pole = perch
+ chain = 4 * rod
+ furlong = 40 * rod = fur
+ cables_length = 120 * fathom
+ survey_mile = 5280 * survey_foot = smi = us_statute_mile
+ league = 3 * survey_mile
+
+ square_rod = rod ** 2 = sq_rod = sq_pole = sq_perch
+ acre = 10 * chain ** 2
+ square_survey_mile = survey_mile ** 2 = _ = section
+ square_league = league ** 2
+
+ acre_foot = acre * survey_foot = _ = acre_feet
@end
@group USCSDryVolume
- dry_pint = 33.6003125 cubic_inch = dpi = US_dry_pint
- dry_quart = 2 dry_pint = dqt = US_dry_quart
- dry_gallon = 8 dry_pint = dgal = US_dry_gallon
- peck = 16 dry_pint = pk
- bushel = 64 dry_pint = bu
- dry_barrel = 7065 cubic_inch = US_dry_barrel
+ dry_pint = bushel / 64 = dpi = US_dry_pint
+ dry_quart = bushel / 32 = dqt = US_dry_quart
+ dry_gallon = bushel / 8 = dgal = US_dry_gallon
+ peck = bushel / 4 = pk
+ bushel = 2150.42 cubic_inch = bu
+ dry_barrel = 7056 cubic_inch = _ = US_dry_barrel
+ board_foot = ft * ft * in = FBM = board_feet = BF = BDFT = super_foot = superficial_foot = super_feet = superficial_feet
@end
@group USCSLiquidVolume
- minim = liquid_pint / 7680
- fluid_dram = liquid_pint / 128 = fldr = fluidram = US_fluid_dram
- fluid_ounce = liquid_pint / 16 = floz = US_fluid_ounce = US_liquid_ounce
- gill = liquid_pint / 4 = gi = liquid_gill = US_liquid_gill
-
- pint = 28.875 cubic_inch = pt = liquid_pint = US_pint
-
- quart = 2 liquid_pint = qt = liquid_quart = US_liquid_quart
- gallon = 8 liquid_pint = gal = liquid_gallon = US_liquid_gallon
+ minim = pint / 7680
+ fluid_dram = pint / 128 = fldr = fluidram = US_fluid_dram = US_liquid_dram
+ fluid_ounce = pint / 16 = floz = US_fluid_ounce = US_liquid_ounce
+ gill = pint / 4 = gi = liquid_gill = US_liquid_gill
+ pint = quart / 2 = pt = liquid_pint = US_pint
+ fifth = gallon / 5 = _ = US_liquid_fifth
+ quart = gallon / 4 = qt = liquid_quart = US_liquid_quart
+ gallon = 231 * cubic_inch = gal = liquid_gallon = US_liquid_gallon
@end
@group USCSVolumeOther
- teaspoon = tablespoon / 3 = tsp
- tablespoon = floz / 2 = tbsp = Tbsp = Tblsp = tblsp = tbs = Tbl
+ teaspoon = fluid_ounce / 6 = tsp
+ tablespoon = fluid_ounce / 2 = tbsp
shot = 3 * tablespoon = jig = US_shot
- cup = 8 fluid_ounce = cp = liquid_cup = US_liquid_cup
+ cup = pint / 2 = cp = liquid_cup = US_liquid_cup
barrel = 31.5 * gallon = bbl
oil_barrel = 42 * gallon = oil_bbl
beer_barrel = 31 * gallon = beer_bbl
@@ -396,69 +542,264 @@ stere = meter ** 3
@end
@group Avoirdupois
- grain = avdp_pound / 7000 = gr
- drachm = pound / 256 = dr = avoirdupois_dram = avdp_dram = dram
+ dram = pound / 256 = dr = avoirdupois_dram = avdp_dram = drachm
ounce = pound / 16 = oz = avoirdupois_ounce = avdp_ounce
- pound = 453.59237 gram = lb = avoirdupois_pound = avdp_pound
-
- short_hunderdweight = 100 avoirdupois_pound = ch_cwt
- long_hunderweight = 112 avoirdupois_pound = lg_cwt
- short_ton = 2000 avoirdupois_pound
- long_ton = 2240 avoirdupois_pound
- force_short_ton = short_ton * g_0 = short_ton_force
- force_long_ton = long_ton * g_0 = long_ton_force
+ pound = 7e3 * grain = lb = avoirdupois_pound = avdp_pound
+ stone = 14 * pound
+ quarter = 28 * stone
+ bag = 94 * pound
+ hundredweight = 100 * pound = cwt = short_hundredweight
+ long_hundredweight = 112 * pound
+ ton = 2e3 * pound = _ = short_ton
+ long_ton = 2240 * pound
+ slug = g_0 * pound * second ** 2 / foot
+
+ force_ounce = g_0 * ounce = ozf = ounce_force
+ force_pound = g_0 * pound = lbf = pound_force
+ force_ton = g_0 * ton = _ = ton_force = force_short_ton = short_ton_force
+ force_long_ton = g_0 * long_ton = _ = long_ton_force
+ kip = 1e3 * force_pound
+ poundal = pound * foot / second ** 2 = pdl
@end
-@group Troy
- pennyweight = 24 grain = dwt
- troy_ounce = 480 grain = toz
- troy_pound = 12 troy_ounce = tlb
+@group AvoirdupoisUK using Avoirdupois
+ UK_hundredweight = long_hundredweight = UK_cwt
+ UK_ton = long_ton
+ UK_force_ton = force_long_ton = _ = UK_ton_force
@end
-@group Apothecary
- scruple = 20 grain
- apothecary_dram = 3 scruple = ap_dr
- apothecary_ounce = 8 apothecary_dram = ap_oz
- apothecary_pound = 12 apothecary_ounce = ap_lb
+@group AvoirdupoisUS using Avoirdupois
+ US_hundredweight = hundredweight = US_cwt
+ US_ton = ton
+ US_force_ton = force_ton = _ = US_ton_force
@end
-@group AvoirdupoisUK using Avoirdupois
- stone = 14 pound
- quarter = 28 stone
- UK_hundredweight = long_hunderweight = UK_cwt
- UK_ton = long_ton
- UK_ton_force = force_long_ton
+@group Troy
+ pennyweight = 24 * grain = dwt
+ troy_ounce = 480 * grain = toz = ozt
+ troy_pound = 12 * troy_ounce = tlb = lbt
@end
-@group AvoirdupoisUS using Avoirdupois
- US_hundredweight = short_hunderdweight = US_cwt
- US_ton = short_ton = ton
- US_ton_force = force_short_ton = ton_force = force_ton
+@group Apothecary
+ scruple = 20 * grain
+ apothecary_dram = 3 * scruple = ap_dr
+ apothecary_ounce = 8 * apothecary_dram = ap_oz
+ apothecary_pound = 12 * apothecary_ounce = ap_lb
+@end
+
+@group ImperialVolume
+ imperial_minim = imperial_fluid_ounce / 480
+ imperial_fluid_scruple = imperial_fluid_ounce / 24
+ imperial_fluid_drachm = imperial_fluid_ounce / 8 = imperial_fldr = imperial_fluid_dram
+ imperial_fluid_ounce = imperial_pint / 20 = imperial_floz = UK_fluid_ounce
+ imperial_gill = imperial_pint / 4 = imperial_gi = UK_gill
+ imperial_cup = imperial_pint / 2 = imperial_cp = UK_cup
+ imperial_pint = imperial_gallon / 8 = imperial_pt = UK_pint
+ imperial_quart = imperial_gallon / 4 = imperial_qt = UK_quart
+ imperial_gallon = 4.54609 * liter = imperial_gal = UK_gallon
+ imperial_peck = 2 * imperial_gallon = imperial_pk = UK_pk
+ imperial_bushel = 8 * imperial_gallon = imperial_bu = UK_bushel
+ imperial_barrel = 36 * imperial_gallon = imperial_bbl = UK_bbl
@end
@group Printer
- # Length
- pixel = [printing_unit] = dot = px = pel = picture_element
+ pica = inch / 6 = _ = printers_pica
+ point = pica / 12 = pp = printers_point = big_point = bp
+ didot = 1 / 2660 * m
+ cicero = 12 * didot
+ tex_point = inch / 72.27
+ tex_pica = 12 * tex_point
+ tex_didot = 1238 / 1157 * tex_point
+ tex_cicero = 12 * tex_didot
+ scaled_point = tex_point / 65536
+ css_pixel = inch / 96 = px
+
+ pixel = [printing_unit] = _ = dot = pel = picture_element
pixels_per_centimeter = pixel / cm = PPCM
pixels_per_inch = pixel / inch = dots_per_inch = PPI = ppi = DPI = printers_dpi
bits_per_pixel = bit / pixel = bpp
+@end
- point = yard / 216 / 12 = pp = printers_point
- thou = yard / 36000 = th = mil
- pica = yard / 216 = P̸ = printers_pica
+@group Textile
+ tex = gram / kilometer = Tt
+ dtex = decitex
+ denier = gram / (9 * kilometer) = den = Td
+ jute = pound / (14400 * yard) = Tj
+ aberdeen = jute = Ta
+ RKM = kgf * 1000 / tex
+
+ number_english = 840 * yard / pound = Ne = NeC = ECC
+ number_meter = kilometer / kilogram = Nm
@end
-@group ImperialVolume
- imperial_fluid_ounce = imperial_pint / 20 = imperial_floz = UK_fluid_ounce
- imperial_fluid_drachm = imperial_fluid_ounce / 8 = imperial_fluid_dram
- imperial_gill = imperial_pint / 4 = imperial_gi = UK_gill
- imperial_cup = imperial_pint / 2 = imperial_cp = UK_cup
- imperial_pint = 568.26125 * milliliter = imperial_pt = UK_pint
- imperial_quart = 2 * imperial_pint = imperial_qt = UK_quart
- imperial_gallon = 8 * imperial_pint = imperial_gal = UK_gallon
- imperial_peck = 16 * imperial_pint = imperial_pk = UK_pk
- imperial_bushel = 64 * imperial_pint = imperial_bu = UK_bushel
- imperial_barrel = 288 * imperial_pint = imperial_bbl = UK_bbl
+
+#### CGS ELECTROMAGNETIC UNITS ####
+
+# === Gaussian system of units ===
+@group Gaussian
+ franklin = erg ** 0.5 * centimeter ** 0.5 = Fr = statcoulomb = statC = esu
+ statvolt = erg / franklin = statV
+ statampere = franklin / second = statA
+ gauss = dyne / franklin = G
+ maxwell = gauss * centimeter ** 2 = Mx
+ oersted = dyne / maxwell = Oe = ørsted
+ statohm = statvolt / statampere = statΩ
+ statfarad = franklin / statvolt = statF
+ statmho = statampere / statvolt
+@end
+# Note this system is not commensurate with SI, as ε_0 and µ_0 disappear;
+# some quantities with different dimensions in SI have the same
+# dimensions in the Gaussian system (e.g. [Mx] = [Fr], but [Wb] != [C]),
+# and therefore the conversion factors depend on the context (not in pint sense)
+[gaussian_charge] = [length] ** 1.5 * [mass] ** 0.5 / [time]
+[gaussian_current] = [gaussian_charge] / [time]
+[gaussian_electric_potential] = [gaussian_charge] / [length]
+[gaussian_electric_field] = [gaussian_electric_potential] / [length]
+[gaussian_electric_displacement_field] = [gaussian_charge] / [area]
+[gaussian_electric_flux] = [gaussian_charge]
+[gaussian_electric_dipole] = [gaussian_charge] * [length]
+[gaussian_electric_quadrupole] = [gaussian_charge] * [area]
+[gaussian_magnetic_field] = [force] / [gaussian_charge]
+[gaussian_magnetic_field_strength] = [gaussian_magnetic_field]
+[gaussian_magnetic_flux] = [gaussian_magnetic_field] * [area]
+[gaussian_magnetic_dipole] = [energy] / [gaussian_magnetic_field]
+[gaussian_resistance] = [gaussian_electric_potential] / [gaussian_current]
+[gaussian_resistivity] = [gaussian_resistance] * [length]
+[gaussian_capacitance] = [gaussian_charge] / [gaussian_electric_potential]
+[gaussian_inductance] = [gaussian_electric_potential] * [time] / [gaussian_current]
+[gaussian_conductance] = [gaussian_current] / [gaussian_electric_potential]
+@context Gaussian = Gau
+ [gaussian_charge] -> [charge]: value / k_C ** 0.5
+ [charge] -> [gaussian_charge]: value * k_C ** 0.5
+ [gaussian_current] -> [current]: value / k_C ** 0.5
+ [current] -> [gaussian_current]: value * k_C ** 0.5
+ [gaussian_electric_potential] -> [electric_potential]: value * k_C ** 0.5
+ [electric_potential] -> [gaussian_electric_potential]: value / k_C ** 0.5
+ [gaussian_electric_field] -> [electric_field]: value * k_C ** 0.5
+ [electric_field] -> [gaussian_electric_field]: value / k_C ** 0.5
+ [gaussian_electric_displacement_field] -> [electric_displacement_field]: value / (4 * π / ε_0) ** 0.5
+ [electric_displacement_field] -> [gaussian_electric_displacement_field]: value * (4 * π / ε_0) ** 0.5
+ [gaussian_electric_dipole] -> [electric_dipole]: value / k_C ** 0.5
+ [electric_dipole] -> [gaussian_electric_dipole]: value * k_C ** 0.5
+ [gaussian_electric_quadrupole] -> [electric_quadrupole]: value / k_C ** 0.5
+ [electric_quadrupole] -> [gaussian_electric_quadrupole]: value * k_C ** 0.5
+ [gaussian_magnetic_field] -> [magnetic_field]: value / (4 * π / µ_0) ** 0.5
+ [magnetic_field] -> [gaussian_magnetic_field]: value * (4 * π / µ_0) ** 0.5
+ [gaussian_magnetic_flux] -> [magnetic_flux]: value / (4 * π / µ_0) ** 0.5
+ [magnetic_flux] -> [gaussian_magnetic_flux]: value * (4 * π / µ_0) ** 0.5
+ [gaussian_magnetic_field_strength] -> [magnetic_field_strength]: value / (4 * π * µ_0) ** 0.5
+ [magnetic_field_strength] -> [gaussian_magnetic_field_strength]: value * (4 * π * µ_0) ** 0.5
+ [gaussian_magnetic_dipole] -> [magnetic_dipole]: value * (4 * π / µ_0) ** 0.5
+ [magnetic_dipole] -> [gaussian_magnetic_dipole]: value / (4 * π / µ_0) ** 0.5
+ [gaussian_resistance] -> [resistance]: value * k_C
+ [resistance] -> [gaussian_resistance]: value / k_C
+ [gaussian_resistivity] -> [resistivity]: value * k_C
+ [resistivity] -> [gaussian_resistivity]: value / k_C
+ [gaussian_capacitance] -> [capacitance]: value / k_C
+ [capacitance] -> [gaussian_capacitance]: value * k_C
+ [gaussian_inductance] -> [inductance]: value * k_C
+ [inductance] -> [gaussian_inductance]: value / k_C
+ [gaussian_conductance] -> [conductance]: value / k_C
+ [conductance] -> [gaussian_conductance]: value * k_C
+@end
+
+# === ESU system of units ===
+# (where different from Gaussian)
+# See note for Gaussian system too
+@group ESU using Gaussian
+ statweber = statvolt * second = statWb
+ stattesla = statweber / centimeter ** 2 = statT
+ stathenry = statweber / statampere = statH
+@end
+[esu_charge] = [length] ** 1.5 * [mass] ** 0.5 / [time]
+[esu_current] = [esu_charge] / [time]
+[esu_electric_potential] = [esu_charge] / [length]
+[esu_magnetic_flux] = [esu_electric_potential] * [time]
+[esu_magnetic_field] = [esu_magnetic_flux] / [area]
+[esu_magnetic_field_strength] = [esu_current] / [length]
+[esu_magnetic_dipole] = [esu_current] * [area]
+@context ESU = esu
+ [esu_magnetic_field] -> [magnetic_field]: value * k_C ** 0.5
+ [magnetic_field] -> [esu_magnetic_field]: value / k_C ** 0.5
+ [esu_magnetic_flux] -> [magnetic_flux]: value * k_C ** 0.5
+ [magnetic_flux] -> [esu_magnetic_flux]: value / k_C ** 0.5
+ [esu_magnetic_field_strength] -> [magnetic_field_strength]: value / (4 * π / ε_0) ** 0.5
+ [magnetic_field_strength] -> [esu_magnetic_field_strength]: value * (4 * π / ε_0) ** 0.5
+ [esu_magnetic_dipole] -> [magnetic_dipole]: value / k_C ** 0.5
+ [magnetic_dipole] -> [esu_magnetic_dipole]: value * k_C ** 0.5
+@end
+
+
+#### CONVERSION CONTEXTS ####
+
+@context(n=1) spectroscopy = sp
+ # n index of refraction of the medium.
+ [length] <-> [frequency]: speed_of_light / n / value
+ [frequency] -> [energy]: planck_constant * value
+ [energy] -> [frequency]: value / planck_constant
+ # allow wavenumber / kayser
+ [wavenumber] <-> [length]: 1 / value
+@end
+
+@context boltzmann
+ [temperature] -> [energy]: boltzmann_constant * value
+ [energy] -> [temperature]: value / boltzmann_constant
+@end
+
+@context energy
+ [energy] -> [energy] / [substance]: value * N_A
+ [energy] / [substance] -> [energy]: value / N_A
+ [energy] -> [mass]: value / c ** 2
+ [mass] -> [energy]: value * c ** 2
+@end
+
+@context(mw=0,volume=0,solvent_mass=0) chemistry = chem
+ # mw is the molecular weight of the species
+ # volume is the volume of the solution
+ # solvent_mass is the mass of solvent in the solution
+
+ # moles -> mass require the molecular weight
+ [substance] -> [mass]: value * mw
+ [mass] -> [substance]: value / mw
+
+ # moles/volume -> mass/volume and moles/mass -> mass/mass
+ # require the molecular weight
+ [substance] / [volume] -> [mass] / [volume]: value * mw
+ [mass] / [volume] -> [substance] / [volume]: value / mw
+ [substance] / [mass] -> [mass] / [mass]: value * mw
+ [mass] / [mass] -> [substance] / [mass]: value / mw
+
+ # moles/volume -> moles requires the solution volume
+ [substance] / [volume] -> [substance]: value * volume
+ [substance] -> [substance] / [volume]: value / volume
+
+ # moles/mass -> moles requires the solvent (usually water) mass
+ [substance] / [mass] -> [substance]: value * solvent_mass
+ [substance] -> [substance] / [mass]: value / solvent_mass
+
+ # moles/mass -> moles/volume require the solvent mass and the volume
+ [substance] / [mass] -> [substance]/[volume]: value * solvent_mass / volume
+ [substance] / [volume] -> [substance] / [mass]: value / solvent_mass * volume
+
+@end
+
+@context textile
+ # Allow switching between Direct count system (i.e. tex) and
+ # Indirect count system (i.e. Ne, Nm)
+ [mass] / [length] <-> [length] / [mass]: 1 / value
+@end
+
+
+#### SYSTEMS OF UNITS ####
+
+@system SI
+ second
+ meter
+ kilogram
+ ampere
+ kelvin
+ mole
+ candela
@end
@system mks using international
@@ -467,12 +808,30 @@ stere = meter ** 3
second
@end
-@system cgs using international
+@system cgs using international, Gaussian, ESU
centimeter
gram
second
@end
+@system atomic using international
+ # based on unit m_e, e, hbar, k_C, k
+ bohr: meter
+ electron_mass: gram
+ atomic_unit_of_time: second
+ atomic_unit_of_current: ampere
+ atomic_unit_of_temperature: kelvin
+@end
+
+@system Planck using international
+ # based on unit c, gravitational_constant, hbar, k_C, k
+ planck_length: meter
+ planck_mass: gram
+ planck_time: second
+ planck_current: ampere
+ planck_temperature: kelvin
+@end
+
@system imperial using ImperialVolume, USCSLengthInternational, AvoirdupoisUK
yard
pound
diff --git a/pint/definitions.py b/pint/definitions.py
index 4ed39ea..428aeae 100644
--- a/pint/definitions.py
+++ b/pint/definitions.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
pint.definitions
- ~~~~~~~~~
+ ~~~~~~~~~~~~~~~~
Functions and classes related to unit definitions.
@@ -52,9 +52,18 @@ class Definition(object):
value.decode('utf-8')
except UnicodeEncodeError:
result.remove(value)
- value, aliases = result[0], tuple(result[1:])
+
+ # @alias name = alias1 = alias2 = ...
+ if name.startswith("@alias "):
+ name = name[len("@alias "):].lstrip()
+ return AliasDefinition(name, tuple(result))
+
+ value, aliases = result[0], tuple([x for x in result[1:] if x != ''])
symbol, aliases = (aliases[0], aliases[1:]) if aliases else (None,
aliases)
+ if symbol == '_':
+ symbol = None
+ aliases = tuple([x for x in aliases if x != '_'])
if name.startswith('['):
return DimensionDefinition(name, symbol, aliases, value)
@@ -80,6 +89,10 @@ class Definition(object):
def aliases(self):
return self._aliases
+ def add_aliases(self, *alias):
+ alias = tuple(a for a in alias if a not in self._aliases)
+ self._aliases = self._aliases + alias
+
@property
def converter(self):
return self._converter
@@ -123,10 +136,10 @@ class UnitDefinition(Definition):
modifiers = {}
converter = ParserHelper.from_string(converter)
- if all(_is_dim(key) for key in converter.keys()):
- self.is_base = True
- elif not any(_is_dim(key) for key in converter.keys()):
+ if not any(_is_dim(key) for key in converter.keys()):
self.is_base = False
+ elif all(_is_dim(key) for key in converter.keys()):
+ self.is_base = True
else:
raise ValueError('Cannot mix dimensions and units in the same definition. '
'Base units must be referenced only to dimensions. '
@@ -163,3 +176,12 @@ class DimensionDefinition(Definition):
super(DimensionDefinition, self).__init__(name, symbol, aliases,
converter=None)
+
+
+class AliasDefinition(Definition):
+ """Additional alias(es) for an already existing unit
+ """
+ def __init__(self, name, aliases):
+ super(AliasDefinition, self).__init__(
+ name=name, symbol=None, aliases=aliases, converter=None
+ )
diff --git a/pint/errors.py b/pint/errors.py
index 65ad56b..700d79c 100644
--- a/pint/errors.py
+++ b/pint/errors.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
pint.errors
- ~~~~~~~~~
+ ~~~~~~~~~~~
Functions and classes related to unit definitions and conversions.
@@ -109,6 +109,7 @@ class OffsetUnitCalculusError(ValueError):
def __str__(self):
msg = ("Ambiguous operation with offset unit (%s)." %
', '.join(['%s' % u for u in [self.units1, self.units2] if u])
+ + " See https://pint.readthedocs.io/en/latest/nonmult.html for guidance."
+ self.extra_msg)
return msg.format(self.units1, self.units2)
diff --git a/pint/formatting.py b/pint/formatting.py
index c71437a..efd68f7 100644
--- a/pint/formatting.py
+++ b/pint/formatting.py
@@ -187,14 +187,13 @@ def formatter(items, as_ratio=True, single_denominator=False,
# http://docs.python.org/2/library/string.html#format-specification-mini-language
# We also add uS for uncertainties.
_BASIC_TYPES = frozenset('bcdeEfFgGnosxX%uS')
-_KNOWN_TYPES = frozenset(list(_FORMATS.keys()) + ['~'])
def _parse_spec(spec):
result = ''
for ch in reversed(spec):
if ch == '~' or ch in _BASIC_TYPES:
continue
- elif ch in _KNOWN_TYPES:
+ elif ch in list(_FORMATS.keys()) + ['~']:
if result:
raise ValueError("expected ':' after format specifier")
else:
@@ -272,7 +271,7 @@ def siunitx_format_unit(units):
def remove_custom_flags(spec):
- for flag in _KNOWN_TYPES:
+ for flag in list(_FORMATS.keys()) + ['~']:
if flag:
spec = spec.replace(flag, '')
return spec
diff --git a/pint/matplotlib.py b/pint/matplotlib.py
index bda251d..4c8af68 100644
--- a/pint/matplotlib.py
+++ b/pint/matplotlib.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
pint.matplotlib
- ~~~~~~~~~
+ ~~~~~~~~~~~~~~~
Functions and classes related to working with Matplotlib's support
for plotting with units.
@@ -13,6 +13,8 @@ from __future__ import absolute_import
import matplotlib.units
+from .util import iterable, sized
+
class PintAxisInfo(matplotlib.units.AxisInfo):
"""Support default axis and tick labeling and default limits."""
@@ -31,7 +33,7 @@ class PintConverter(matplotlib.units.ConversionInterface):
def convert(self, value, unit, axis):
"""Convert :`Quantity` instances for matplotlib to use."""
- if isinstance(value, (tuple, list)):
+ if iterable(value):
return [self._convert_value(v, unit, axis) for v in value]
else:
return self._convert_value(value, unit, axis)
@@ -51,6 +53,8 @@ class PintConverter(matplotlib.units.ConversionInterface):
@staticmethod
def default_units(x, axis):
"""Get the default unit to use for the given combination of unit and axis."""
+ if iterable(x) and sized(x):
+ return getattr(x[0], 'units', None)
return getattr(x, 'units', None)
diff --git a/pint/measurement.py b/pint/measurement.py
index 82ca5a4..3fc40e1 100644
--- a/pint/measurement.py
+++ b/pint/measurement.py
@@ -11,17 +11,18 @@ from __future__ import division, unicode_literals, print_function, absolute_impo
from .compat import ufloat
from .formatting import _FORMATS, siunitx_format_unit
+from .quantity import Quantity
MISSING = object()
-class _Measurement(object):
+class Measurement(Quantity):
"""Implements a class to describe a quantity with uncertainty.
- :param value: The most likely value of the measurement.
- :type value: Quantity or Number
- :param error: The error or uncertainty of the measurement.
- :type error: Quantity or Number
+ :param value: The expected value of the measurement
+ :type value: pint.Quantity or any numeric type
+ :param error: The error or uncertainty of the measurement
+ :type error: pint.Quantity or any numeric type
"""
def __new__(cls, value, error, units=MISSING):
@@ -29,28 +30,28 @@ class _Measurement(object):
try:
value, units = value.magnitude, value.units
except AttributeError:
- #if called with two arguments and the first looks like a ufloat
+ # if called with two arguments and the first looks like a ufloat
# then assume the second argument is the units, keep value intact
- if hasattr(value,"nominal_value"):
+ if hasattr(value, "nominal_value"):
units = error
- error = MISSING #used for check below
+ error = MISSING # used for check below
else:
units = ''
try:
error = error.to(units).magnitude
except AttributeError:
pass
-
+
if error is MISSING:
mag = value
elif error < 0:
raise ValueError('The magnitude of the error cannot be negative'.format(value, error))
else:
- mag = ufloat(value,error)
-
- inst = super(_Measurement, cls).__new__(cls, mag, units)
+ mag = ufloat(value, error)
+
+ inst = super(Measurement, cls).__new__(cls, mag, units)
return inst
-
+
@property
def value(self):
return self._REGISTRY.Quantity(self.magnitude.nominal_value, self.units)
@@ -63,6 +64,12 @@ class _Measurement(object):
def rel(self):
return float(abs(self.magnitude.std_dev / self.magnitude.nominal_value))
+ def __reduce__(self):
+ # See notes in Quantity.__reduce__
+ from . import _unpickle
+
+ return _unpickle, (Measurement, self.magnitude, self._units)
+
def __repr__(self):
return "<Measurement({0:.2f}, {1:.2f}, {2})>".format(self.magnitude.nominal_value,
self.magnitude.std_dev,
@@ -127,19 +134,20 @@ class _Measurement(object):
return pars.format(mag) + space + format(self.units, spec)
-def build_measurement_class(registry, force_ndarray=False):
+_Measurement = Measurement
+
+
+def build_measurement_class(registry):
if ufloat is None:
class Measurement(object):
+ _REGISTRY = registry
def __init__(self, *args):
raise RuntimeError("Pint requires the 'uncertainties' package to create a Measurement object.")
else:
- class Measurement(_Measurement, registry.Quantity):
- pass
-
- Measurement._REGISTRY = registry
- Measurement.force_ndarray = force_ndarray
+ class Measurement(_Measurement):
+ _REGISTRY = registry
return Measurement
diff --git a/pint/quantity.py b/pint/quantity.py
index 3373552..f093d08 100644
--- a/pint/quantity.py
+++ b/pint/quantity.py
@@ -31,6 +31,7 @@ from .util import (PrettyIPython, logger, UnitsContainer, SharedRegistryObject,
fix_str_conversions)
from pint.compat import Loc
+
def _eq(first, second, check_all):
"""Comparison of scalars and arrays
"""
@@ -64,6 +65,7 @@ def ireduce_dimensions(f):
return result
return wrapped
+
def check_implemented(f):
def wrapped(self, *args, **kwargs):
other=args[0]
@@ -94,22 +96,32 @@ def printoptions(*args, **kwargs):
@fix_str_conversions
-class _Quantity(PrettyIPython, SharedRegistryObject):
+class Quantity(PrettyIPython, SharedRegistryObject):
"""Implements a class to describe a physical quantity:
the product of a numerical value and a unit of measurement.
- :param value: value of the physical quantity to be created.
- :type value: str, Quantity or any numeric type.
- :param units: units of the physical quantity to be created.
- :type units: UnitsContainer, str or Quantity.
+ :param value: value of the physical quantity to be created
+ :type value: str, pint.Quantity or any numeric type
+ :param units: units of the physical quantity to be created
+ :type units: UnitsContainer, str or pint.Quantity
"""
#: Default formatting string.
default_format = ''
+ @property
+ def force_ndarray(self):
+ return self._REGISTRY.force_ndarray
+
def __reduce__(self):
- from . import _build_quantity
- return _build_quantity, (self.magnitude, self._units)
+ """Allow pickling quantities. Since UnitRegistries are not pickled, upon
+ unpickling the new object is always attached to the application registry.
+ """
+ from . import _unpickle
+
+ # Note: type(self) would be a mistake as subclasses built by
+ # build_quantity_class can't be pickled
+ return _unpickle, (Quantity, self.magnitude, self._units)
def __new__(cls, value, units=None):
if units is None:
@@ -117,29 +129,30 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
if value == '':
raise ValueError('Expression to parse as Quantity cannot '
'be an empty string.')
- inst = cls._REGISTRY.parse_expression(value)
+ ureg = SharedRegistryObject.__new__(cls)._REGISTRY
+ inst = ureg.parse_expression(value)
return cls.__new__(cls, inst)
elif isinstance(value, cls):
inst = copy.copy(value)
else:
- inst = object.__new__(cls)
+ inst = SharedRegistryObject.__new__(cls)
inst._magnitude = _to_magnitude(value, inst.force_ndarray)
inst._units = UnitsContainer()
elif isinstance(units, (UnitsContainer, UnitDefinition)):
- inst = object.__new__(cls)
+ inst = SharedRegistryObject.__new__(cls)
inst._magnitude = _to_magnitude(value, inst.force_ndarray)
inst._units = units
elif isinstance(units, string_types):
- inst = object.__new__(cls)
+ inst = SharedRegistryObject.__new__(cls)
inst._magnitude = _to_magnitude(value, inst.force_ndarray)
inst._units = inst._REGISTRY.parse_units(units)._units
elif isinstance(units, SharedRegistryObject):
- if isinstance(units, _Quantity) and units.magnitude != 1:
+ if isinstance(units, Quantity) and units.magnitude != 1:
inst = copy.copy(units)
logger.warning('Creating new Quantity using a non unity '
'Quantity as units.')
else:
- inst = object.__new__(cls)
+ inst = SharedRegistryObject.__new__(cls)
inst._units = units._units
inst._magnitude = _to_magnitude(value, inst.force_ndarray)
else:
@@ -148,22 +161,24 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
inst.__used = False
inst.__handling = None
- # Only instances where the magnitude is iterable should have __iter__()
- if hasattr(inst._magnitude,"__iter__"):
- inst.__iter__ = cls._iter
+
return inst
- def _iter(self):
- """
- Will be become __iter__() for instances with iterable magnitudes
- """
- # # Allow exception to propagate in case of non-iterable magnitude
- it_mag = iter(self.magnitude)
- return iter((self.__class__(mag, self._units) for mag in it_mag))
@property
def debug_used(self):
return self.__used
+ def __iter__(self):
+ # Make sure that, if self.magnitude is not iterable, we raise TypeError as soon as one
+ # calls iter(self) without waiting for the first element to be drawn from the iterator
+ it_magnitude = iter(self.magnitude)
+
+ def it_outer():
+ for element in it_magnitude:
+ yield self.__class__(element, self._units)
+
+ return it_outer()
+
def __copy__(self):
ret = self.__class__(copy.copy(self._magnitude), self._units)
ret.__used = self.__used
@@ -249,7 +264,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
def _repr_pretty_(self, p, cycle):
if cycle:
- super(_Quantity, self)._repr_pretty_(p, cycle)
+ super(Quantity, self)._repr_pretty_(p, cycle)
else:
p.pretty(self.magnitude)
p.text(" ")
@@ -289,7 +304,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
"""Quantity's magnitude expressed in particular units.
:param units: destination units
- :type units: Quantity, str or dict
+ :type units: pint.Quantity, str or dict
"""
return self.to(units).magnitude
@@ -297,7 +312,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
def units(self):
"""Quantity's units. Long form for `u`
- :rtype: UnitContainer
+ :rtype: UnitsContainer
"""
return self._REGISTRY.Unit(self._units)
@@ -305,7 +320,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
def u(self):
"""Quantity's units. Short form for `units`
- :rtype: UnitContainer
+ :rtype: UnitsContainer
"""
return self._REGISTRY.Unit(self._units)
@@ -349,10 +364,10 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
If units is not specified and list is empty, the unit cannot be determined
and a ValueError is raised.
- :param quant_list: list of Quantities
- :type quant_list: list of Quantity
- :param units: units of the physical quantity to be created.
- :type units: UnitsContainer, str or Quantity.
+ :param quant_list: list of pint.Quantity
+ :type quant_list: list of pint.Quantity
+ :param units: units of the physical quantity to be created
+ :type units: UnitsContainer, str or pint.Quantity
"""
return cls.from_sequence(quant_list, units=units)
@@ -364,10 +379,10 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
If units is not specified and sequence is empty, the unit cannot be determined
and a ValueError is raised.
- :param seq: sequence of Quantities
- :type seq: sequence of Quantity
- :param units: units of the physical quantity to be created.
- :type units: UnitsContainer, str or Quantity.
+ :param seq: sequence of pint.Quantity
+ :type seq: sequence of pint.Quantity
+ :param units: units of the physical quantity to be created
+ :type units: UnitsContainer, str or pint.Quantity
"""
len_seq = len(seq)
@@ -419,7 +434,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
"""Inplace rescale to different units.
:param other: destination units.
- :type other: Quantity, str or dict
+ :type other: pint.Quantity, str or dict
"""
other = to_units_container(other, self._REGISTRY)
@@ -433,7 +448,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
"""Return Quantity rescaled to different units.
:param other: destination units.
- :type other: Quantity, str or dict
+ :type other: pint.Quantity, str or dict
"""
other = to_units_container(other, self._REGISTRY)
@@ -481,7 +496,6 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
return self.__class__(magnitude, other)
-
def ito_reduced_units(self):
"""Return Quantity scaled in place to reduced units, i.e. one unit per
dimension. This will not reduce compound units (intentionally), nor
@@ -517,7 +531,6 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
newq.ito_reduced_units()
return newq
-
def to_compact(self, unit=None):
"""Return Quantity rescaled to compact, human-readable units.
@@ -608,7 +621,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
"""Perform addition or subtraction operation in-place and return the result.
:param other: object to be added to / subtracted from self
- :type other: Quantity or any type accepted by :func:`_to_magnitude`
+ :type other: pint.Quantity or any type accepted by :func:`_to_magnitude`
:param op: operator function (e.g. operator.add, operator.isub)
:type op: function
"""
@@ -707,7 +720,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
"""Perform addition or subtraction operation and return the result.
:param other: object to be added to / subtracted from self
- :type other: Quantity or any type accepted by :func:`_to_magnitude`
+ :type other: pint.Quantity or any type accepted by :func:`_to_magnitude`
:param op: operator function (e.g. operator.add, operator.isub)
:type op: function
"""
@@ -841,7 +854,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
result.
:param other: object to be multiplied/divided with self
- :type other: Quantity or any type accepted by :func:`_to_magnitude`
+ :type other: pint.Quantity or any type accepted by :func:`_to_magnitude`
:param magnitude_op: operator function to perform on the magnitudes
(e.g. operator.mul)
:type magnitude_op: function
@@ -899,7 +912,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
"""Perform multiplication or division operation and return the result.
:param other: object to be multiplied/divided with self
- :type other: Quantity or any type accepted by :func:`_to_magnitude`
+ :type other: pint.Quantity or any type accepted by :func:`_to_magnitude`
:param magnitude_op: operator function to perform on the magnitudes
(e.g. operator.mul)
:type magnitude_op: function
@@ -1197,7 +1210,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
def __eq__(self, other):
# We compare to the base class of Quantity because
# each Quantity class is unique.
- if not isinstance(other, _Quantity):
+ if not isinstance(other, Quantity):
if _eq(other, 0, True):
# Handle the special case in which we compare to zero
# (or an array of zeros)
@@ -1439,7 +1452,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
# Attributes starting with `__array_` are common attributes of NumPy ndarray.
# They are requested by numpy functions.
if item.startswith('__array_'):
- warnings.warn("The unit of the quantity is stripped.", UnitStrippedWarning)
+ warnings.warn("The unit of the quantity is stripped.", UnitStrippedWarning, stacklevel=2)
if isinstance(self._magnitude, ndarray):
return getattr(self._magnitude, item)
else:
@@ -1715,7 +1728,7 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
def _ok_for_muldiv(self, no_offset_units=None):
"""Checks if Quantity object can be multiplied or divided
- :q: quantity object that is checked
+ :q: pint.Quantity object that is checked
:no_offset_units: number of offset units in q
"""
is_ok = True
@@ -1737,13 +1750,11 @@ class _Quantity(PrettyIPython, SharedRegistryObject):
return datetime.timedelta(microseconds=self.to('microseconds').magnitude)
+_Quantity = Quantity
-def build_quantity_class(registry, force_ndarray=False):
+def build_quantity_class(registry):
class Quantity(_Quantity):
- pass
-
- Quantity._REGISTRY = registry
- Quantity.force_ndarray = force_ndarray
+ _REGISTRY = registry
return Quantity
diff --git a/pint/registry.py b/pint/registry.py
index ad1e2c7..e3eafbb 100644
--- a/pint/registry.py
+++ b/pint/registry.py
@@ -12,11 +12,13 @@
- NonMultiplicativeRegistry: Conversion between non multiplicative (offset) units.
(e.g. Temperature)
+
* Inherits from BaseRegistry
- ContextRegisty: Conversion between units with different dimenstions according
to previously established relations (contexts).
(e.g. in the spectroscopy, conversion between frequency and energy is possible)
+
* Inherits from BaseRegistry
- SystemRegistry: Group unit and changing of base units.
@@ -32,6 +34,7 @@
from __future__ import division, unicode_literals, print_function, absolute_import
+import copy
import os
import re
import math
@@ -47,14 +50,15 @@ from tokenize import NUMBER, NAME
from . import registry_helpers
from .context import Context, ContextChain
-from .util import (logger, pi_theorem, solve_dependencies, ParserHelper,
+from .util import (getattr_maybe_raise,
+ logger, pi_theorem, solve_dependencies, ParserHelper,
string_preprocessor, find_connected_nodes,
find_shortest_path, UnitsContainer, _is_dim,
to_units_container, SourceIterator)
from .compat import tokenizer, string_types, meta
from .definitions import (Definition, UnitDefinition, PrefixDefinition,
- DimensionDefinition)
+ DimensionDefinition, AliasDefinition)
from .converters import ScaleConverter
from .errors import (DimensionalityError, UndefinedUnitError,
DefinitionSyntaxError, RedefinitionError)
@@ -106,7 +110,7 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
:param filename: path of the units definition file to load or line iterable object.
Empty to load the default definition file.
None to leave the UnitRegistry empty.
- :type filename: str | None
+ :type filename: str or None
:param force_ndarray: convert any input, scalar or not to a numpy.ndarray.
:param on_redefinition: action to take in case a unit is redefined.
'warn', 'raise', 'ignore'
@@ -130,17 +134,10 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
def __init__(self, filename='', force_ndarray=False, on_redefinition='warn', auto_reduce_dimensions=False):
self._register_parsers()
-
- from .unit import build_unit_class
- self.Unit = build_unit_class(self)
-
- from .quantity import build_quantity_class
- self.Quantity = build_quantity_class(self, force_ndarray)
-
- from .measurement import build_measurement_class
- self.Measurement = build_measurement_class(self, force_ndarray)
+ self._init_dynamic_classes()
self._filename = filename
+ self.force_ndarray = force_ndarray
#: Action to take in case a unit is redefined. 'warn', 'raise', 'ignore'
self._on_redefinition = on_redefinition
@@ -174,16 +171,28 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
self._initialized = False
+ def _init_dynamic_classes(self):
+ """Generate subclasses on the fly and attach them to self
+ """
+ from .unit import build_unit_class
+ self.Unit = build_unit_class(self)
+
+ from .quantity import build_quantity_class
+ self.Quantity = build_quantity_class(self)
+
+ from .measurement import build_measurement_class
+ self.Measurement = build_measurement_class(self)
+
def _after_init(self):
"""This should be called after all __init__
"""
+ self.define(UnitDefinition('pi', 'π', (), ScaleConverter(math.pi)))
+
if self._filename == '':
self.load_definitions('default_en.txt', True)
elif self._filename is not None:
self.load_definitions(self._filename)
- self.define(UnitDefinition('pi', 'π', (), ScaleConverter(math.pi)))
-
self._build_cache()
self._initialized = True
@@ -200,9 +209,14 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
k, v = part.split('=')
self._defaults[k.strip()] = v.strip()
+ def __deepcopy__(self, memo):
+ new = object.__new__(type(self))
+ new.__dict__ = copy.deepcopy(self.__dict__, memo)
+ new._init_dynamic_classes()
+ return new
+
def __getattr__(self, item):
- if item[0] == '_':
- return super(BaseRegistry, self).__getattribute__(item)
+ getattr_maybe_raise(self, item)
return self.Unit(item)
def __getitem__(self, item):
@@ -228,7 +242,7 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
"""Add unit to the registry.
:param definition: a dimension, unit or prefix definition.
- :type definition: str | Definition
+ :type definition: str or Definition
"""
if isinstance(definition, string_types):
@@ -269,6 +283,11 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
elif isinstance(definition, PrefixDefinition):
d, di = self._prefixes, None
+ elif isinstance(definition, AliasDefinition):
+ d, di = self._units, self._units_casei
+ self._define_alias(definition, d, di)
+ return d[definition.name], d, di
+
else:
raise TypeError('{} is not a valid definition.'.format(definition))
@@ -285,7 +304,8 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
else:
d_symbol = None
- d_aliases = tuple('Δ' + alias for alias in definition.aliases)
+ d_aliases = tuple('Δ' + alias for alias in definition.aliases) + \
+ tuple('delta_' + alias for alias in definition.aliases)
d_reference = UnitsContainer(dict((ref, value)
for ref, value in definition.reference.items()))
@@ -330,6 +350,13 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
if casei_unit_dict is not None:
casei_unit_dict[key.lower()].add(key)
+ def _define_alias(self, definition, unit_dict, casei_unit_dict):
+ unit = unit_dict[definition.name]
+ unit.add_aliases(*definition.aliases)
+ for alias in unit.aliases:
+ unit_dict[alias] = unit
+ casei_unit_dict[alias.lower()].add(alias)
+
def _register_parser(self, prefix, parserfunc):
"""Register a loader for a given @ directive..
@@ -372,7 +399,7 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
ifile = SourceIterator(file)
for no, line in ifile:
- if line and line[0] == '@':
+ if line.startswith('@') and not line.startswith('@alias'):
if line.startswith('@import'):
if is_resource:
path = line[7:].strip()
@@ -705,9 +732,9 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
:param value: value
:param src: source units.
- :type src: Quantity or str
+ :type src: pint.Quantity or str
:param dst: destination units.
- :type dst: Quantity or str
+ :type dst: pint.Quantity or str
:return: converted value
"""
@@ -847,9 +874,7 @@ class BaseRegistry(meta.with_metaclass(_Meta)):
token_type = token[0]
token_text = token[1]
if token_type == NAME:
- if token_text == 'pi':
- return self.Quantity(math.pi)
- elif token_text == 'dimensionless':
+ if token_text == 'dimensionless':
return 1 * self.dimensionless
elif token_text in values:
return self.Quantity(values[token_text])
@@ -1078,6 +1103,8 @@ class ContextRegistry(BaseRegistry):
Notice that this method will NOT enable the context. Use `enable_contexts`.
"""
+ if not context.name:
+ raise ValueError("Can't add unnamed context to registry")
if context.name in self._contexts:
logger.warning('The name %s was already registered for another context.',
context.name)
@@ -1311,12 +1338,14 @@ class SystemRegistry(BaseRegistry):
#: Map group name to group.
#: :type: dict[ str | Group]
self._groups = {}
- self.Group = systems.build_group_class(self)
self._groups['root'] = self.Group('root')
- self.System = systems.build_system_class(self)
-
self._default_system = system
+ def _init_dynamic_classes(self):
+ super(SystemRegistry, self)._init_dynamic_classes()
+ self.Group = systems.build_group_class(self)
+ self.System = systems.build_system_class(self)
+
def _after_init(self):
"""After init function
diff --git a/pint/registry_helpers.py b/pint/registry_helpers.py
index ed993ea..74f16d3 100644
--- a/pint/registry_helpers.py
+++ b/pint/registry_helpers.py
@@ -220,8 +220,8 @@ def check(ureg, *args):
:param ureg: a UnitRegistry instance.
:param args: iterable of input units.
:return: the wrapped function.
- :raises:
- :class:`DimensionalityError` if the parameters don't match dimensions
+ :raises pint.DimensionalityError:
+ if the parameters don't match dimensions
"""
dimensions = [ureg.get_dimensionality(dim) if dim is not None else None for dim in args]
diff --git a/pint/systems.py b/pint/systems.py
index 47b1ed1..3c56660 100644
--- a/pint/systems.py
+++ b/pint/systems.py
@@ -15,12 +15,12 @@ import re
from .definitions import Definition, UnitDefinition
from .errors import DefinitionSyntaxError, RedefinitionError
-from .util import to_units_container, SharedRegistryObject, SourceIterator, logger
+from .util import to_units_container, getattr_maybe_raise, SharedRegistryObject, SourceIterator, logger
from .babel_names import _babel_systems
from pint.compat import Loc
-class _Group(SharedRegistryObject):
+class Group(SharedRegistryObject):
"""A group is a set of units.
Units can be added directly or by including other groups.
@@ -30,14 +30,13 @@ class _Group(SharedRegistryObject):
The group belongs to one Registry.
- It can be specified in the definition file as:
+ It can be specified in the definition file as::
@group <name> [using <group 1>, ..., <group N>]
<definition 1>
...
<definition N>
@end
-
"""
#: Regex to match the header parts of a definition.
@@ -232,10 +231,11 @@ class _Group(SharedRegistryObject):
return grp
def __getattr__(self, item):
+ getattr_maybe_raise(self, item)
return self._REGISTRY
-class _System(SharedRegistryObject):
+class System(SharedRegistryObject):
"""A system is a Group plus a set of base units.
Members are computed dynamically, that is if a unit is added to a group X
@@ -243,7 +243,7 @@ class _System(SharedRegistryObject):
The System belongs to one Registry.
- It can be specified in the definition file as:
+ It can be specified in the definition file as::
@system <name> [using <group 1>, ..., <group N>]
<rule 1>
@@ -297,6 +297,7 @@ class _System(SharedRegistryObject):
return list(self.members)
def __getattr__(self, item):
+ getattr_maybe_raise(self, item)
u = getattr(self._REGISTRY, self.name + '_' + item, None)
if u is not None:
return u
@@ -440,22 +441,23 @@ class Lister(object):
return list(self.d.keys())
def __getattr__(self, item):
+ getattr_maybe_raise(self, item)
return self.d[item]
-def build_group_class(registry):
+_Group = Group
+_System = System
+
+def build_group_class(registry):
class Group(_Group):
- pass
+ _REGISTRY = registry
- Group._REGISTRY = registry
return Group
def build_system_class(registry):
-
class System(_System):
- pass
+ _REGISTRY = registry
- System._REGISTRY = registry
return System
diff --git a/pint/testsuite/__init__.py b/pint/testsuite/__init__.py
index 4b02bc8..2f50544 100644
--- a/pint/testsuite/__init__.py
+++ b/pint/testsuite/__init__.py
@@ -5,15 +5,13 @@ from __future__ import division, unicode_literals, print_function, absolute_impo
import doctest
import logging
import os
-import sys
import unittest
from contextlib import contextmanager
from pint.compat import ndarray, np
-from pint import logger, UnitRegistry
-from pint.quantity import _Quantity
+from pint import logger, UnitRegistry, Quantity
from pint.testsuite.helpers import PintOutputChecker
from logging.handlers import BufferingHandler
@@ -79,15 +77,15 @@ class QuantityTestCase(BaseTestCase):
cls.U_ = cls.ureg.Unit
def _get_comparable_magnitudes(self, first, second, msg):
- if isinstance(first, _Quantity) and isinstance(second, _Quantity):
+ if isinstance(first, Quantity) and isinstance(second, Quantity):
second = second.to(first)
self.assertEqual(first.units, second.units, msg=msg + ' Units are not equal.')
m1, m2 = first.magnitude, second.magnitude
- elif isinstance(first, _Quantity):
+ elif isinstance(first, Quantity):
self.assertTrue(first.dimensionless, msg=msg + ' The first is not dimensionless.')
first = first.to('')
m1, m2 = first.magnitude, second
- elif isinstance(second, _Quantity):
+ elif isinstance(second, Quantity):
self.assertTrue(second.dimensionless, msg=msg + ' The second is not dimensionless.')
second = second.to('')
m1, m2 = first, second.magnitude
diff --git a/pint/testsuite/parameterized.py b/pint/testsuite/parameterized.py
index 9b92037..7c45915 100644
--- a/pint/testsuite/parameterized.py
+++ b/pint/testsuite/parameterized.py
@@ -32,6 +32,11 @@ from functools import wraps
import collections
import unittest
+try:
+ from collections.abc import Callable
+except ImportError:
+ from collections import Callable
+
def add_metaclass(metaclass):
"""Class decorator for creating a class with a metaclass."""
def wrapper(cls):
@@ -69,7 +74,7 @@ class ParameterizedTestCaseMetaClass(type):
new_class_dict = {}
for attr_name, attr_value in list(class_dict.items()):
- if isinstance(attr_value, collections.Callable) and hasattr(attr_value, 'param_names'):
+ if isinstance(attr_value, Callable) and hasattr(attr_value, 'param_names'):
# print("Processing attr_name = %r; attr_value = %r" % (
# attr_name, attr_value))
diff --git a/pint/testsuite/test_application_registry.py b/pint/testsuite/test_application_registry.py
new file mode 100644
index 0000000..f80162c
--- /dev/null
+++ b/pint/testsuite/test_application_registry.py
@@ -0,0 +1,233 @@
+"""Tests for global UnitRegistry, Unit, and Quantity
+"""
+import pickle
+
+from pint import (
+ Measurement,
+ Quantity,
+ UndefinedUnitError,
+ Unit,
+ UnitRegistry,
+ get_application_registry,
+ set_application_registry,
+)
+
+from pint.testsuite import BaseTestCase
+from pint.testsuite.helpers import requires_uncertainties
+
+
+class TestDefaultApplicationRegistry(BaseTestCase):
+ def test_unit(self):
+ u = Unit("kg")
+ self.assertEqual(str(u), "kilogram")
+ u = pickle.loads(pickle.dumps(u))
+ self.assertEqual(str(u), "kilogram")
+
+ def test_quantity_1arg(self):
+ q = Quantity("123 kg")
+ self.assertEqual(str(q.units), "kilogram")
+ self.assertEqual(q.to("t").magnitude, 0.123)
+ q = pickle.loads(pickle.dumps(q))
+ self.assertEqual(str(q.units), "kilogram")
+ self.assertEqual(q.to("t").magnitude, 0.123)
+
+ def test_quantity_2args(self):
+ q = Quantity(123, "kg")
+ self.assertEqual(str(q.units), "kilogram")
+ self.assertEqual(q.to("t").magnitude, 0.123)
+ q = pickle.loads(pickle.dumps(q))
+ self.assertEqual(str(q.units), "kilogram")
+ self.assertEqual(q.to("t").magnitude, 0.123)
+
+ @requires_uncertainties()
+ def test_measurement_2args(self):
+ m = Measurement(Quantity(123, "kg"), Quantity(15, "kg"))
+ self.assertEqual(m.value.magnitude, 123)
+ self.assertEqual(m.error.magnitude, 15)
+ self.assertEqual(str(m.units), "kilogram")
+ m = pickle.loads(pickle.dumps(m))
+ self.assertEqual(m.value.magnitude, 123)
+ self.assertEqual(m.error.magnitude, 15)
+ self.assertEqual(str(m.units), "kilogram")
+
+ @requires_uncertainties()
+ def test_measurement_3args(self):
+ m = Measurement(123, 15, "kg")
+ self.assertEqual(m.value.magnitude, 123)
+ self.assertEqual(m.error.magnitude, 15)
+ self.assertEqual(str(m.units), "kilogram")
+ m = pickle.loads(pickle.dumps(m))
+ self.assertEqual(m.value.magnitude, 123)
+ self.assertEqual(m.error.magnitude, 15)
+ self.assertEqual(str(m.units), "kilogram")
+
+ def test_get_application_registry(self):
+ ureg = get_application_registry()
+ u = ureg.Unit("kg")
+ self.assertEqual(str(u), "kilogram")
+
+ def test_pickle_crash(self):
+ ureg = UnitRegistry(None)
+ ureg.define("foo = []")
+ q = ureg.Quantity(123, "foo")
+ b = pickle.dumps(q)
+ self.assertRaises(UndefinedUnitError, pickle.loads, b)
+ b = pickle.dumps(q.units)
+ self.assertRaises(UndefinedUnitError, pickle.loads, b)
+
+ @requires_uncertainties()
+ def test_pickle_crash_measurement(self):
+ ureg = UnitRegistry(None)
+ ureg.define("foo = []")
+ m = ureg.Quantity(123, "foo").plus_minus(10)
+ b = pickle.dumps(m)
+ self.assertRaises(UndefinedUnitError, pickle.loads, b)
+
+
+class TestCustomApplicationRegistry(BaseTestCase):
+ def setUp(self):
+ super(TestCustomApplicationRegistry, self).setUp()
+ self.ureg_bak = get_application_registry()
+ self.ureg = UnitRegistry(None)
+ self.ureg.define("foo = []")
+ self.ureg.define("bar = foo / 2")
+ set_application_registry(self.ureg)
+ assert get_application_registry() is self.ureg
+
+ def tearDown(self):
+ super(TestCustomApplicationRegistry, self).tearDown()
+ set_application_registry(self.ureg_bak)
+
+ def test_unit(self):
+ u = Unit("foo")
+ self.assertEqual(str(u), "foo")
+ u = pickle.loads(pickle.dumps(u))
+ self.assertEqual(str(u), "foo")
+
+ def test_quantity_1arg(self):
+ q = Quantity("123 foo")
+ self.assertEqual(str(q.units), "foo")
+ self.assertEqual(q.to("bar").magnitude, 246)
+ q = pickle.loads(pickle.dumps(q))
+ self.assertEqual(str(q.units), "foo")
+ self.assertEqual(q.to("bar").magnitude, 246)
+
+ def test_quantity_2args(self):
+ q = Quantity(123, "foo")
+ self.assertEqual(str(q.units), "foo")
+ self.assertEqual(q.to("bar").magnitude, 246)
+ q = pickle.loads(pickle.dumps(q))
+ self.assertEqual(str(q.units), "foo")
+ self.assertEqual(q.to("bar").magnitude, 246)
+
+ @requires_uncertainties()
+ def test_measurement_2args(self):
+ m = Measurement(Quantity(123, "foo"), Quantity(10, "bar"))
+ self.assertEqual(m.value.magnitude, 123)
+ self.assertEqual(m.error.magnitude, 5)
+ self.assertEqual(str(m.units), "foo")
+ m = pickle.loads(pickle.dumps(m))
+ self.assertEqual(m.value.magnitude, 123)
+ self.assertEqual(m.error.magnitude, 5)
+ self.assertEqual(str(m.units), "foo")
+
+ @requires_uncertainties()
+ def test_measurement_3args(self):
+ m = Measurement(123, 5, "foo")
+ self.assertEqual(m.value.magnitude, 123)
+ self.assertEqual(m.error.magnitude, 5)
+ self.assertEqual(str(m.units), "foo")
+ m = pickle.loads(pickle.dumps(m))
+ self.assertEqual(m.value.magnitude, 123)
+ self.assertEqual(m.error.magnitude, 5)
+ self.assertEqual(str(m.units), "foo")
+
+
+class TestSwapApplicationRegistry(BaseTestCase):
+ """Test that the constructors of Quantity, Unit, and Measurement capture
+ the registry that is set as the application registry at creation time
+ """
+
+ def setUp(self):
+ super(TestSwapApplicationRegistry, self).setUp()
+ self.ureg_bak = get_application_registry()
+ self.ureg1 = UnitRegistry(None)
+ self.ureg1.define("foo = [dim1]")
+ self.ureg1.define("bar = foo / 2")
+ self.ureg2 = UnitRegistry(None)
+ self.ureg2.define("foo = [dim2]")
+ self.ureg2.define("bar = foo / 3")
+
+ def tearDown(self):
+ set_application_registry(self.ureg_bak)
+
+ def test_quantity_1arg(self):
+ set_application_registry(self.ureg1)
+ q1 = Quantity("1 foo")
+ set_application_registry(self.ureg2)
+ q2 = Quantity("1 foo")
+ q3 = pickle.loads(pickle.dumps(q1))
+ assert q1.dimensionality == {"[dim1]": 1}
+ assert q2.dimensionality == {"[dim2]": 1}
+ assert q3.dimensionality == {"[dim2]": 1}
+ assert q1.to("bar").magnitude == 2
+ assert q2.to("bar").magnitude == 3
+ assert q3.to("bar").magnitude == 3
+
+ def test_quantity_2args(self):
+ set_application_registry(self.ureg1)
+ q1 = Quantity(1, "foo")
+ set_application_registry(self.ureg2)
+ q2 = Quantity(1, "foo")
+ q3 = pickle.loads(pickle.dumps(q1))
+ assert q1.dimensionality == {"[dim1]": 1}
+ assert q2.dimensionality == {"[dim2]": 1}
+ assert q3.dimensionality == {"[dim2]": 1}
+ assert q1.to("bar").magnitude == 2
+ assert q2.to("bar").magnitude == 3
+ assert q3.to("bar").magnitude == 3
+
+ def test_unit(self):
+ set_application_registry(self.ureg1)
+ u1 = Unit("bar")
+ set_application_registry(self.ureg2)
+ u2 = Unit("bar")
+ u3 = pickle.loads(pickle.dumps(u1))
+ assert u1.dimensionality == {"[dim1]": 1}
+ assert u2.dimensionality == {"[dim2]": 1}
+ assert u3.dimensionality == {"[dim2]": 1}
+
+ @requires_uncertainties()
+ def test_measurement_2args(self):
+ set_application_registry(self.ureg1)
+ m1 = Measurement(Quantity(10, "foo"), Quantity(1, "foo"))
+ set_application_registry(self.ureg2)
+ m2 = Measurement(Quantity(10, "foo"), Quantity(1, "foo"))
+ m3 = pickle.loads(pickle.dumps(m1))
+
+ assert m1.dimensionality == {"[dim1]": 1}
+ assert m2.dimensionality == {"[dim2]": 1}
+ assert m3.dimensionality == {"[dim2]": 1}
+ self.assertEqual(m1.to("bar").value.magnitude, 20)
+ self.assertEqual(m2.to("bar").value.magnitude, 30)
+ self.assertEqual(m3.to("bar").value.magnitude, 30)
+ self.assertEqual(m1.to("bar").error.magnitude, 2)
+ self.assertEqual(m2.to("bar").error.magnitude, 3)
+ self.assertEqual(m3.to("bar").error.magnitude, 3)
+
+ @requires_uncertainties()
+ def test_measurement_3args(self):
+ set_application_registry(self.ureg1)
+ m1 = Measurement(10, 1, "foo")
+ set_application_registry(self.ureg2)
+ m2 = Measurement(10, 1, "foo")
+ m3 = pickle.loads(pickle.dumps(m1))
+
+ assert m1.dimensionality == {"[dim1]": 1}
+ assert m2.dimensionality == {"[dim2]": 1}
+ self.assertEqual(m1.to("bar").value.magnitude, 20)
+ self.assertEqual(m2.to("bar").value.magnitude, 30)
+ self.assertEqual(m3.to("bar").value.magnitude, 30)
+ self.assertEqual(m1.to("bar").error.magnitude, 2)
+ self.assertEqual(m2.to("bar").error.magnitude, 3)
+ self.assertEqual(m3.to("bar").error.magnitude, 3)
diff --git a/pint/testsuite/test_contexts.py b/pint/testsuite/test_contexts.py
index 22805db..7766aa7 100644
--- a/pint/testsuite/test_contexts.py
+++ b/pint/testsuite/test_contexts.py
@@ -269,7 +269,6 @@ class TestContexts(QuantityTestCase):
self.assertFalse(ureg._active_ctx)
self.assertFalse(ureg._active_ctx.graph)
-
def test_one_context(self):
ureg = UnitRegistry()
@@ -288,7 +287,6 @@ class TestContexts(QuantityTestCase):
self.assertRaises(ValueError, q.to, 'Hz')
self.assertEqual(ureg.get_compatible_units(q), meter_units)
-
def test_multiple_context(self):
ureg = UnitRegistry()
@@ -308,7 +306,6 @@ class TestContexts(QuantityTestCase):
self.assertRaises(ValueError, q.to, 'Hz')
self.assertEqual(ureg.get_compatible_units(q), meter_units)
-
def test_nested_context(self):
ureg = UnitRegistry()
@@ -385,7 +382,6 @@ class TestContexts(QuantityTestCase):
self.assertRaises(TypeError, q.to, 'Hz')
ureg.disable_contexts(1)
-
def test_context_with_arg_def(self):
ureg = UnitRegistry()
@@ -421,7 +417,6 @@ class TestContexts(QuantityTestCase):
self.assertEqual(q.to('Hz'), s / 2)
self.assertRaises(ValueError, q.to, 'Hz')
-
def test_context_with_sharedarg_def(self):
ureg = UnitRegistry()
@@ -462,6 +457,44 @@ class TestContexts(QuantityTestCase):
with ureg.context('lc', n=6):
self.assertEqual(q.to('Hz'), s / 6)
+ def test_anonymous_context(self):
+ ureg = UnitRegistry()
+ c = Context()
+ c.add_transformation(
+ '[length]', '[time]',
+ lambda ureg, x: x / ureg("5 cm/s")
+ )
+ self.assertRaises(ValueError, ureg.add_context, c)
+
+ x = ureg("10 cm")
+ expect = ureg("2 s")
+ self.assertQuantityEqual(x.to("s", c), expect)
+
+ with ureg.context(c):
+ self.assertQuantityEqual(x.to("s"), expect)
+
+ ureg.enable_contexts(c)
+ self.assertQuantityEqual(x.to("s"), expect)
+ ureg.disable_contexts(1)
+ self.assertRaises(errors.DimensionalityError, x.to, "s")
+
+ # Multiple anonymous contexts
+ c2 = Context()
+ c2.add_transformation(
+ '[length]', '[time]',
+ lambda ureg, x: x / ureg("10 cm/s")
+ )
+ c2.add_transformation(
+ '[mass]', '[time]',
+ lambda ureg, x: x / ureg("10 kg/s")
+ )
+ with ureg.context(c2, c):
+ self.assertQuantityEqual(x.to("s"), expect)
+ # Transformations only in c2 are still working even if c takes priority
+ self.assertQuantityEqual(ureg("100 kg").to("s"), ureg("10 s"))
+ with ureg.context(c, c2):
+ self.assertQuantityEqual(x.to("s"), ureg("1 s"))
+
def _test_ctx(self, ctx):
ureg = UnitRegistry()
q = 500 * ureg.meter
@@ -629,7 +662,6 @@ class TestDefinedContexts(QuantityTestCase):
# assertAlmostEqualRelError converts second to first
self.assertQuantityAlmostEqual(b, a, rtol=0.01, msg=msg)
-
for a, b in itertools.product(eq, eq):
self.assertQuantityAlmostEqual(a.to(b.units, 'sp'), b, rtol=0.01)
diff --git a/pint/testsuite/test_definitions.py b/pint/testsuite/test_definitions.py
index 573ee21..8a66a4e 100644
--- a/pint/testsuite/test_definitions.py
+++ b/pint/testsuite/test_definitions.py
@@ -5,7 +5,7 @@ from __future__ import division, unicode_literals, print_function, absolute_impo
from pint.util import (UnitsContainer)
from pint.converters import (ScaleConverter, OffsetConverter)
from pint.definitions import (Definition, PrefixDefinition, UnitDefinition,
- DimensionDefinition)
+ DimensionDefinition, AliasDefinition)
from pint.testsuite import BaseTestCase
@@ -70,6 +70,16 @@ class TestDefinition(BaseTestCase):
self.assertEqual(x.converter.offset, 255.372222)
self.assertEqual(x.reference, UnitsContainer(kelvin=1))
+ x = Definition.from_string('turn = 6.28 * radian = _ = revolution = = cycle = _')
+ self.assertIsInstance(x, UnitDefinition)
+ self.assertEqual(x.name, 'turn')
+ self.assertEqual(x.aliases, ('revolution', 'cycle'))
+ self.assertEqual(x.symbol, 'turn')
+ self.assertFalse(x.is_base)
+ self.assertIsInstance(x.converter, ScaleConverter)
+ self.assertEqual(x.converter.scale, 6.28)
+ self.assertEqual(x.reference, UnitsContainer(radian=1))
+
def test_dimension_definition(self):
x = DimensionDefinition('[time]', '', (), converter='')
self.assertTrue(x.is_base)
@@ -78,3 +88,9 @@ class TestDefinition(BaseTestCase):
x = Definition.from_string('[speed] = [length]/[time]')
self.assertIsInstance(x, DimensionDefinition)
self.assertEqual(x.reference, UnitsContainer({'[length]': 1, '[time]': -1}))
+
+ def test_alias_definition(self):
+ x = Definition.from_string("@alias meter = metro = metr")
+ self.assertIsInstance(x, AliasDefinition)
+ self.assertEqual(x.name, "meter")
+ self.assertEqual(x.aliases, ("metro", "metr"))
diff --git a/pint/testsuite/test_errors.py b/pint/testsuite/test_errors.py
index 42f2ea5..b6537c1 100644
--- a/pint/testsuite/test_errors.py
+++ b/pint/testsuite/test_errors.py
@@ -2,9 +2,10 @@
from __future__ import division, unicode_literals, print_function, absolute_import
-from pint.errors import DimensionalityError, UndefinedUnitError
+from pint import DimensionalityError, UndefinedUnitError
from pint.testsuite import BaseTestCase
+
class TestErrors(BaseTestCase):
def test_errors(self):
diff --git a/pint/testsuite/test_infer_base_unit.py b/pint/testsuite/test_infer_base_unit.py
index 885f7ed..12507b6 100644
--- a/pint/testsuite/test_infer_base_unit.py
+++ b/pint/testsuite/test_infer_base_unit.py
@@ -1,11 +1,7 @@
-from pint import UnitRegistry, set_application_registry
+from pint import Quantity as Q
from pint.testsuite import QuantityTestCase
from pint.util import infer_base_unit
-ureg = UnitRegistry()
-set_application_registry(ureg)
-Q = ureg.Quantity
-
class TestInferBaseUnit(QuantityTestCase):
def test_infer_base_unit(self):
@@ -29,4 +25,4 @@ class TestInferBaseUnit(QuantityTestCase):
r = Q(1, 'V') * Q(1, 'mV') / Q(1, 'kV')
b = infer_base_unit(r)
self.assertEqual(b, Q(1, 'V').units)
- self.assertQuantityAlmostEqual(r, Q(1, 'uV')) \ No newline at end of file
+ self.assertQuantityAlmostEqual(r, Q(1, 'uV'))
diff --git a/pint/testsuite/test_issues.py b/pint/testsuite/test_issues.py
index c3fcb31..eea527b 100644
--- a/pint/testsuite/test_issues.py
+++ b/pint/testsuite/test_issues.py
@@ -7,12 +7,11 @@ import copy
import unittest
import sys
-from pint import UnitRegistry
+from pint import DimensionalityError, UndefinedUnitError, UnitRegistry
from pint.unit import UnitsContainer
from pint.util import ParserHelper
-from pint.compat import np, long_type
-from pint.errors import UndefinedUnitError, DimensionalityError
+from pint.compat import np
from pint.testsuite import QuantityTestCase, helpers
@@ -40,10 +39,84 @@ class TestIssues(QuantityTestCase):
def test_issue29(self):
ureg = UnitRegistry()
- t = 4 * ureg('mM')
+ t = 4 * ureg('mW')
self.assertEqual(t.magnitude, 4)
- self.assertEqual(t._units, UnitsContainer(millimolar=1))
- self.assertEqual(t.to('mole / liter'), 4e-3 * ureg('M'))
+ self.assertEqual(t._units, UnitsContainer(milliwatt=1))
+ self.assertEqual(t.to('joule / second'), 4e-3 * ureg('W'))
+
+ @unittest.expectedFailure
+ @helpers.requires_numpy()
+ def test_issue37(self):
+ x = np.ma.masked_array([1, 2, 3], mask=[True, True, False])
+ ureg = UnitRegistry()
+ q = ureg.meter * x
+ self.assertIsInstance(q, ureg.Quantity)
+ np.testing.assert_array_equal(q.magnitude, x)
+ self.assertEqual(q.units, ureg.meter.units)
+ q = x * ureg.meter
+ self.assertIsInstance(q, ureg.Quantity)
+ np.testing.assert_array_equal(q.magnitude, x)
+ self.assertEqual(q.units, ureg.meter.units)
+
+ m = np.ma.masked_array(2 * np.ones(3,3))
+ qq = q * m
+ self.assertIsInstance(qq, ureg.Quantity)
+ np.testing.assert_array_equal(qq.magnitude, x * m)
+ self.assertEqual(qq.units, ureg.meter.units)
+ qq = m * q
+ self.assertIsInstance(qq, ureg.Quantity)
+ np.testing.assert_array_equal(qq.magnitude, x * m)
+ self.assertEqual(qq.units, ureg.meter.units)
+
+ @unittest.expectedFailure
+ @helpers.requires_numpy()
+ def test_issue39(self):
+ x = np.matrix([[1, 2, 3], [1, 2, 3], [1, 2, 3]])
+ ureg = UnitRegistry()
+ q = ureg.meter * x
+ self.assertIsInstance(q, ureg.Quantity)
+ np.testing.assert_array_equal(q.magnitude, x)
+ self.assertEqual(q.units, ureg.meter.units)
+ q = x * ureg.meter
+ self.assertIsInstance(q, ureg.Quantity)
+ np.testing.assert_array_equal(q.magnitude, x)
+ self.assertEqual(q.units, ureg.meter.units)
+
+ m = np.matrix(2 * np.ones(3,3))
+ qq = q * m
+ self.assertIsInstance(qq, ureg.Quantity)
+ np.testing.assert_array_equal(qq.magnitude, x * m)
+ self.assertEqual(qq.units, ureg.meter.units)
+ qq = m * q
+ self.assertIsInstance(qq, ureg.Quantity)
+ np.testing.assert_array_equal(qq.magnitude, x * m)
+ self.assertEqual(qq.units, ureg.meter.units)
+
+ @helpers.requires_numpy()
+ def test_issue44(self):
+ ureg = UnitRegistry()
+ x = 4. * ureg.dimensionless
+ np.sqrt(x)
+ self.assertQuantityAlmostEqual(np.sqrt([4.] * ureg.dimensionless), [2.] * ureg.dimensionless)
+ self.assertQuantityAlmostEqual(np.sqrt(4. * ureg.dimensionless), 2. * ureg.dimensionless)
+
+ def test_issue45(self):
+ import math
+ ureg = UnitRegistry()
+ self.assertAlmostEqual(math.sqrt(4 * ureg.m/ureg.cm), math.sqrt(4 * 100))
+ self.assertAlmostEqual(float(ureg.V / ureg.mV), 1000.)
+
+ @helpers.requires_numpy()
+ def test_issue45b(self):
+ ureg = UnitRegistry()
+ self.assertAlmostEqual(np.sin([np.pi/2] * ureg.m / ureg.m ), np.sin([np.pi/2] * ureg.dimensionless))
+ self.assertAlmostEqual(np.sin([np.pi/2] * ureg.cm / ureg.m ), np.sin([np.pi/2] * ureg.dimensionless * 0.01))
+
+ def test_issue50(self):
+ ureg = UnitRegistry()
+ Q_ = ureg.Quantity
+ self.assertEqual(Q_(100), 100 * ureg.dimensionless)
+ self.assertEqual(Q_('100'), 100 * ureg.dimensionless)
def test_issue52(self):
u1 = UnitRegistry()
@@ -86,6 +159,11 @@ class TestIssues(QuantityTestCase):
self.assertRaises(TypeError, Q_, value)
self.assertRaises(TypeError, Q_, value, 'meter')
+ def test_issue62(self):
+ ureg = UnitRegistry()
+ m = ureg('m**0.5')
+ self.assertEqual(str(m.units), 'meter ** 0.5')
+
def test_issue66(self):
ureg = UnitRegistry()
self.assertEqual(ureg.get_dimensionality(UnitsContainer({'[temperature]': 1})),
@@ -107,6 +185,46 @@ class TestIssues(QuantityTestCase):
q = ureg('m').to(ureg('in'))
self.assertEqual(q, ureg('m').to('in'))
+ @helpers.requires_numpy()
+ def test_issue74(self):
+ ureg = UnitRegistry()
+ v1 = np.asarray([1., 2., 3.])
+ v2 = np.asarray([3., 2., 1.])
+ q1 = v1 * ureg.ms
+ q2 = v2 * ureg.ms
+
+ np.testing.assert_array_equal(q1 < q2, v1 < v2)
+ np.testing.assert_array_equal(q1 > q2, v1 > v2)
+
+ np.testing.assert_array_equal(q1 <= q2, v1 <= v2)
+ np.testing.assert_array_equal(q1 >= q2, v1 >= v2)
+
+ q2s = np.asarray([0.003, 0.002, 0.001]) * ureg.s
+ v2s = q2s.to('ms').magnitude
+
+ np.testing.assert_array_equal(q1 < q2s, v1 < v2s)
+ np.testing.assert_array_equal(q1 > q2s, v1 > v2s)
+
+ np.testing.assert_array_equal(q1 <= q2s, v1 <= v2s)
+ np.testing.assert_array_equal(q1 >= q2s, v1 >= v2s)
+
+ @helpers.requires_numpy()
+ def test_issue75(self):
+ ureg = UnitRegistry()
+ v1 = np.asarray([1., 2., 3.])
+ v2 = np.asarray([3., 2., 1.])
+ q1 = v1 * ureg.ms
+ q2 = v2 * ureg.ms
+
+ np.testing.assert_array_equal(q1 == q2, v1 == v2)
+ np.testing.assert_array_equal(q1 != q2, v1 != v2)
+
+ q2s = np.asarray([0.003, 0.002, 0.001]) * ureg.s
+ v2s = q2s.to('ms').magnitude
+
+ np.testing.assert_array_equal(q1 == q2s, v1 == v2s)
+ np.testing.assert_array_equal(q1 != q2s, v1 != v2s)
+
@helpers.requires_uncertainties()
def test_issue77(self):
ureg = UnitRegistry()
@@ -127,7 +245,7 @@ class TestIssues(QuantityTestCase):
except:
self.assertTrue(False, 'Error while trying to get base units for {}'.format(va))
- boltmk = 1.3806488e-23*ureg.J/ureg.K
+ boltmk = 1.380649e-23*ureg.J/ureg.K
vb = 2. * boltmk * T / m
self.assertQuantityAlmostEqual(va.to_base_units(), vb.to_base_units())
@@ -205,32 +323,36 @@ class TestIssues(QuantityTestCase):
self.assertQuantityAlmostEqual(x + y, 5.1 * ureg.meter)
self.assertQuantityAlmostEqual(z, 5.1 * ureg.meter)
- def test_issue523(self):
+ @helpers.requires_numpy_previous_than('1.10')
+ def test_issue94(self):
ureg = UnitRegistry()
- src, dst = UnitsContainer({'meter': 1}), UnitsContainer({'degF': 1})
- value = 10.
- convert = self.ureg.convert
- self.assertRaises(DimensionalityError, convert, value, src, dst)
- self.assertRaises(DimensionalityError, convert, value, dst, src)
+ v1 = np.array([5, 5]) * ureg.meter
+ v2 = 0.1 * ureg.meter
+ v3 = np.array([5, 5]) * ureg.meter
+ v3 += v2
- def _test_issueXX(self):
- ureg = UnitRegistry()
- try:
- ureg.convert(1, ureg.degC, ureg.kelvin * ureg.meter / ureg.nanometer)
- except:
- self.assertTrue(False,
- 'Error while trying to convert {} to {}'.format(ureg.degC, ureg.kelvin * ureg.meter / ureg.nanometer))
+ np.testing.assert_array_equal((v1 + v2).magnitude, np.array([5.1, 5.1]))
+ np.testing.assert_array_equal(v3.magnitude, np.array([5, 5]))
- def test_issue121(self):
- sh = (2, 1)
+ def test_issue104(self):
ureg = UnitRegistry()
- z, v = 0, 2.
- self.assertEqual(z + v * ureg.meter, v * ureg.meter)
- self.assertEqual(z - v * ureg.meter, -v * ureg.meter)
- self.assertEqual(v * ureg.meter + z, v * ureg.meter)
- self.assertEqual(v * ureg.meter - z, v * ureg.meter)
- self.assertEqual(sum([v * ureg.meter, v * ureg.meter]), 2 * v * ureg.meter)
+ x = [ureg('1 meter'), ureg('1 meter'), ureg('1 meter')]
+ y = [ureg('1 meter')] * 3
+
+ def summer(values):
+ if not values:
+ return 0
+ total = values[0]
+ for v in values[1:]:
+ total += v
+
+ return total
+
+ self.assertQuantityAlmostEqual(summer(x), ureg.Quantity(3, 'meter'))
+ self.assertQuantityAlmostEqual(x[0], ureg.Quantity(1, 'meter'))
+ self.assertQuantityAlmostEqual(summer(y), ureg.Quantity(3, 'meter'))
+ self.assertQuantityAlmostEqual(y[0], ureg.Quantity(1, 'meter'))
def test_issue105(self):
ureg = UnitRegistry()
@@ -245,25 +367,61 @@ class TestIssues(QuantityTestCase):
self.assertRaises(AttributeError, func, 'METER')
self.assertEqual(val, func('METER', False))
- def test_issue104(self):
+ def test_issue121(self):
+ sh = (2, 1)
ureg = UnitRegistry()
+ z, v = 0, 2.
+ self.assertEqual(z + v * ureg.meter, v * ureg.meter)
+ self.assertEqual(z - v * ureg.meter, -v * ureg.meter)
+ self.assertEqual(v * ureg.meter + z, v * ureg.meter)
+ self.assertEqual(v * ureg.meter - z, v * ureg.meter)
- x = [ureg('1 meter'), ureg('1 meter'), ureg('1 meter')]
- y = [ureg('1 meter')] * 3
+ self.assertEqual(sum([v * ureg.meter, v * ureg.meter]), 2 * v * ureg.meter)
- def summer(values):
- if not values:
- return 0
- total = values[0]
- for v in values[1:]:
- total += v
+ @helpers.requires_numpy18()
+ def test_issue121b(self):
+ sh = (2, 1)
+ ureg = UnitRegistry()
- return total
+ z, v = 0, 2.
+ self.assertEqual(z + v * ureg.meter, v * ureg.meter)
+ self.assertEqual(z - v * ureg.meter, -v * ureg.meter)
+ self.assertEqual(v * ureg.meter + z, v * ureg.meter)
+ self.assertEqual(v * ureg.meter - z, v * ureg.meter)
- self.assertQuantityAlmostEqual(summer(x), ureg.Quantity(3, 'meter'))
- self.assertQuantityAlmostEqual(x[0], ureg.Quantity(1, 'meter'))
- self.assertQuantityAlmostEqual(summer(y), ureg.Quantity(3, 'meter'))
- self.assertQuantityAlmostEqual(y[0], ureg.Quantity(1, 'meter'))
+ self.assertEqual(sum([v * ureg.meter, v * ureg.meter]), 2 * v * ureg.meter)
+
+ z, v = np.zeros(sh), 2. * np.ones(sh)
+ self.assertQuantityEqual(z + v * ureg.meter, v * ureg.meter)
+ self.assertQuantityEqual(z - v * ureg.meter, -v * ureg.meter)
+ self.assertQuantityEqual(v * ureg.meter + z, v * ureg.meter)
+ self.assertQuantityEqual(v * ureg.meter - z, v * ureg.meter)
+
+ z, v = np.zeros((3, 1)), 2. * np.ones(sh)
+ for x, y in ((z, v),
+ (z, v * ureg.meter),
+ (v * ureg.meter, z)
+ ):
+ try:
+ w = x + y
+ self.assertTrue(False, "ValueError not raised")
+ except ValueError:
+ pass
+ try:
+ w = x - y
+ self.assertTrue(False, "ValueError not raised")
+ except ValueError:
+ pass
+
+ @helpers.requires_numpy()
+ def test_issue127(self):
+ q = [1., 2., 3., 4.] * self.ureg.meter
+ q[0] = np.nan
+ self.assertNotEqual(q[0], 1.)
+ self.assertTrue(math.isnan(q[0].magnitude))
+ q[1] = float('NaN')
+ self.assertNotEqual(q[1], 2.)
+ self.assertTrue(math.isnan(q[1].magnitude))
def test_issue170(self):
Q_ = UnitRegistry().Quantity
@@ -316,193 +474,7 @@ class TestIssues(QuantityTestCase):
else:
ureg.Quantity(2, 'µm')
-@helpers.requires_numpy()
-class TestIssuesNP(QuantityTestCase):
-
- FORCE_NDARRAY = False
-
- @unittest.expectedFailure
- def test_issue37(self):
- x = np.ma.masked_array([1, 2, 3], mask=[True, True, False])
- ureg = UnitRegistry()
- q = ureg.meter * x
- self.assertIsInstance(q, ureg.Quantity)
- np.testing.assert_array_equal(q.magnitude, x)
- self.assertEqual(q.units, ureg.meter.units)
- q = x * ureg.meter
- self.assertIsInstance(q, ureg.Quantity)
- np.testing.assert_array_equal(q.magnitude, x)
- self.assertEqual(q.units, ureg.meter.units)
-
- m = np.ma.masked_array(2 * np.ones(3,3))
- qq = q * m
- self.assertIsInstance(qq, ureg.Quantity)
- np.testing.assert_array_equal(qq.magnitude, x * m)
- self.assertEqual(qq.units, ureg.meter.units)
- qq = m * q
- self.assertIsInstance(qq, ureg.Quantity)
- np.testing.assert_array_equal(qq.magnitude, x * m)
- self.assertEqual(qq.units, ureg.meter.units)
-
- @unittest.expectedFailure
- def test_issue39(self):
- x = np.matrix([[1, 2, 3], [1, 2, 3], [1, 2, 3]])
- ureg = UnitRegistry()
- q = ureg.meter * x
- self.assertIsInstance(q, ureg.Quantity)
- np.testing.assert_array_equal(q.magnitude, x)
- self.assertEqual(q.units, ureg.meter.units)
- q = x * ureg.meter
- self.assertIsInstance(q, ureg.Quantity)
- np.testing.assert_array_equal(q.magnitude, x)
- self.assertEqual(q.units, ureg.meter.units)
-
- m = np.matrix(2 * np.ones(3,3))
- qq = q * m
- self.assertIsInstance(qq, ureg.Quantity)
- np.testing.assert_array_equal(qq.magnitude, x * m)
- self.assertEqual(qq.units, ureg.meter.units)
- qq = m * q
- self.assertIsInstance(qq, ureg.Quantity)
- np.testing.assert_array_equal(qq.magnitude, x * m)
- self.assertEqual(qq.units, ureg.meter.units)
-
- def test_issue44(self):
- ureg = UnitRegistry()
- x = 4. * ureg.dimensionless
- np.sqrt(x)
- self.assertQuantityAlmostEqual(np.sqrt([4.] * ureg.dimensionless), [2.] * ureg.dimensionless)
- self.assertQuantityAlmostEqual(np.sqrt(4. * ureg.dimensionless), 2. * ureg.dimensionless)
-
- def test_issue45(self):
- import math
- ureg = UnitRegistry()
- self.assertAlmostEqual(math.sqrt(4 * ureg.m/ureg.cm), math.sqrt(4 * 100))
- self.assertAlmostEqual(float(ureg.V / ureg.mV), 1000.)
-
- def test_issue45b(self):
- ureg = UnitRegistry()
- self.assertAlmostEqual(np.sin([np.pi/2] * ureg.m / ureg.m ), np.sin([np.pi/2] * ureg.dimensionless))
- self.assertAlmostEqual(np.sin([np.pi/2] * ureg.cm / ureg.m ), np.sin([np.pi/2] * ureg.dimensionless * 0.01))
-
- def test_issue50(self):
- ureg = UnitRegistry()
- Q_ = ureg.Quantity
- self.assertEqual(Q_(100), 100 * ureg.dimensionless)
- self.assertEqual(Q_('100'), 100 * ureg.dimensionless)
-
- def test_issue62(self):
- ureg = UnitRegistry()
- m = ureg('m**0.5')
- self.assertEqual(str(m.units), 'meter ** 0.5')
-
- def test_issue74(self):
- ureg = UnitRegistry()
- v1 = np.asarray([1., 2., 3.])
- v2 = np.asarray([3., 2., 1.])
- q1 = v1 * ureg.ms
- q2 = v2 * ureg.ms
-
- np.testing.assert_array_equal(q1 < q2, v1 < v2)
- np.testing.assert_array_equal(q1 > q2, v1 > v2)
-
- np.testing.assert_array_equal(q1 <= q2, v1 <= v2)
- np.testing.assert_array_equal(q1 >= q2, v1 >= v2)
-
- q2s = np.asarray([0.003, 0.002, 0.001]) * ureg.s
- v2s = q2s.to('ms').magnitude
-
- np.testing.assert_array_equal(q1 < q2s, v1 < v2s)
- np.testing.assert_array_equal(q1 > q2s, v1 > v2s)
-
- np.testing.assert_array_equal(q1 <= q2s, v1 <= v2s)
- np.testing.assert_array_equal(q1 >= q2s, v1 >= v2s)
-
- def test_issue75(self):
- ureg = UnitRegistry()
- v1 = np.asarray([1., 2., 3.])
- v2 = np.asarray([3., 2., 1.])
- q1 = v1 * ureg.ms
- q2 = v2 * ureg.ms
-
- np.testing.assert_array_equal(q1 == q2, v1 == v2)
- np.testing.assert_array_equal(q1 != q2, v1 != v2)
-
- q2s = np.asarray([0.003, 0.002, 0.001]) * ureg.s
- v2s = q2s.to('ms').magnitude
-
- np.testing.assert_array_equal(q1 == q2s, v1 == v2s)
- np.testing.assert_array_equal(q1 != q2s, v1 != v2s)
-
- def test_issue93(self):
- ureg = UnitRegistry()
- x = 5 * ureg.meter
- self.assertIsInstance(x.magnitude, int)
- y = 0.1 * ureg.meter
- self.assertIsInstance(y.magnitude, float)
- z = 5 * ureg.meter
- self.assertIsInstance(z.magnitude, int)
- z += y
- self.assertIsInstance(z.magnitude, float)
-
- self.assertQuantityAlmostEqual(x + y, 5.1 * ureg.meter)
- self.assertQuantityAlmostEqual(z, 5.1 * ureg.meter)
-
- @helpers.requires_numpy_previous_than('1.10')
- def test_issue94(self):
- ureg = UnitRegistry()
- v1 = np.array([5, 5]) * ureg.meter
- v2 = 0.1 * ureg.meter
- v3 = np.array([5, 5]) * ureg.meter
- v3 += v2
-
- np.testing.assert_array_equal((v1 + v2).magnitude, np.array([5.1, 5.1]))
- np.testing.assert_array_equal(v3.magnitude, np.array([5, 5]))
-
- @helpers.requires_numpy18()
- def test_issue121(self):
- sh = (2, 1)
- ureg = UnitRegistry()
-
- z, v = 0, 2.
- self.assertEqual(z + v * ureg.meter, v * ureg.meter)
- self.assertEqual(z - v * ureg.meter, -v * ureg.meter)
- self.assertEqual(v * ureg.meter + z, v * ureg.meter)
- self.assertEqual(v * ureg.meter - z, v * ureg.meter)
-
- self.assertEqual(sum([v * ureg.meter, v * ureg.meter]), 2 * v * ureg.meter)
-
- z, v = np.zeros(sh), 2. * np.ones(sh)
- self.assertQuantityEqual(z + v * ureg.meter, v * ureg.meter)
- self.assertQuantityEqual(z - v * ureg.meter, -v * ureg.meter)
- self.assertQuantityEqual(v * ureg.meter + z, v * ureg.meter)
- self.assertQuantityEqual(v * ureg.meter - z, v * ureg.meter)
-
- z, v = np.zeros((3, 1)), 2. * np.ones(sh)
- for x, y in ((z, v),
- (z, v * ureg.meter),
- (v * ureg.meter, z)
- ):
- try:
- w = x + y
- self.assertTrue(False, "ValueError not raised")
- except ValueError:
- pass
- try:
- w = x - y
- self.assertTrue(False, "ValueError not raised")
- except ValueError:
- pass
-
- def test_issue127(self):
- q = [1., 2., 3., 4.] * self.ureg.meter
- q[0] = np.nan
- self.assertNotEqual(q[0], 1.)
- self.assertTrue(math.isnan(q[0].magnitude))
- q[1] = float('NaN')
- self.assertNotEqual(q[1], 2.)
- self.assertTrue(math.isnan(q[1].magnitude))
-
+ @helpers.requires_numpy()
def test_issue171_real_imag(self):
qr = [1., 2., 3., 4.] * self.ureg.meter
qi = [4., 3., 2., 1.] * self.ureg.meter
@@ -510,12 +482,14 @@ class TestIssuesNP(QuantityTestCase):
self.assertQuantityEqual(q.real, qr)
self.assertQuantityEqual(q.imag, qi)
+ @helpers.requires_numpy()
def test_issue171_T(self):
a = np.asarray([[1., 2., 3., 4.],[4., 3., 2., 1.]])
q1 = a * self.ureg.meter
q2 = a.T * self.ureg.meter
self.assertQuantityEqual(q1.T, q2)
+ @helpers.requires_numpy()
def test_issue250(self):
a = self.ureg.V
b = self.ureg.mV
@@ -553,11 +527,6 @@ class TestIssuesNP(QuantityTestCase):
self.assertEqual('{0:~}'.format(1 * self.ureg('MiB')),
'1 MiB')
- def test_issue482(self):
- q = self.ureg.Quantity(1, self.ureg.dimensionless)
- qe = np.exp(q)
- self.assertIsInstance(qe, self.ureg.Quantity)
-
def test_issue468(self):
ureg = UnitRegistry()
@@ -570,6 +539,13 @@ class TestIssuesNP(QuantityTestCase):
z = x * y
self.assertEquals(z, ureg.Quantity(1., 'meter * kilogram'))
+ @helpers.requires_numpy()
+ def test_issue482(self):
+ q = self.ureg.Quantity(1, self.ureg.dimensionless)
+ qe = np.exp(q)
+ self.assertIsInstance(qe, self.ureg.Quantity)
+
+ @helpers.requires_numpy()
def test_issue483(self):
ureg = self.ureg
a = np.asarray([1, 2, 3])
@@ -577,6 +553,14 @@ class TestIssuesNP(QuantityTestCase):
p = (q ** q).m
np.testing.assert_array_equal(p, a ** a)
+ def test_issue523(self):
+ ureg = UnitRegistry()
+ src, dst = UnitsContainer({'meter': 1}), UnitsContainer({'degF': 1})
+ value = 10.
+ convert = self.ureg.convert
+ self.assertRaises(DimensionalityError, convert, value, src, dst)
+ self.assertRaises(DimensionalityError, convert, value, dst, src)
+
def test_issue532(self):
ureg = self.ureg
@@ -668,11 +652,7 @@ class TestIssuesNP(QuantityTestCase):
self.assertEqual(velocity.check('[length] / [time]'), True)
self.assertEqual(velocity.check('1 / [time] * [length]'), True)
- def test_issue783(self):
- ureg = UnitRegistry()
- assert not ureg('g') == []
-
- def test_issue(self):
+ def test_issue655b(self):
import math
try:
from inspect import signature
@@ -695,4 +675,44 @@ class TestIssuesNP(QuantityTestCase):
t = pendulum_period(l, moon_gravity)
self.assertAlmostEqual(t, Q_('4.928936075204336 second'))
+ def test_issue783(self):
+ ureg = UnitRegistry()
+ assert not ureg('g') == []
+ def test_issue856(self):
+ ph1 = ParserHelper(scale=123)
+ ph2 = copy.deepcopy(ph1)
+ assert ph2.scale == ph1.scale
+
+ ureg1 = UnitRegistry()
+ ureg2 = copy.deepcopy(ureg1)
+ # Very basic functionality test
+ assert ureg2('1 t').to('kg').magnitude == 1000
+
+ def test_issue856b(self):
+ # Test that, after a deepcopy(), the two UnitRegistries are
+ # independent from each other
+ ureg1 = UnitRegistry()
+ ureg2 = copy.deepcopy(ureg1)
+ ureg1.define('test123 = 123 kg')
+ ureg2.define('test123 = 456 kg')
+ assert ureg1('1 test123').to('kg').magnitude == 123
+ assert ureg2('1 test123').to('kg').magnitude == 456
+
+ def test_issue876(self):
+ # Same hash must not imply equality.
+
+ # As an implementation detail of CPython, hash(-1) == hash(-2).
+ # This test is useless in potential alternative Python implementations where
+ # hash(-1) != hash(-2); one would need to find hash collisions specific for each
+ # implementation
+
+ a = UnitsContainer({"[mass]": -1})
+ b = UnitsContainer({"[mass]": -2})
+ c = UnitsContainer({"[mass]": -3})
+
+ # Guarantee working on alternative Python implementations
+ assert (hash(-1) == hash(-2)) == (hash(a) == hash(b))
+ assert (hash(-1) == hash(-3)) == (hash(a) == hash(c))
+ assert a != b
+ assert a != c
diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py
index 360b29b..996195c 100644
--- a/pint/testsuite/test_numpy.py
+++ b/pint/testsuite/test_numpy.py
@@ -6,7 +6,7 @@ import copy
import operator as op
import unittest
-from pint import DimensionalityError, set_application_registry
+from pint import DimensionalityError
from pint.compat import np
from pint.testsuite import QuantityTestCase, helpers
from pint.testsuite.test_umath import TestUFuncs
@@ -235,6 +235,10 @@ class TestNumpyMethods(QuantityTestCase):
for q, v in zip(self.q.flatten(), [1, 2, 3, 4]):
self.assertEqual(q, v * self.ureg.m)
+ def test_iterable(self):
+ self.assertTrue(np.iterable(self.q))
+ self.assertFalse(np.iterable(1 * self.ureg.m))
+
def test_reversible_op(self):
"""
"""
@@ -247,13 +251,13 @@ class TestNumpyMethods(QuantityTestCase):
def test_pickle(self):
import pickle
- set_application_registry(self.ureg)
+
def pickle_test(q):
pq = pickle.loads(pickle.dumps(q))
np.testing.assert_array_equal(q.magnitude, pq.magnitude)
self.assertEqual(q.units, pq.units)
- pickle_test([10,20]*self.ureg.m)
+ pickle_test([10, 20]*self.ureg.m)
def test_equal(self):
x = self.q.magnitude
@@ -458,7 +462,7 @@ class TestNDArrayQuantityMath(QuantityTestCase):
self.assertRaises(DimensionalityError, op.pow, arr_cp, q_cp)
# ..not for op.ipow !
# q_cp is treated as if it is an array. The units are ignored.
- # _Quantity.__ipow__ is never called
+ # Quantity.__ipow__ is never called
arr_cp = copy.copy(arr)
q_cp = copy.copy(q)
self.assertRaises(DimensionalityError, op.ipow, arr_cp, q_cp)
diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py
index fdb2460..e5fc076 100644
--- a/pint/testsuite/test_quantity.py
+++ b/pint/testsuite/test_quantity.py
@@ -383,7 +383,7 @@ class TestQuantity(QuantityTestCase):
self.assertFalse(u_array_2.u == u_array_ref_reversed.u)
u_array_3 = self.Q_.from_sequence(u_seq_reversed, units='g')
- self.assertTrue(all(u_array_3 == u_array_ref_reversed))
+ self.assertTrue(all(u_array_3 == u_array_ref_reversed))
self.assertTrue(u_array_3.u == u_array_ref_reversed.u)
with self.assertRaises(ValueError):
@@ -392,6 +392,19 @@ class TestQuantity(QuantityTestCase):
u_array_5 = self.Q_.from_list(u_seq)
self.assertTrue(all(u_array_5 == u_array_ref))
+ @helpers.requires_numpy()
+ def test_iter(self):
+ # Verify that iteration gives element as Quantity with same units
+ x = self.Q_([0, 1, 2, 3], 'm')
+ self.assertQuantityEqual(next(iter(x)), self.Q_(0, 'm'))
+
+ def test_notiter(self):
+ # Verify that iter() crashes immediately, without needing to draw any
+ # element from it, if the magnitude isn't iterable
+ x = self.Q_(1, 'm')
+ with self.assertRaises(TypeError):
+ iter(x)
+
class TestQuantityToCompact(QuantityTestCase):
@@ -454,7 +467,11 @@ class TestQuantityToCompact(QuantityTestCase):
def test_nonnumeric_magnitudes(self):
ureg = self.ureg
x = "some string"*ureg.m
- self.assertRaises(RuntimeError, self.compareQuantity_compact(x,x))
+ if PYTHON3:
+ with self.assertWarns(RuntimeWarning):
+ self.compareQuantity_compact(x,x)
+ else:
+ self.assertRaises(RuntimeError, self.compareQuantity_compact(x,x))
class TestQuantityBasicMath(QuantityTestCase):
diff --git a/pint/testsuite/test_unit.py b/pint/testsuite/test_unit.py
index b9e3b06..9c6bf51 100644
--- a/pint/testsuite/test_unit.py
+++ b/pint/testsuite/test_unit.py
@@ -562,6 +562,8 @@ class TestCompatibleUnits(QuantityTestCase):
def test_get_base_units(self):
ureg = UnitRegistry()
self.assertEqual(ureg.get_base_units(''), (1, ureg.Unit('')))
+ self.assertEqual(ureg.get_base_units('pi'), (math.pi, ureg.Unit('')))
+ self.assertEqual(ureg.get_base_units('ln10'), (math.log(10), ureg.Unit('')))
self.assertEqual(ureg.get_base_units('meter'), ureg.get_base_units(ParserHelper(meter=1)))
def test_get_compatible_units(self):
@@ -646,3 +648,30 @@ class TestConvertWithOffset(QuantityTestCase, ParameterizedTestCase):
if src != dst:
self.assertQuantityAlmostEqual(convert(expected, dst, src),
value, atol=0.001)
+
+ def test_alias(self):
+ # Use load_definitions
+ ureg = UnitRegistry([
+ "canonical = [] = can = alias1 = alias2\n",
+ # overlapping aliases
+ "@alias canonical = alias2 = alias3\n",
+ # Against another alias
+ "@alias alias3 = alias4\n",
+ ])
+
+ # Use define
+ ureg.define("@alias canonical = alias5")
+
+ # Test that new aliases work
+ # Test that pre-existing aliases and symbol are not eliminated
+ for a in ("can", "alias1", "alias2", "alias3", "alias4", "alias5"):
+ self.assertEqual(ureg.Unit(a), ureg.Unit("canonical"))
+
+ # Test that aliases defined multiple times are not duplicated
+ self.assertEqual(
+ ureg._units["canonical"].aliases,
+ ("alias1", "alias2", "alias3", "alias4", "alias5")
+ )
+
+ # Define against unknown name
+ self.assertRaises(KeyError, ureg.define, "@alias notexist = something")
diff --git a/pint/testsuite/test_util.py b/pint/testsuite/test_util.py
index 915fc66..30d0bf9 100644
--- a/pint/testsuite/test_util.py
+++ b/pint/testsuite/test_util.py
@@ -9,7 +9,7 @@ from decimal import Decimal
from pint.testsuite import BaseTestCase, QuantityTestCase
from pint.util import (string_preprocessor, find_shortest_path, matrix_to_string,
transpose, tokenizer, find_connected_nodes, ParserHelper,
- UnitsContainer, to_units_container)
+ UnitsContainer, to_units_container, iterable, sized)
class TestUnitsContainer(QuantityTestCase):
@@ -134,8 +134,9 @@ class TestToUnitsContainer(BaseTestCase):
UnitsContainer(m=1))
def test_unit_conversion(self):
- from pint.unit import _Unit
- self.assertEqual(to_units_container(_Unit(UnitsContainer(m=1))),
+ from pint import Unit
+
+ self.assertEqual(to_units_container(Unit(UnitsContainer(m=1))),
UnitsContainer(m=1))
def test_dict_conversion(self):
@@ -333,3 +334,22 @@ class TestMatrix(BaseTestCase):
def test_transpose(self):
self.assertEqual(transpose([[1, 2], [3, 4]]), [[1, 3], [2, 4]])
+
+
+class TestOtherUtils(BaseTestCase):
+
+ def test_iterable(self):
+
+ # Test with list, string, generator, and scalar
+ self.assertTrue(iterable([0, 1, 2, 3]))
+ self.assertTrue(iterable('test'))
+ self.assertTrue(iterable((i for i in range(5))))
+ self.assertFalse(iterable(0))
+
+ def test_sized(self):
+
+ # Test with list, string, generator, and scalar
+ self.assertTrue(sized([0, 1, 2, 3]))
+ self.assertTrue(sized('test'))
+ self.assertFalse(sized((i for i in range(5))))
+ self.assertFalse(sized(0))
diff --git a/pint/unit.py b/pint/unit.py
index adb871c..24c9e0b 100644
--- a/pint/unit.py
+++ b/pint/unit.py
@@ -24,7 +24,7 @@ from .definitions import UnitDefinition
@fix_str_conversions
-class _Unit(PrettyIPython, SharedRegistryObject):
+class Unit(PrettyIPython, SharedRegistryObject):
"""Implements a class to describe a unit supporting math operations.
:type units: UnitsContainer, str, Unit or Quantity.
@@ -35,24 +35,25 @@ class _Unit(PrettyIPython, SharedRegistryObject):
default_format = ''
def __reduce__(self):
- from . import _build_unit
- return _build_unit, (self._units, )
+ # See notes in Quantity.__reduce__
+ from . import _unpickle
- def __new__(cls, units):
- inst = object.__new__(cls)
+ return _unpickle, (Unit, self._units)
+
+ def __init__(self, units):
+ super(Unit, self).__init__()
if isinstance(units, (UnitsContainer, UnitDefinition)):
- inst._units = units
+ self._units = units
elif isinstance(units, string_types):
- inst._units = inst._REGISTRY.parse_units(units)._units
- elif isinstance(units, _Unit):
- inst._units = units._units
+ self._units = self._REGISTRY.parse_units(units)._units
+ elif isinstance(units, Unit):
+ self._units = units._units
else:
raise TypeError('units must be of type str, Unit or '
'UnitsContainer; not {}.'.format(type(units)))
- inst.__used = False
- inst.__handling = None
- return inst
+ self.__used = False
+ self.__handling = None
@property
def debug_used(self):
@@ -64,7 +65,7 @@ class _Unit(PrettyIPython, SharedRegistryObject):
return ret
def __deepcopy__(self, memo):
- ret = self.__class__(copy.deepcopy(self._units))
+ ret = self.__class__(copy.deepcopy(self._units, memo))
ret.__used = self.__used
return ret
@@ -210,7 +211,7 @@ class _Unit(PrettyIPython, SharedRegistryObject):
if isinstance(other, NUMERIC_TYPES):
return self_q.compare(other, op)
- elif isinstance(other, (_Unit, UnitsContainer, dict)):
+ elif isinstance(other, (Unit, UnitsContainer, dict)):
return self_q.compare(self._REGISTRY.Quantity(1, other), op)
else:
return NotImplemented
@@ -288,10 +289,12 @@ class _Unit(PrettyIPython, SharedRegistryObject):
"""
return self.from_(value, strict=strict, name=name).magnitude
-def build_unit_class(registry):
+_Unit = Unit
+
+
+def build_unit_class(registry):
class Unit(_Unit):
- pass
+ _REGISTRY = registry
- Unit._REGISTRY = registry
return Unit
diff --git a/pint/util.py b/pint/util.py
index 13fc149..e447264 100644
--- a/pint/util.py
+++ b/pint/util.py
@@ -243,6 +243,9 @@ class udict(dict):
def __missing__(self, key):
return 0.
+ def copy(self):
+ return udict(self)
+
class UnitsContainer(Mapping):
"""The UnitsContainer stores the product of units and their respective
@@ -264,7 +267,7 @@ class UnitsContainer(Mapping):
raise TypeError('value must be a number, not {}'.format(type(value)))
if not isinstance(value, float):
d[key] = float(value)
- self._hash = hash(frozenset(self._d.items()))
+ self._hash = None
def copy(self):
return self.__copy__()
@@ -275,24 +278,28 @@ class UnitsContainer(Mapping):
if newval:
new._d[key] = newval
else:
- del new._d[key]
-
+ new._d.pop(key, None)
+ new._hash = None
return new
def remove(self, keys):
""" Create a new UnitsContainer purged from given keys.
"""
- d = udict(self._d)
- return UnitsContainer(((key, d[key]) for key in d if key not in keys))
+ new = self.copy()
+ for k in keys:
+ new._d.pop(k, None)
+ new._hash = None
+ return new
def rename(self, oldkey, newkey):
""" Create a new UnitsContainer in which an entry has been renamed.
"""
- d = udict(self._d)
- d[newkey] = d.pop(oldkey)
- return UnitsContainer(d)
+ new = self.copy()
+ new._d[newkey] = new._d.pop(oldkey)
+ new._hash = None
+ return new
def __iter__(self):
return iter(self._d)
@@ -307,18 +314,28 @@ class UnitsContainer(Mapping):
return key in self._d
def __hash__(self):
+ if self._hash is None:
+ self._hash = hash(frozenset(self._d.items()))
return self._hash
+ # Only needed by Python 2.7
def __getstate__(self):
- return {'_d': self._d, '_hash': self._hash}
+ return self._d, self._hash
def __setstate__(self, state):
- self._d = state['_d']
- self._hash = state['_hash']
+ self._d, self._hash = state
def __eq__(self, other):
if isinstance(other, UnitsContainer):
+ # UnitsContainer.__hash__(self) is not the same as hash(self); see
+ # ParserHelper.__hash__ and __eq__.
+ # Different hashes guarantee that the actual contents are different, but
+ # identical hashes give no guarantee of equality.
+ # e.g. in CPython, hash(-1) == hash(-2)
+ if UnitsContainer.__hash__(self) != UnitsContainer.__hash__(other):
+ return False
other = other._d
+
elif isinstance(other, string_types):
other = ParserHelper.from_string(other)
other = other._d
@@ -340,20 +357,25 @@ class UnitsContainer(Mapping):
return format_unit(self, spec, **kwspec)
def __copy__(self):
- return UnitsContainer(self._d)
+ # Skip expensive health checks performed by __init__
+ out = object.__new__(self.__class__)
+ out._d = self._d.copy()
+ out._hash = self._hash
+ return out
def __mul__(self, other):
- d = udict(self._d)
if not isinstance(other, self.__class__):
err = 'Cannot multiply UnitsContainer by {}'
raise TypeError(err.format(type(other)))
+
+ new = self.copy()
for key, value in other.items():
- d[key] += value
- keys = [key for key, value in d.items() if value == 0]
- for key in keys:
- del d[key]
+ new._d[key] += value
+ if new._d[key] == 0:
+ del new._d[key]
- return UnitsContainer(d)
+ new._hash = None
+ return new
__rmul__ = __mul__
@@ -361,26 +383,26 @@ class UnitsContainer(Mapping):
if not isinstance(other, NUMERIC_TYPES):
err = 'Cannot power UnitsContainer by {}'
raise TypeError(err.format(type(other)))
- d = udict(self._d)
- for key, value in d.items():
- d[key] *= other
- return UnitsContainer(d)
+
+ new = self.copy()
+ for key, value in new._d.items():
+ new._d[key] *= other
+ new._hash = None
+ return new
def __truediv__(self, other):
if not isinstance(other, self.__class__):
err = 'Cannot divide UnitsContainer by {}'
raise TypeError(err.format(type(other)))
- d = udict(self._d)
-
+ new = self.copy()
for key, value in other.items():
- d[key] -= value
-
- keys = [key for key, value in d.items() if value == 0]
- for key in keys:
- del d[key]
+ new._d[key] -= value
+ if new._d[key] == 0:
+ del new._d[key]
- return UnitsContainer(d)
+ new._hash = None
+ return new
def __rtruediv__(self, other):
if not isinstance(other, self.__class__) and other != 1:
@@ -468,7 +490,9 @@ class ParserHelper(UnitsContainer):
for key, value in ret.items()))
def __copy__(self):
- return ParserHelper(scale=self.scale, **self)
+ new = super(ParserHelper, self).__copy__()
+ new.scale = self.scale
+ return new
def copy(self):
return self.__copy__()
@@ -477,12 +501,21 @@ class ParserHelper(UnitsContainer):
if self.scale != 1.0:
mess = 'Only scale 1.0 ParserHelper instance should be considered hashable'
raise ValueError(mess)
- return self._hash
+ return super(ParserHelper, self).__hash__()
+
+ # Only needed by Python 2.7
+ def __getstate__(self):
+ return self._d, self._hash, self.scale
+
+ def __setstate__(self, state):
+ self._d, self._hash, self.scale = state
def __eq__(self, other):
- if isinstance(other, self.__class__):
- return self.scale == other.scale and\
+ if isinstance(other, ParserHelper):
+ return (
+ self.scale == other.scale and
super(ParserHelper, self).__eq__(other)
+ )
elif isinstance(other, string_types):
return self == ParserHelper.from_string(other)
elif isinstance(other, Number):
@@ -604,12 +637,21 @@ def _is_dim(name):
class SharedRegistryObject(object):
- """Base class for object keeping a refrence to the registree.
+ """Base class for object keeping a reference to the registree.
- Such object are for now _Quantity and _Unit, in a number of places it is
+ Such object are for now Quantity and Unit, in a number of places it is
that an object from this class has a '_units' attribute.
"""
+ def __new__(cls, *args, **kwargs):
+ inst = object.__new__(cls)
+ if not hasattr(cls, "_REGISTRY"):
+ # Base class, not subclasses dynamically by
+ # UnitRegistry._init_dynamic_classes
+ from . import _APP_REGISTRY
+
+ inst._REGISTRY = _APP_REGISTRY
+ return inst
def _check(self, other):
"""Check if the other object use a registry and if so that it is the
@@ -696,6 +738,16 @@ def fix_str_conversions(cls):
return cls
+def getattr_maybe_raise(self, item):
+ """Helper function to invoke at the beginning of all overridden ``__getattr__``
+ methods. Raise AttributeError if the user tries to ask for a _ or __ attribute.
+ """
+ # Double-underscore attributes are tricky to detect because they are
+ # automatically prefixed with the class name - which may be a subclass of self
+ if item.startswith('_') or item.endswith('__'):
+ raise AttributeError("%r object has no attribute %r" % (self, item))
+
+
class SourceIterator(object):
"""Iterator to facilitate reading the definition files.
@@ -767,3 +819,32 @@ class BlockIterator(SourceIterator):
return lineno, line
next = __next__
+
+
+def iterable(y):
+ """Check whether or not an object can be iterated over.
+
+ Vendored from numpy under the terms of the BSD 3-Clause License. (Copyright
+ (c) 2005-2019, NumPy Developers.)
+
+ :param value: Input object.
+ :param type: object
+ """
+ try:
+ iter(y)
+ except TypeError:
+ return False
+ return True
+
+
+def sized(y):
+ """Check whether or not an object has a defined length.
+
+ :param value: Input object.
+ :param type: object
+ """
+ try:
+ len(y)
+ except TypeError:
+ return False
+ return True
diff --git a/pint/xtranslated.txt b/pint/xtranslated.txt
index 3ccfd9d..3c4d032 100644
--- a/pint/xtranslated.txt
+++ b/pint/xtranslated.txt
@@ -1,22 +1,14 @@
# a few unit definitions added to use the translations by unicode cldr
-dietary_calorie = 1000 * calorie = Calorie
+dietary_calorie = 1000 * calorie = Cal = Calorie
metric_cup = liter / 4
-mps = meter / second
-square_inch = inch ** 2 = sq_in
-square_mile = mile ** 2 = sq_mile
square_meter = kilometer ** 2 = sq_m
square_kilometer = kilometer ** 2 = sq_km
mile_scandinavian = 10000 * meter
-century = 100 * year
cubic_mile = 1 * mile ** 3 = cu_mile = cubic_miles
-cubic_yard = 1 * yard ** 3 = cu_yd = cubic_yards
-cubic_foot = 1 * foot ** 3 = cu_ft = cubic_feet
-cubic_inch = 1 * inch ** 3 = cu_in = cubic_inches
cubic_meter = 1 * meter ** 3 = cu_m
cubic_kilometer = 1 * kilometer ** 3 = cu_km
-karat = [purity] = Karat
[consumption] = [volume] / [length]
liter_per_kilometer = liter / kilometer