pax_global_header00006660000000000000000000000064131153754530014521gustar00rootroot0000000000000052 comment=697834d17d6fb5bb5d0adf945a5d5f5d187506a1 pint-0.8.1/000077500000000000000000000000001311537545300125015ustar00rootroot00000000000000pint-0.8.1/.gitignore000066400000000000000000000002001311537545300144610ustar00rootroot00000000000000*~ __pycache__ *egg-info* *.pyc .DS_Store docs/_build/ .idea build/ dist/ MANIFEST .tox # WebDAV file system cache files .DAV/ pint-0.8.1/.travis-exclude.yml000066400000000000000000000003771311537545300162500ustar00rootroot00000000000000# This file is used to autogenerate the travis exclude matrix. rules: "3.5": "NUMPY_VERSION>=1.9.2" "3.4": "NUMPY_VERSION>=1.8.1" "3.3": "NUMPY_VERSION>=1.7.0,<=1.9.2" "2.7": "NUMPY_VERSION>1.5.1" "2.6": "NUMPY_VERSION>1.5.1,<=1.9.2" pint-0.8.1/.travis-full.yml000066400000000000000000000073361311537545300155630ustar00rootroot00000000000000language: python python: - "2.6" - "2.7" - "3.3" - "3.4" - "3.5" env: - UNCERTAINTIES="N" NUMPY_VERSION=0 - UNCERTAINTIES="N" NUMPY_VERSION=1.6.2 - UNCERTAINTIES="N" NUMPY_VERSION=1.7.1 - UNCERTAINTIES="N" NUMPY_VERSION=1.8.2 - UNCERTAINTIES="N" NUMPY_VERSION=1.9.3 - UNCERTAINTIES="N" NUMPY_VERSION=1.10.4 - UNCERTAINTIES="Y" NUMPY_VERSION=0 - UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2 - UNCERTAINTIES="Y" NUMPY_VERSION=1.7.1 - UNCERTAINTIES="Y" NUMPY_VERSION=1.8.2 - UNCERTAINTIES="Y" NUMPY_VERSION=1.9.3 - UNCERTAINTIES="Y" NUMPY_VERSION=1.10.4 branches: only: - master - develop before_install: - sudo apt-get update - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; else wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; fi - bash miniconda.sh -b -p $HOME/miniconda - export PATH="$HOME/miniconda/bin:$PATH" - hash -r - conda config --set always_yes yes --set changeps1 no - conda update -q conda # 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 - export ENV_NAME=travis install: - conda create -c mwcraig --yes -n $ENV_NAME python=$TRAVIS_PYTHON_VERSION pip - source activate $ENV_NAME - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then pip install unittest2; fi - if [ $UNCERTAINTIES == 'Y' ]; then pip install 'uncertainties==2.4.7.1'; fi - if [ $NUMPY_VERSION != '0' ]; then conda install -c mwcraig --yes numpy==$NUMPY_VERSION; fi - if [ $TRAVIS_PYTHON_VERSION == '3.2' ]; then pip install coverage==3.7.1; else pip install coverage; fi - if [[ $TRAVIS_PYTHON_VERSION == '3.5' && $NUMPY_VERSION == 1.10.4 && $UNCERTAINTIES == "Y" ]]; then pip install serialize pyyaml; fi - pip install coveralls script: - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then coverage run -p --source=pint --omit="*test*","*compat*" setup.py test; fi - if [ $TRAVIS_PYTHON_VERSION != '2.6' ]; then python -bb -m coverage run -p --source=pint --omit="*test*","*compat*" setup.py test; fi - coverage combine - coverage report -m after_success: - coveralls --verbose matrix: exclude: # Do not edit after this line - python: "3.5" env: UNCERTAINTIES="N" NUMPY_VERSION=1.8.2 - python: "3.5" env: UNCERTAINTIES="N" NUMPY_VERSION=1.6.2 - python: "3.5" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.8.2 - python: "3.5" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2 - python: "3.5" env: UNCERTAINTIES="N" NUMPY_VERSION=1.7.1 - python: "3.5" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.7.1 - python: "3.4" env: UNCERTAINTIES="N" NUMPY_VERSION=1.6.2 - python: "3.4" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2 - python: "3.4" env: UNCERTAINTIES="N" NUMPY_VERSION=1.7.1 - python: "3.4" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.7.1 - python: "3.3" env: UNCERTAINTIES="N" NUMPY_VERSION=1.6.2 - python: "3.3" env: UNCERTAINTIES="N" NUMPY_VERSION=1.9.3 - python: "3.3" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.10.4 - python: "3.3" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.6.2 - python: "3.3" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.9.3 - python: "3.3" env: UNCERTAINTIES="N" NUMPY_VERSION=1.10.4 - python: "2.6" env: UNCERTAINTIES="N" NUMPY_VERSION=1.9.3 - python: "2.6" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.10.4 - python: "2.6" env: UNCERTAINTIES="Y" NUMPY_VERSION=1.9.3 - python: "2.6" env: UNCERTAINTIES="N" NUMPY_VERSION=1.10.4 pint-0.8.1/.travis.yml000066400000000000000000000042471311537545300146210ustar00rootroot00000000000000language: python env: - UNCERTAINTIES="N" PYTHON="2.6" NUMPY_VERSION=1.9.2 - UNCERTAINTIES="N" PYTHON="2.7" NUMPY_VERSION=1.11.2 - UNCERTAINTIES="N" PYTHON="3.3" NUMPY_VERSION=1.9.2 - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.11.2 - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.11.2 - UNCERTAINTIES="Y" PYTHON="3.5" NUMPY_VERSION=1.11.2 - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.11.2 - UNCERTAINTIES="N" PYTHON="2.7" NUMPY_VERSION=0 - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=0 branches: only: - staging - trying - master before_install: - sudo apt-get update - if [[ "$PYTHON" == "2.7" ]]; then wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; else wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; fi - bash miniconda.sh -b -p $HOME/miniconda - export PATH="$HOME/miniconda/bin:$PATH" - hash -r - conda config --set always_yes yes --set changeps1 no - conda update -q conda # 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 - export ENV_NAME=travis install: - conda create --yes -n $ENV_NAME python=$PYTHON pip - source activate $ENV_NAME - if [ $PYTHON == '2.6' ]; then pip install unittest2; fi - if [ $UNCERTAINTIES == 'Y' ]; then pip install 'uncertainties==2.4.7.1'; fi - if [ $NUMPY_VERSION != '0' ]; then conda install --yes numpy==$NUMPY_VERSION; fi - if [ $PYTHON == '3.2' ]; then pip install coverage==3.7.1; else pip install coverage; fi - if [[ $TRAVIS_PYTHON_VERSION == '3.5' && $NUMPY_VERSION == 1.11.2 && $UNCERTAINTIES == "Y" ]]; then pip install babel serialize pyyaml; fi - pip install coveralls script: - if [ $PYTHON == '2.6' ]; then coverage run -p --source=pint --omit="*test*","*compat*" setup.py test; fi - if [ $PYTHON != '2.6' ]; then python -bb -m coverage run -p --source=pint --omit="*test*","*compat*" setup.py test; fi - coverage combine - coverage report -m after_success: - coveralls --verbose pint-0.8.1/AUTHORS000066400000000000000000000026751311537545300135630ustar00rootroot00000000000000Pint is written and maintained by Hernan E. Grecco . Other contributors, listed alphabetically, are: * Aaron Coleman * Alexander Böhn * Ana Krivokapic * Andrea Zonca * Brend Wanders * choloepus * coutinho * Daniel Sokolowski * Dave Brooks * David Linke * Ed Schofield * Eduard Bopp * Eli * Felix Hummel * Giel van Schijndel * James Rowe * Jim Turner * Joel B. Mohler * John David Reaver * Jonas Olson * Kaido Kert * Kenneth D. Mankoff * Kevin Davies * Luke Campbell * Matthieu Dartiailh * Nate Bogdanowicz * Peter Grayson * Richard Barnes * Ryan Dwyer * Ryan Kingsbury * Sundar Raman * Tiago Coutinho * Thomas Kluyver * Tom Ritchford * Virgil Dupras (If you think that your name belongs here, please let the maintainer know) pint-0.8.1/BADGES.rst000066400000000000000000000016121311537545300141600ustar00rootroot00000000000000.. image:: https://img.shields.io/pypi/v/pint.svg :target: https://pypi.python.org/pypi/pint :alt: Latest Version .. image:: https://readthedocs.org/projects/pip/badge/ :target: http://pint.readthedocs.org/ :alt: Documentation .. image:: https://img.shields.io/pypi/l/pint.svg :target: https://pypi.python.org/pypi/pint :alt: License .. image:: https://img.shields.io/pypi/pyversions/pint.svg :target: https://pypi.python.org/pypi/pint :alt: Python Versions .. image:: https://travis-ci.org/hgrecco/pint.svg?branch=master :target: https://travis-ci.org/hgrecco/pint :alt: CI .. image:: https://coveralls.io/repos/github/hgrecco/pint/badge.svg?branch=master :target: https://coveralls.io/github/hgrecco/pint?branch=master :alt: Coverage .. image:: https://readthedocs.org/projects/pint/badge/ :target: http://pint.readthedocs.org/ :alt: Docs pint-0.8.1/CHANGES000066400000000000000000000327371311537545300135100ustar00rootroot00000000000000Pint Changelog ============== 0.8.1 (2017-06-05) ------------------ - Add support for datetime math. (Issue #510, thanks robertd) - Fixed _repr_html_ in Python 2.7. (Issue #512) - Implemented BaseRegistry.auto_reduce_dimensions. (Issue #500, thanks robertd) - Fixed dimension compatibility bug introduced on Registry refactoring (Issue #523, thanks dalito) 0.8 (2017-04-16) ---------------- - Refactored the Registry in multiple classes for better separation of concerns and clarity. - Implemented support for defining multiple units per `define` call (one definition per line). (Issue #462) - In pow and ipow, allow array exponents (with len > 1) when base is dimensionless. (Issue #483) - Wraps now gets the canonical name of the unit when passed as string. (Issue #468) - NumPy exp and log keeps the type (Issue #95) - Implemented a function decorator to ensure that a context is active (with_context) (Issue #465) - Add warning when a System contains an unknown Group. (Issue #472) - Add conda-forge installation snippet. (Issue #485, thanks stadelmanma) - Properly support floor division and modulo. (Issue #474, thanks tecki) - Measurement Correlated variable fix. (Issue #463, thanks tadhgmister) - Implement degree sign handling. (Issue #449, thanks iamthad) - Change `UndefinedUnitError` to inherit from `AttributeError` (Issue #480, thanks jhidding) - Simplified travis for faster testing. - Fixed order units in siunitx formatting. (Issue #441) - Changed Systems lister to return a list instead of frozenset. (Issue #425, thanks GloriaVictis) - Fixed issue with negative values in to_compact() method. (Issue #443, thanks nowox) - Improved defintions. (Issues #448, thanks gdonval) - Improved Parser to support capital "E" on scientific notation. (Issue #390, thanks javenoneal) - Make sure that prefixed units are defined on the registry when unplicking. (Issue #405) - Automatic unit names translation through babel. (Issue #338, thanks alexbodn) - Support pickling Unit objects. (Issue #349) - Add support for wavenumber/kayser in spectroscopy context. (Issue #321, thanks gerritholl) - Improved formatting. (thanks endolith and others) - Add support for inline comments in definitions file. (Issue #366) - Implement Unit.__deepcopy__. (Issue #357, thanks noahl) - Allow changing shape for Quantities with numpy arrays. (Issue #344, thanks tecki) 0.7.2 (2016-03-02) ------------------ - Fixed backward incompatibility problem when parsing dimensionless units. 0.7.1 (2016-02-23) ------------------ - Use NIST as source for most of the unit information. - Added message to assertQuantityEqual. - Added detection of circular dependencies in definitions. 0.7 (2016-02-20) ---------------- - Added Systems and groups. (Issue #215, #315) - Implemented references for wraps decorator. (Issue #195) - Added check decorator to UnitRegistry. (Issue #283, thanks kaidokert) - Added compact conversion. (See #224, thanks Ryan Dwyer) - Added compact formating code. (Issue #240) - New Unit Class. (thanks Matthieu Dartiailh) - Refactor UnitRegistry. (thanks Matthieu Dartiailh) - Move definitions, errors, and converters into their own modules. (thanks Matthieu Dartiailh) - UnitsContainer is now immutable (Issue #202, thanks Matthieu Dartiailh) - New parser and evaluator. (Issue #226, thanks Aaron Coleman) - Added support for Unicode identifiers. - Added m_as as way top retrieve the magnitude in different units. (Issue #227) - Added Short form for magnitude and units. (Issue #234) - Improved deepcopy. (Issue #252, thanks Emilien Kofman) - Improved testing infrastructure. - Improved docs. (thanks Ryan Dwyer, Martin Thoma, Andrea Zonca) - Fixed short names on electron_volt and hartree. - Fixed definitions of scruple and drachm. (Issue #262, thanks takowl) - Fixed troy ounce to 480 'grains'. (thanks elifab) - Added 'quad' as a unit of energy (= 10**15 Btu). (thanks Ed Schofield) - Added "hectare" as a supported unit of area and 'ha' as the symbol for hectare. (thanks Ed Schofield) - Added peak sun hour and Langley. (thanks Ed Schofield) - Added photometric units: lumen & lux. (Issue #230, thanks janpipek) - A fraction magnitude quantity is conserved (Issue #323, thanks emilienkofman) - Improved conversion performance by removing unnecessart try/except. (Issue #251) - Added to_tuple and from_tuple to facilitate serialization. - Fixed support for NumPy 1.10 due to a change in the Default casting rule (Issue #320) - Infrastructure: Added doctesting. - Infrastructure: Better way to specify exclude matrix in travis. 0.6 (2014-11-07) ---------------- - Fix operations with measurments and user defined units. (Issue #204) - Faster conversions through caching and other performance improvements. (Issue #193, thanks MatthieuDartiailh) - Better error messages on Quantity.__setitem__. (Issue #191) - Fixed abbreviation of fluid_ounce. (Issue #187, thanks hsoft) - Defined Angstrom symbol. (Issue #181, thanks JonasOlson) - Removed fetching version from git repo as it triggers XCode installation on OSX. (Issue #178, thanks deanishe) - Improved context documentation. (Issue #176 and 179, thanks rsking84) - Added Chemistry context. (Issue #179, thanks rsking84) - Fix help(UnitRegisty) (Issue #168) - Optimized "get_dimensionality" and "get_base_name". (Issue #166 and #167, thanks jbmohler) - Renamed ureg.parse_units parameter "to_delta" to "as_delta" to make clear. that no conversion happens. Accordingly, the parameter/property "default_to_delta" of UnitRegistry was renamed to "default_as_delta". (Issue #158, thanks dalit) - Fixed problem when adding two uncertainties. (thanks dalito) - Full support for Offset units (e.g. temperature) (Issue #88, #143, #147 and #161, thanks dalito) 0.5.2 (2014-07-31) ------------------ - Changed travis config to use miniconda for faster testing. - Added wheel configuration to setup.cfg. - Ensure resource streams are closed after reading. - Require setuptools. (Issue #169) - Implemented real, imag and T Quantity properties. (Issue #171) - Implemented __int__ and __long__ for Quantity (Issue #170) - Fixed SI prefix error on ureg.convert. (Issue #156, thanks jdreaver) - Fixed parsing of multiparemeter contexts. (Issue #174) 0.5.1 (2014-06-03) ------------------ - Implemented a standard way to change the registry used in unpickling operations. (Issue #148) - Fix bug where conversion would fail due to caching. (Issue #140, thanks jdreaver) - Allow assigning Not a Number to a quantity array. (Issue #127) - Decoupled Quantity in place and not in place unit conversion methods. - Return None in functions that modify quantities in place. - Improved testing infrastructure to check for unwanted warnings. - Added test function at the package level to run all tests. 0.5 (2014-05-07) ---------------- - Improved test suite helper functions. - Print honors default format w/o format(). (Issue #132, thanks mankoff) - Fixed sum() by treating number zero as a special case. (Issue #122, thanks rec) - Improved behaviour in ScaleConverter, OffsetConverter and Quantity.to. (Issue #120) - Reimplemented loading of default definitions to allow Pint in a cx_freeze or similar package. (Issue #118, thanks jbmohler) - Implemented parsing of pretty printed units. (Issue #117, thanks jpgrayson) - Fixed representation of dimensionless quantities. (Issue #112, thanks rec) - Raise error when invalid formatting code is given. (Issue #111, thanks rec) - Default registry to lazy load, raise error on redefinition (Issue #108, thanks rec, aepsil0n) - Added condensed format. (Issue #107, thanks rec) - Added UnitRegistry () operator to parse expression replacing []. (Issue #106, thanks rec) - Optional case insensitive unit parsing. (Issue #105, thanks rec, jeremyfreeman, dbrnz) - Change the Quantity mutability depending on magnitude type. (Issue #104, thanks rec) - Implemented API to list compatible units. (Issue #89) - Implemented cache of key UnitRegistry methods. - Rewrote the Measurement class to use uncertainties. (Issue #24) 0.4.2 (2014-02-14) ------------------ - Python 2.6 support (Issue #96, thanks tiagocoutinho) - Fixed symbol for inch. (Issue #102, thanks cybertoast) - Stop raising AttributeError when wrapping funcs without all of the attributes. (Issue #100, thanks jturner314) - Fixed warning appearing in Py2.x when comparing a Numpy Array with an empty string. (Issue #98, thanks jturner314) - Add links to AUR packages in docs. (Issue #91, thanks jturner314) - Fixed garbage collection related problem. (Issue #92, thanks jturner314) 0.4.1 (2014-01-12) ------------------ - Integer Division with Arrays. (Issue #80, thanks jdreaver) - Improved Documentation. (Issue #83, thanks choloepus) - Removed 'h' alias for hour due to conflict with Planck's constant. (Issue #82, thanks choloepus) - Improved get_base_units for non-multiplicative units. (Issue #85, thanks exxus) - Refactored code for multiplication. (Issue #84, thanks jturner314) - Removed 'R' alias for roentgen as it collides with molar_gas_constant. (Issue #87, thanks rsking84) - Improved naming of temperature units and multiplication of non-multiplicative units. (Issue #86, tahsnk exxus) 0.4 (2013-12-17) ---------------- - Introduced Contexts: relation between incompatible dimensions. (Issue #65) - Fixed get_base_units for non multiplicative units. (Related to issue #66) - Implemented default formatting for quantities. - Changed comparison between Quantities containing NumPy arrays. (Issue #75) - BACKWARDS INCOMPATIBLE CHANGE - Fixes for NumPy 1.8 due to changes in handling binary ops. (Issue #73) 0.3.3 (2013-11-29) ------------------ - ParseHelper can now parse units named like python keywords. (Issue #69) - Fix comparison of quantities. (Issue #74) - Fix Inequality operator. (Issue #70, thanks muggenhor) - Improved travis configuration. (thanks muggenhor) 0.3.2 (2013-10-22) ------------------ - Fix get_dimensionality for non multiplicative units. (Issue #66) - Proper handling of @import directive inside a file read using pkg_resources. (Issue #68) 0.3.1 (2013-09-15) ------------------ - fix right division on python 2.7 (Issue #58, thanks natezb) - fix formatting of fractional exponentials between 0 and 1. (Issue #62, thanks jdreaver) - fix installation as egg. (Issue #61) - fix handling of strange values as input of Quantity. (Issue #53) - math operations between quantities of different registries now raise a ValueError. (Issue #52) 0.3 (2013-09-02) ---------------- - support for IPython autocomplete and rich display. (Issues #30 and #31) - support for @import directive in definitions file. (Issue #22) - support for wrapping functions to make them pint-aware. (Issue #16) - support for comparing UnitsContainer to string. (Issue #35) - fix error raised while converting from a single unit to one expressed as the relation between many. (Issue #29) - fix error raised when unit symbol is missing. (Issue #41) - fix error raised when magnitude is Decimal. (Issue #46, thanks danielsokolowski) - support for non-installed pint. (Issue #42, thanks danielsokolowski) - support for application of numpy function on non-ndarray magnitudes. (Issue #44) - support for math operations on dimensionless Quantities (written with units). (Issue #45) - fix obtaining dimensionless quantity from string. (Issue #50) - fix adding and comparing numbers to a dimensionless quantity (written with units). (Issue #54) - Support for iter in Quantity. (Issue #55, thanks natezb) 0.2.1 (2013-07-02) ------------------ - fix error raised while converting from a single unit to one expressed as the relation between many. (Issue #29) 0.2 (2013-05-13) ---------------- - support for Measurement (Quantity +/- error). - implemented buckingham pi theorem for dimensional analysis. - support for temperature units and temperature difference units. - parser can infers if the user mean temperature or temperature difference. - support for derived dimensions (e.g. [speed] = [length] / [time]). - refactored the code into multiple files. - refactored code to isolate definitions and converters. - refactored formatter out of UnitParser class. - added tox and travis config files for CI. - comprehensive NumPy testing including almost all ufuncs. - full NumPy support (features is not longer experimental). - fixed bug preventing from having independent registries. (Issue #10, thanks bwanders) - forces real division as default for Quantities. (Issue #7, thanks dbrnz) - improved default unit definition file. (Issue #13, thanks r-barnes) - smarter parser supporting spaces as multiplications and other nice features. (Issue #13, thanks r-barnes) - moved testsuite inside package. - short forms of binary prefixes, more units and fix to less than comparison. (Issue #20, thanks muggenhor) - pint is now zip-safe (Issue #23, thanks muggenhor) Version 0.1.3 (2013-01-07) -------------------------- - abbreviated quantity string formating. - complete Python 2.7 compatibility. - implemented pickle support for Quantities objects. - extended NumPy support. - various bugfixes. Version 0.1.2 (2012-08-12) -------------------------- - experimenal NumPy support. - included default unit definitions file. (Issue #1, thanks fish2000) - better testing. - various bugfixes. - fixed some units definitions. (Issue #4, thanks craigholm) Version 0.1.1 (2012-07-31) -------------------------- - better packaging and installation. Version 0.1 (2012-07-26) -------------------------- - first public release. pint-0.8.1/CHANGES_DEV000066400000000000000000000000451311537545300141710ustar00rootroot00000000000000 0.7 (unreleased) ---------------- pint-0.8.1/LICENSE000066400000000000000000000030601311537545300135050ustar00rootroot00000000000000Copyright (c) 2012 by Hernan E. Grecco and contributors. See AUTHORS for more details. Some rights reserved. Redistribution and use in source and binary forms of the software as well as documentation, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pint-0.8.1/MANIFEST.in000066400000000000000000000004111311537545300142330ustar00rootroot00000000000000include README AUTHORS CHANGES LICENSE CHANGES_DEV README.rst BADGES.rst recursive-include pint * recursive-include docs * recursive-include bench * prune docs/_build prune docs/_themes/.git global-exclude *.pyc *~ .DS_Store *__pycache__* *.pyo .travis-exclude.yml pint-0.8.1/README000066400000000000000000000052731311537545300133700ustar00rootroot00000000000000Pint: makes units easy ====================== Pint is a Python package to define, operate and manipulate physical quantities: the product of a numerical value and a unit of measurement. It allows arithmetic operations between them and conversions from and to different units. It is distributed with a comprehensive list of physical units, prefixes and constants. Due to its modular design, you can extend (or even rewrite!) the complete list without changing the source code. It supports a lot of numpy mathematical operations **without monkey patching or wrapping numpy**. It has a complete test coverage. It runs in Python 2.6 and 3.X with no other dependency. It is licensed under BSD. It is extremely easy and natural to use: .. code-block:: python >>> import pint >>> ureg = pint.UnitRegistry() >>> 3 * ureg.meter + 4 * ureg.cm and you can make good use of numpy if you want: .. code-block:: python >>> import numpy as np >>> [3, 4] * ureg.meter + [4, 3] * ureg.cm >>> np.sum(_) Quick Installation ------------------ To install Pint, simply: .. code-block:: bash $ pip install pint or utilizing conda with, the conda-forge channel: .. code-block:: bash $ conda install -c conda-forge pint and then simply enjoy it! Documentation ------------- Full documentation is available at http://pint.readthedocs.org/ Design principles ----------------- Although there are already a few very good Python packages to handle physical quantities, no one was really fitting my needs. Like most developers, I programed Pint to scratch my own itches. - Unit parsing: prefixed and pluralized forms of units are recognized without explicitly defining them. In other words: as the prefix *kilo* and the unit *meter* are defined, Pint understands *kilometers*. This results in a much shorter and maintainable unit definition list as compared to other packages. - Standalone unit definitions: units definitions are loaded from simple and easy to edit text file. Adding and changing units and their definitions does not involve changing the code. - Advanced string formatting: a quantity can be formatted into string using PEP 3101 syntax. Extended conversion flags are given to provide latex and pretty formatting. - Small codebase: small and easy to maintain with a flat hierarchy. - Dependency free: it depends only on Python and its standard library. - Python 2 and 3: A single codebase that runs unchanged in Python 2.6+ and Python 3.0+. - Advanced NumPy support: While NumPy is not a requirement for Pint, when available ndarray methods and ufuncs can be used in Quantity objects. pint-0.8.1/README.rst000066400000000000000000000071041311537545300141720ustar00rootroot00000000000000.. image:: https://img.shields.io/pypi/v/pint.svg :target: https://pypi.python.org/pypi/pint :alt: Latest Version .. image:: https://readthedocs.org/projects/pip/badge/ :target: http://pint.readthedocs.org/ :alt: Documentation .. image:: https://img.shields.io/pypi/l/pint.svg :target: https://pypi.python.org/pypi/pint :alt: License .. image:: https://img.shields.io/pypi/pyversions/pint.svg :target: https://pypi.python.org/pypi/pint :alt: Python Versions .. image:: https://travis-ci.org/hgrecco/pint.svg?branch=master :target: https://travis-ci.org/hgrecco/pint :alt: CI .. image:: https://coveralls.io/repos/github/hgrecco/pint/badge.svg?branch=master :target: https://coveralls.io/github/hgrecco/pint?branch=master :alt: Coverage .. image:: https://readthedocs.org/projects/pint/badge/ :target: http://pint.readthedocs.org/ :alt: Docs Pint: makes units easy ====================== Pint is a Python package to define, operate and manipulate physical quantities: the product of a numerical value and a unit of measurement. It allows arithmetic operations between them and conversions from and to different units. It is distributed with a comprehensive list of physical units, prefixes and constants. Due to its modular design, you can extend (or even rewrite!) the complete list without changing the source code. It supports a lot of numpy mathematical operations **without monkey patching or wrapping numpy**. It has a complete test coverage. It runs in Python 2.6 and 3.X with no other dependency. It is licensed under BSD. It is extremely easy and natural to use: .. code-block:: python >>> import pint >>> ureg = pint.UnitRegistry() >>> 3 * ureg.meter + 4 * ureg.cm and you can make good use of numpy if you want: .. code-block:: python >>> import numpy as np >>> [3, 4] * ureg.meter + [4, 3] * ureg.cm >>> np.sum(_) Quick Installation ------------------ To install Pint, simply: .. code-block:: bash $ pip install pint or utilizing conda, with the conda-forge channel: .. code-block:: bash $ conda install -c conda-forge pint and then simply enjoy it! Documentation ------------- Full documentation is available at http://pint.readthedocs.org/ Design principles ----------------- Although there are already a few very good Python packages to handle physical quantities, no one was really fitting my needs. Like most developers, I programed Pint to scratch my own itches. - Unit parsing: prefixed and pluralized forms of units are recognized without explicitly defining them. In other words: as the prefix *kilo* and the unit *meter* are defined, Pint understands *kilometers*. This results in a much shorter and maintainable unit definition list as compared to other packages. - Standalone unit definitions: units definitions are loaded from simple and easy to edit text file. Adding and changing units and their definitions does not involve changing the code. - Advanced string formatting: a quantity can be formatted into string using PEP 3101 syntax. Extended conversion flags are given to provide latex and pretty formatting. - Small codebase: small and easy to maintain with a flat hierarchy. - Dependency free: it depends only on Python and its standard library. - Python 2 and 3: A single codebase that runs unchanged in Python 2.6+ and Python 3.0+. - Advanced NumPy support: While NumPy is not a requirement for Pint, when available ndarray methods and ufuncs can be used in Quantity objects. pint-0.8.1/bench/000077500000000000000000000000001311537545300135605ustar00rootroot00000000000000pint-0.8.1/bench/bench.py000066400000000000000000000057371311537545300152250ustar00rootroot00000000000000 from __future__ import division, unicode_literals, print_function, absolute_import import fnmatch import os import copy from timeit import Timer import yaml def time_stmt(stmt='pass', setup='pass', number=0, repeat=3): """Timer function with the same behaviour as running `python -m timeit ` in the command line. :return: elapsed time in seconds or NaN if the command failed. :rtype: float """ t = Timer(stmt, setup) if not number: # determine number so that 0.2 <= total time < 2.0 for i in range(1, 10): number = 10**i try: x = t.timeit(number) except: print(t.print_exc()) return float('NaN') if x >= 0.2: break try: r = t.repeat(repeat, number) except: print(t.print_exc()) return float('NaN') best = min(r) return best / number def build_task(task, name='', setup='', number=0, repeat=3): nt = copy.copy(task) nt['name'] = (name + ' ' + task.get('name', '')).strip() nt['setup'] = (setup + '\n' + task.get('setup', '')).strip('\n') nt['stmt'] = task.get('stmt', '') nt['number'] = task.get('number', number) nt['repeat'] = task.get('repeat', repeat) return nt def time_task(name, stmt='pass', setup='pass', number=0, repeat=3, stmts='', base=''): if base: nvalue = time_stmt(stmt=base, setup=setup, number=number, repeat=repeat) yield name + ' (base)', nvalue suffix = ' (normalized)' else: nvalue = 1. suffix = '' if stmt: value = time_stmt(stmt=stmt, setup=setup, number=number, repeat=repeat) yield name, value / nvalue for task in stmts: new_task = build_task(task, name, setup, number, repeat) for task_name, value in time_task(**new_task): yield task_name + suffix, value / nvalue def time_file(filename, name='', setup='', number=0, repeat=3): """Open a yaml benchmark file an time each statement, yields a tuple with filename, task name, time in seconds. """ with open(filename, 'r') as fp: tasks = yaml.load(fp) for task in tasks: new_task = build_task(task, name, setup, number, repeat) for task_name, value in time_task(**new_task): yield task_name, value def recursive_glob(rootdir='.', pattern='*'): return [os.path.join(looproot, filename) for looproot, _, filenames in os.walk(rootdir) for filename in filenames if fnmatch.fnmatch(filename, pattern)] def main(filenames=None): if not filenames: filenames = recursive_glob('.', 'bench_*.yaml') elif isinstance(filenames, basestring): filenames = [filenames, ] for filename in filenames: print(filename) print('-' * len(filename)) print() for task_name, value in time_file(filename): print('%.2e %s' % (value, task_name)) print() main() pint-0.8.1/bench/bench_base.yaml000066400000000000000000000015631311537545300165220ustar00rootroot00000000000000 - name: importing stmt: import pint - name: empty registry setup: import pint stmt: ureg = pint.UnitRegistry(None) - name: default registry setup: import pint stmt: ureg = pint.UnitRegistry() - name: finding meter setup: | import pint ureg = pint.UnitRegistry() stmts: - name: (attr) stmt: q = ureg.meter - name: (item) stmt: q = ureg['meter'] - name: base units setup: | import pint ureg = pint.UnitRegistry() stmts: - name: meter stmt: ureg.get_base_units('meter') - name: yard stmt: ureg.get_base_units('yard') - name: meter / second stmt: ureg.get_base_units('meter / second') - name: yard / minute stmt: ureg.get_base_units('yard / minute') - name: build cache setup: | import pint ureg = pint.UnitRegistry() stmt: ureg._build_cache() pint-0.8.1/bench/bench_numpy.yaml000066400000000000000000000010301311537545300167450ustar00rootroot00000000000000 - name: NumPy setup: | import numpy as np import pint ureg = pint.UnitRegistry() stmts: - name: cosine setup: | d = np.arange(0, 90, 10) r = np.deg2rad(d) base: np.cos(r) stmts: - name: radian setup: x = r * ureg.radian stmt: np.cos(x) - name: dimensionless setup: x = r * ureg.dimensionless stmt: np.cos(x) - name: degree setup: x = d * ureg.degree stmt: np.cos(x) pint-0.8.1/bors.toml000066400000000000000000000000711311537545300143410ustar00rootroot00000000000000status = [ "continuous-integration/travis-ci/push", ] pint-0.8.1/docs/000077500000000000000000000000001311537545300134315ustar00rootroot00000000000000pint-0.8.1/docs/Makefile000066400000000000000000000130021311537545300150650ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = ifeq ($(wildcard sphinx-build), ) SPHINXBUILD = sphinx-build else SPHINXBUILD = ./sphinx-build endif PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pint.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pint.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/pint" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pint" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." pint-0.8.1/docs/_static/000077500000000000000000000000001311537545300150575ustar00rootroot00000000000000pint-0.8.1/docs/_static/logo-full.jpg000066400000000000000000000270071311537545300174670ustar00rootroot00000000000000JFIFHH@ICC_PROFILE0appl mntrRGB XYZ   acspAPPLappl-appl dscmdescogXYZlwtptrXYZbXYZrTRCcprt8chad,gTRCbTRCmluc enUS&~esES&daDK.deDE,fiFI(frFU(*itIT(VnlNL(nbNO&ptBR&svSE&jaJPRkoKR@zhTWlzhCNruRU"plPL,Yleinen RGB-profiiliGenerisk RGB-profilProfil Gnrique RVBN, RGB 000000u( RGB r_icϏPerfil RGB GenricoAllgemeines RGB-Profilfn RGB cϏeNGenerel RGB-beskrivelseAlgemeen RGB-profiel| RGB \ |Profilo RGB GenericoGeneric RGB Profile1I89 ?@>D8;L RGBUniwersalny profil RGBdescGeneric RGB ProfileGeneric RGB ProfileXYZ Zus4XYZ RXYZ tM=XYZ (6curvtextCopyright 2007 Apple Inc., all rights reserved.sf32 B&lExifMM*JR(iZHH\C     C  \" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((((((()jES((((((((({PE!8{P%+}@ZE^C\zё^0>'pC[X[gE {LBmWLքzpVC3M'<Gxj1Y'fG^km"1#N,QJ599҄ԕ(1QEP(((((({R`s$& K1U(gkm_]&ZE!?,+Ļd[EcFLϢז]Û+ ӓ5y6Lp8d3>Ud.]T+4K,2wيkO)$}P$73mRq9׵xS<ϪO6";n$n|M \(;h'='xJ0|6O"oe?Cۚ_XmR_D,.0GCRƐ\*ZrU~.Qw+rcev~x;Ė/uxH`Ko?OċGTKI7Z. f%+'3QE} Q@Q@Q@Q@Q@Q@2OGyj,пh,R+p/-/c۷m  5! r?uH HSИZ{Hi |Vw׉osŬqke_B$sYrj& 0#$zϯ@nɎO+s؍8U@%k ԇS>H rg#y-P>*602jJ2]յ@sul Upyڙ.%#KaMx]ԟs_H"ڨ-:q׽}tU"%O?,A7(W0q4W?P u1UT43B((((((;F6 2,rR[6eH3*+M|,x4K?Wqߕ0[\x~XFq\ 0O5IT)f-o1$Oyկ5=KB>S57w$~4x-ʚ9=;tg(_3^ $(`QXh,Q{覚kQWv1mY'h 1^PT%m۳;9S$2+c8+'\izm'p縫L$;Bsr! }]C?&xrx9J2V5t^hxºm@{g??ayֿcom/L A!}FeIF&uE#)zk/ؼ'q~]*-IOgMI]N4f?_6Oz1R_N|(QEQEQEQEQEQE(3 IQw z~?o7S?' F-FţrsC#o=_"^p'8_@ /I¿6c c'!FkOp'#7<'U-#eU*X?~qO ?ni'r \lDbMxs=UlQ7g{ץO d2湺RǐbE;&Q3bGf_Kq]ia1q#X_FJk~ź9-~8,;CZ>8'l0:T-QL(((((7g\e 4߆^ T-ճ9 -_a% \+ =ᣫ:nuLeПBLQ&T/\y?j'H99d[I=0+s_bד'܄V8ٸ \y.ާaF0sy=jZ5ځ@I_|QE((((((=(4Y#6gOd .pap!kt@mt>+]ּhxbFm5[ wko+gD.˖K Nj(.)O!_~wM…}B% ʞ`]FY$WGQ߹6'cJZ(?5 ( ( ( ( ( ( CҖn' _ws?(`X{>YIٿ*|g[ cVC 3ȚMdB1]k\7˪:G&o@V7T5601^ilZ1ӥfeFW?T:]-~۹L~Ka>V]<y͞C/M`qSs]<#^x;@ǠU`%WTs69­AqJ LTMexLW3W6W$ۂG Wq=H {ׯJUW>G;T4>xGɟrj?i% k[YEpUߥ9VǁEMj1J)Qҿy~oT<QU5J((((((((@5jև%|>^V9Щ?Mȃڃĝ&8̗1my'| %MT(X"]?iц]yA>x׃$x,srӢ&It] d8S [HnĤCp9J/n?M9lm9W;-tM?Zؓҏ}U41E$?t?g>r(u9I(mD#c+˼R3,0*i$U %G;vvj=b6IvVO_%?moWTv2|Cp%QEQEQEQEQEQEQEQEVfaooP.mQ"?VD=s(vg.4{ vo.%,Xr6>^ͥ&q[WG?~38[jΚGϜmTt8ɱ^&]ٴ^c 7c[g/~ yn#VntsU UWT@xG#F'Ҭ4/JдGT ?xV1ғQ17qa#Uε1Vm|pkx#w9jލzǟxua1k?wySU/$˴ҡ#) ɖR:T'|G)l˜@k\ѿ?bo%RHq[߱mx)F9|ke/mi@HR-~w?J(c ( ( ( ( ( ( ( 1) jwl^[{(yR#CWã6(ҿo~#ig\}@gnp+h'IHܬA~(]O_(zk:Ճjo1cczZg7-dc?=ly)IJsHL# Ҩ]t9YfL uW*^h+/}N w< C-l~moH~"O`ڷ_ (7޹i^N5F: q_υ=G:f=0(((((((()JZC@ncM $e?0Gf6MFh}Y~?z:Ck?ڇ%O+ķ#1Rd3o 8{T|;UJ2a\U5m^K}LƀgVϐ}P83\Ѯ(b (g!j>fj *sɯg%>2 uC c}|(xG\gҿlzO~8|7RN|ik: BLq_χ2: v?nyFMEWڟ&QEQEQEQEQEQEQEQEwQ@O_zٿn?Po.r+}+1xReolʿ.sDө;f=Z?wQbR88@:Wة5"Ug ־Ӛp¦TcY:y'VοUǗ co`(((((((((-<eOOEX/9_wS0XrN08vz_ɾ..sJkeJ/)G0]6Y%¶uKIJ5񖛭M|J~5JNk͸Mԭ)E**&OB=8e_yڤ\o4^,xFmK,vQ+a uLu܍^;'f(zcO\LXyW|=*XJ]UFKsfY !#>չtXE~!'_|@_i8Ֆ +'?Я( 6㷭eHI|ΤRZ(((((((((?t:ʼnدoAx~ bqeSx⿝iCd<7ǐ{4<DA =7~+_Z=,_ W*+}ϽeF>[H5ۂt ^@ݏĐ05j)k Rw={TiFf|CH|C?:y$irzz,H @c^`#T6aX'*x]8L+#qXnXr=у_Z^Ys)૨a_&g[wFk yk_J%625u.Tsۼ[F}gE K_j|QEQEQEQEQEQEQEQETRc<!8=+S?dxƵ΋nu- ke+H r*Xr2y4bSf6~.a|C.>W9O5j>'*U|<9[lyUJڟҾ]`#Ʊ@)XS\lu|*l~y>f1)aPw@UASsOұgEe DKxKϊ,0#{J懅w%%:&Fʃw5xQ(};vZeF G޿CS&n>#|e;~&JHh_1fm'0J\^I׬F5/W:7e/ 6ۦ;xJ D{Vek"щڂ ~AgGׂxn s;:1FxLc޾4?%cxD|an. W̙`;LV"vP uך|&|O ·")!6qs^3ڻdĘQE ( ( ( ( ( ( (_|u?*-Z|=nXaC.7#{ʌ{J^8烚y u}+\r^JF!5t0RG]Fv4|>ÚuƃqCg'5vr<~%©|#//MapGКL%GYap+$ݎZCD|6/vp=+j #k-~1ҿY /Mh!A#oY|@vTa`g oiqk+P?AZu~_կW~] EIs``WCGſ~5<~$YƴoorucTlv7E~hv+nۂg=yt%RL#I+z4_YM<ʑirq6_=n<]ok][tϯҿhuxoJ35e q'r@9=o//vX1.,K]B'-v^^6*IFuPRLo睤ja+?nR Lj׼Y+RͶ*<$3QUCW p8:Sw4iuQ@Š(((((((G^AᦁSDžCA@\Om!bZL42/<0AaLIcOKkO~-c2Z\."LI뎵| [_35;,P9I*4au R+_rM''1N*ЖN<txp$_=_9#m&qiǣ~?|DYn=[j9NzKwt]:wQ,}My_BsR%?7W浍5 wdofj~>h|w79ǐ<÷vS骧ij>бXCߋ j8YmZ ~4a1-64b8#º.Ե?=)c[] N ˷Uǽ5NWSw M9t륕TIݛ;wg$址>x/]־$j0|;+iQyU{^ {~i |3"-[+,"U0:WRq|<𵗂hl47O[D8vII$Aa:cedfnEU((((((((L 4b B)T*\T*84 Ӱ=(֠QE0 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?pint-0.8.1/docs/_templates/000077500000000000000000000000001311537545300155665ustar00rootroot00000000000000pint-0.8.1/docs/_templates/sidebarintro.html000066400000000000000000000013361311537545300211440ustar00rootroot00000000000000

About Pint

Units in Python. You are currently looking at the documentation of version {{ version }}.

Other Formats

You can download the documentation in other formats as well:

Useful Links

pint-0.8.1/docs/_templates/sidebarlogo.html000066400000000000000000000015551311537545300207540ustar00rootroot00000000000000

Logo

About Pint

Units in Python. You are currently looking at the documentation of version {{ version }}.

Other Formats

You can download the documentation in other formats as well:

Useful Links

pint-0.8.1/docs/_themes/000077500000000000000000000000001311537545300150555ustar00rootroot00000000000000pint-0.8.1/docs/_themes/.gitignore000066400000000000000000000000261311537545300170430ustar00rootroot00000000000000*.pyc *.pyo .DS_Store pint-0.8.1/docs/_themes/LICENSE000066400000000000000000000033751311537545300160720ustar00rootroot00000000000000Copyright (c) 2010 by Armin Ronacher. Some rights reserved. Redistribution and use in source and binary forms of the theme, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. We kindly ask you to only use these themes in an unmodified manner just for Flask and Flask-related products, not for unrelated projects. If you like the visual style and want to use it for your own projects, please consider making some larger changes to the themes (such as changing font faces, sizes, colors or margins). THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pint-0.8.1/docs/_themes/README000066400000000000000000000021051311537545300157330ustar00rootroot00000000000000Flask Sphinx Styles =================== This repository contains sphinx styles for Flask and Flask related projects. To use this style in your Sphinx documentation, follow this guide: 1. put this folder as _themes into your docs folder. Alternatively you can also use git submodules to check out the contents there. 2. add this to your conf.py: sys.path.append(os.path.abspath('_themes')) html_theme_path = ['_themes'] html_theme = 'flask' The following themes exist: - 'flask' - the standard flask documentation theme for large projects - 'flask_small' - small one-page theme. Intended to be used by very small addon libraries for flask. The following options exist for the flask_small theme: [options] index_logo = '' filename of a picture in _static to be used as replacement for the h1 in the index.rst file. index_logo_height = 120px height of the index logo github_fork = '' repository name on github for the "fork me" badge pint-0.8.1/docs/_themes/flask/000077500000000000000000000000001311537545300161555ustar00rootroot00000000000000pint-0.8.1/docs/_themes/flask/layout.html000066400000000000000000000017771311537545300203740ustar00rootroot00000000000000{%- extends "basic/layout.html" %} {%- block extrahead %} {{ super() }} {% if theme_touch_icon %} {% endif %} {% endblock %} {%- block relbar2 %} {% if theme_github_fork %} Fork me on GitHub {% endif %} {% endblock %} {% block header %} {{ super() }} {% if pagename == 'index' %}
{% endif %} {% endblock %} {%- block footer %} {% if pagename == 'index' %}
{% endif %} {%- endblock %} pint-0.8.1/docs/_themes/flask/relations.html000066400000000000000000000007711311537545300210500ustar00rootroot00000000000000

Related Topics

{%- endfor %} {%- if prev %}

Previous
{{ prev.title }}

{%- endif %} {%- if next %}

Next
{{ next.title }}

{%- endif %} pint-0.8.1/docs/_themes/flask/static/000077500000000000000000000000001311537545300174445ustar00rootroot00000000000000pint-0.8.1/docs/_themes/flask/static/flasky.css_t000066400000000000000000000144441311537545300220010ustar00rootroot00000000000000/* * flasky.css_t * ~~~~~~~~~~~~ * * :copyright: Copyright 2010 by Armin Ronacher. * :license: Flask Design License, see LICENSE for details. */ {% set page_width = '940px' %} {% set sidebar_width = '220px' %} @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: 'Georgia', serif; font-size: 17px; background-color: white; color: #000; margin: 0; padding: 0; } div.document { width: {{ page_width }}; margin: 30px auto 0 auto; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 {{ sidebar_width }}; } div.sphinxsidebar { width: {{ sidebar_width }}; } hr { border: 1px solid #B1B4B6; } div.body { background-color: #ffffff; color: #3E4349; padding: 0 30px 0 30px; } img.floatingflask { padding: 0 0 10px 10px; float: right; } div.footer { width: {{ page_width }}; margin: 20px auto 30px auto; font-size: 14px; color: #888; text-align: right; } div.footer a { color: #888; } div.related { display: none; } div.sphinxsidebar a { color: #444; text-decoration: none; border-bottom: 1px dotted #999; } div.sphinxsidebar a:hover { border-bottom: 1px solid #999; } div.sphinxsidebar { font-size: 14px; line-height: 1.5; } div.sphinxsidebarwrapper { padding: 18px 10px; } div.sphinxsidebarwrapper p.logo { padding: 0 0 20px 0; margin: 0; text-align: center; } div.sphinxsidebar h3, div.sphinxsidebar h4 { font-family: 'Garamond', 'Georgia', serif; color: #444; font-size: 24px; font-weight: normal; margin: 0 0 5px 0; padding: 0; } div.sphinxsidebar h4 { font-size: 20px; } div.sphinxsidebar h3 a { color: #444; } div.sphinxsidebar p.logo a, div.sphinxsidebar h3 a, div.sphinxsidebar p.logo a:hover, div.sphinxsidebar h3 a:hover { border: none; } div.sphinxsidebar p { color: #555; margin: 10px 0; } div.sphinxsidebar ul { margin: 10px 0; padding: 0; color: #000; } div.sphinxsidebar input { border: 1px solid #ccc; font-family: 'Georgia', serif; font-size: 1em; } /* -- body styles ----------------------------------------------------------- */ a { color: #004B6B; text-decoration: underline; } a:hover { color: #6D4100; text-decoration: underline; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: 'Garamond', 'Georgia', serif; font-weight: normal; margin: 30px 0px 10px 0px; padding: 0; } {% if theme_index_logo %} div.indexwrapper h1 { text-indent: -999999px; background: url({{ theme_index_logo }}) no-repeat center center; height: {{ theme_index_logo_height }}; } {% endif %} div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } div.body h2 { font-size: 180%; } div.body h3 { font-size: 150%; } div.body h4 { font-size: 130%; } div.body h5 { font-size: 100%; } div.body h6 { font-size: 100%; } a.headerlink { color: #ddd; padding: 0 4px; text-decoration: none; } a.headerlink:hover { color: #444; background: #eaeaea; } div.body p, div.body dd, div.body li { line-height: 1.4em; } div.admonition { background: #fafafa; margin: 20px -30px; padding: 10px 30px; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; } div.admonition tt.xref, div.admonition a tt { border-bottom: 1px solid #fafafa; } dd div.admonition { margin-left: -60px; padding-left: 60px; } div.admonition p.admonition-title { font-family: 'Garamond', 'Georgia', serif; font-weight: normal; font-size: 24px; margin: 0 0 10px 0; padding: 0; line-height: 1; } div.admonition p.last { margin-bottom: 0; } div.highlight { background-color: white; } dt:target, .highlight { background: #FAF3E8; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre, tt { font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.9em; } img.screenshot { } tt.descname, tt.descclassname { font-size: 0.95em; } tt.descname { padding-right: 0.08em; } img.screenshot { -moz-box-shadow: 2px 2px 4px #eee; -webkit-box-shadow: 2px 2px 4px #eee; box-shadow: 2px 2px 4px #eee; } table.docutils { border: 1px solid #888; -moz-box-shadow: 2px 2px 4px #eee; -webkit-box-shadow: 2px 2px 4px #eee; box-shadow: 2px 2px 4px #eee; } table.docutils td, table.docutils th { border: 1px solid #888; padding: 0.25em 0.7em; } table.field-list, table.footnote { border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } table.footnote { margin: 15px 0; width: 100%; border: 1px solid #eee; background: #fdfdfd; font-size: 0.9em; } table.footnote + table.footnote { margin-top: -15px; border-top: none; } table.field-list th { padding: 0 0.8em 0 0; } table.field-list td { padding: 0; } table.footnote td.label { width: 0px; padding: 0.3em 0 0.3em 0.5em; } table.footnote td { padding: 0.3em 0.5em; } dl { margin: 0; padding: 0; } dl dd { margin-left: 30px; } blockquote { margin: 0 0 0 30px; padding: 0; } ul, ol { margin: 10px 0 10px 30px; padding: 0; } pre { background: #eee; padding: 7px 30px; margin: 15px -30px; line-height: 1.3em; } dl pre, blockquote pre, li pre { margin-left: -60px; padding-left: 60px; } dl dl pre { margin-left: -90px; padding-left: 90px; } tt { background-color: #ecf0f3; color: #222; /* padding: 1px 2px; */ } tt.xref, a tt { background-color: #FBFBFB; border-bottom: 1px solid white; } a.reference { text-decoration: none; border-bottom: 1px dotted #004B6B; } a.reference:hover { border-bottom: 1px solid #6D4100; } a.footnote-reference { text-decoration: none; font-size: 0.7em; vertical-align: top; border-bottom: 1px dotted #004B6B; } a.footnote-reference:hover { border-bottom: 1px solid #6D4100; } a:hover tt { background: #EEE; } pint-0.8.1/docs/_themes/flask/static/small_flask.css000066400000000000000000000017201311537545300224460ustar00rootroot00000000000000/* * small_flask.css_t * ~~~~~~~~~~~~~~~~~ * * :copyright: Copyright 2010 by Armin Ronacher. * :license: Flask Design License, see LICENSE for details. */ body { margin: 0; padding: 20px 30px; } div.documentwrapper { float: none; background: white; } div.sphinxsidebar { display: block; float: none; width: 102.5%; margin: 50px -30px -20px -30px; padding: 10px 20px; background: #333; color: white; } div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, div.sphinxsidebar h3 a { color: white; } div.sphinxsidebar a { color: #aaa; } div.sphinxsidebar p.logo { display: none; } div.document { width: 100%; margin: 0; } div.related { display: block; margin: 0; padding: 10px 0 20px 0; } div.related ul, div.related ul li { margin: 0; padding: 0; } div.footer { display: none; } div.bodywrapper { margin: 0; } div.body { min-height: 0; padding: 0; } pint-0.8.1/docs/_themes/flask/theme.conf000066400000000000000000000002771311537545300201340ustar00rootroot00000000000000[theme] inherit = basic stylesheet = flasky.css pygments_style = flask_theme_support.FlaskyStyle [options] index_logo = '' index_logo_height = 120px touch_icon = github_fork = hgrecco/pint pint-0.8.1/docs/_themes/flask_theme_support.py000066400000000000000000000114131311537545300215050ustar00rootroot00000000000000# flasky extensions. flasky pygments style based on tango style from pygments.style import Style from pygments.token import Keyword, Name, Comment, String, Error, \ Number, Operator, Generic, Whitespace, Punctuation, Other, Literal class FlaskyStyle(Style): background_color = "#f8f8f8" default_style = "" styles = { # No corresponding class for the following: #Text: "", # class: '' Whitespace: "underline #f8f8f8", # class: 'w' Error: "#a40000 border:#ef2929", # class: 'err' Other: "#000000", # class 'x' Comment: "italic #8f5902", # class: 'c' Comment.Preproc: "noitalic", # class: 'cp' Keyword: "bold #004461", # class: 'k' Keyword.Constant: "bold #004461", # class: 'kc' Keyword.Declaration: "bold #004461", # class: 'kd' Keyword.Namespace: "bold #004461", # class: 'kn' Keyword.Pseudo: "bold #004461", # class: 'kp' Keyword.Reserved: "bold #004461", # class: 'kr' Keyword.Type: "bold #004461", # class: 'kt' Operator: "#582800", # class: 'o' Operator.Word: "bold #004461", # class: 'ow' - like keywords Punctuation: "bold #000000", # class: 'p' # because special names such as Name.Class, Name.Function, etc. # are not recognized as such later in the parsing, we choose them # to look the same as ordinary variables. Name: "#000000", # class: 'n' Name.Attribute: "#c4a000", # class: 'na' - to be revised Name.Builtin: "#004461", # class: 'nb' Name.Builtin.Pseudo: "#3465a4", # class: 'bp' Name.Class: "#000000", # class: 'nc' - to be revised Name.Constant: "#000000", # class: 'no' - to be revised Name.Decorator: "#888", # class: 'nd' - to be revised Name.Entity: "#ce5c00", # class: 'ni' Name.Exception: "bold #cc0000", # class: 'ne' Name.Function: "#000000", # class: 'nf' Name.Property: "#000000", # class: 'py' Name.Label: "#f57900", # class: 'nl' Name.Namespace: "#000000", # class: 'nn' - to be revised Name.Other: "#000000", # class: 'nx' Name.Tag: "bold #004461", # class: 'nt' - like a keyword Name.Variable: "#000000", # class: 'nv' - to be revised Name.Variable.Class: "#000000", # class: 'vc' - to be revised Name.Variable.Global: "#000000", # class: 'vg' - to be revised Name.Variable.Instance: "#000000", # class: 'vi' - to be revised Number: "#990000", # class: 'm' Literal: "#000000", # class: 'l' Literal.Date: "#000000", # class: 'ld' String: "#4e9a06", # class: 's' String.Backtick: "#4e9a06", # class: 'sb' String.Char: "#4e9a06", # class: 'sc' String.Doc: "italic #8f5902", # class: 'sd' - like a comment String.Double: "#4e9a06", # class: 's2' String.Escape: "#4e9a06", # class: 'se' String.Heredoc: "#4e9a06", # class: 'sh' String.Interpol: "#4e9a06", # class: 'si' String.Other: "#4e9a06", # class: 'sx' String.Regex: "#4e9a06", # class: 'sr' String.Single: "#4e9a06", # class: 's1' String.Symbol: "#4e9a06", # class: 'ss' Generic: "#000000", # class: 'g' Generic.Deleted: "#a40000", # class: 'gd' Generic.Emph: "italic #000000", # class: 'ge' Generic.Error: "#ef2929", # class: 'gr' Generic.Heading: "bold #000080", # class: 'gh' Generic.Inserted: "#00A000", # class: 'gi' Generic.Output: "#888", # class: 'go' Generic.Prompt: "#745334", # class: 'gp' Generic.Strong: "bold #000000", # class: 'gs' Generic.Subheading: "bold #800080", # class: 'gu' Generic.Traceback: "bold #a40000", # class: 'gt' } pint-0.8.1/docs/conf.py000066400000000000000000000231701311537545300147330ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # pint documentation build configuration file, created by # sphinx-quickstart on Thu Mar 1 13:33:14 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os import pkg_resources import datetime # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.mathjax'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'pint' author = 'Hernan E. Grecco' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. try: # pragma: no cover version = pkg_resources.get_distribution(project).version except: # pragma: no cover # we seem to have a local copy not installed without setuptools # so the reported version will be unknown version = "unknown" release = version this_year = datetime.date.today().year copyright = '%s, %s' % (this_year, author) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. #html_theme = 'default' html_theme = 'flask' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] html_theme_path = ['_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} html_sidebars = { 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'], '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'] } # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'pintdoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. 'preamble': "".join(( '\DeclareUnicodeCharacter{2212}{-}', # MINUS )), } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'pint.tex', 'pint Documentation', 'Hernan E. Grecco', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pint', 'pint Documentation', ['Hernan E. Grecco'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'pint', 'pint Documentation', 'Hernan E. Grecco', 'pint', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # A tuple containing the cover image and cover page html template filenames. #epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. #epub_exclude_files = [] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} pint-0.8.1/docs/contexts.rst000066400000000000000000000141361311537545300160370ustar00rootroot00000000000000.. _contexts: Contexts ======== 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: .. doctest:: >>> import pint >>> ureg = pint.UnitRegistry() >>> q = 500 * ureg.nm >>> q.to('Hz') Traceback (most recent call last): ... pint.errors.DimensionalityError: Cannot convert from 'nanometer' ([length]) to 'hertz' (1 / [time]) You probably want to use the relation `frequency = speed_of_light / wavelength`: .. doctest:: >>> (ureg.speed_of_light / q).to('Hz') To make this task easy, Pint has the concept of `contexts` which provides conversion rules between dimensions. For example, the relation between wavelength and frequency is defined in the `spectroscopy` context (abbreviated `sp`). You can tell pint to use this context when you convert a quantity to different units. .. doctest:: >>> q.to('Hz', 'spectroscopy') or with the abbreviated form: .. doctest:: >>> q.to('Hz', 'sp') Contexts can be also enabled for blocks of code using the `with` statement: .. doctest:: >>> with ureg.context('sp'): ... q.to('Hz') If you need a particular context in all your code, you can enable it for all operations with the registry:: >>> ureg.enable_contexts('sp') To disable the context, just call:: >>> ureg.disable_contexts() Enabling multiple contexts -------------------------- You can enable multiple contexts: >>> q.to('Hz', 'sp', 'boltzmann') This works also using the `with` statement: >>> with ureg.context('sp', 'boltzmann'): ... q.to('Hz') or in the registry: >>> ureg.enable_contexts('sp', 'boltzmann') >>> q.to('Hz') If a conversion rule between two dimensions appears in more than one context, the one in the last context has precedence. This is easy to remember if you think that the previous syntax is equivalent to nest contexts: >>> with ureg.context('sp'): ... with ureg.context('boltzmann') : ... q.to('Hz') Parameterized contexts ---------------------- Contexts can also take named parameters. For example, in the spectroscopy you can specify the index of refraction of the medium (`n`). In this way you can calculate, for example, the wavelength in water of a laser which on air is 530 nm. .. doctest:: >>> wl = 530. * ureg.nm >>> f = wl.to('Hz', 'sp') >>> f.to('nm', 'sp', n=1.33) Contexts can also accept Pint Quantity objects as parameters. For example, the 'chemistry' context accepts the molecular weight of a substance (as a Quantity with dimensions of [mass]/[substance]) to allow conversion between moles and mass. .. doctest:: >>> substance = 95 * ureg('g') >>> substance.to('moles', 'chemistry', mw = 5 * ureg('g/mol')) Ensuring context when calling a function ---------------------------------------- Pint provides a decorator to make sure that a function called is done within a given context. Just like before, you have to provide as argument the name (or alias) of the context and the parameters that you wish to set. .. doctest:: >>> wl = 530. * ureg.nm >>> @ureg.with_context('sp', n=1.33) ... def f(wl): ... return wl.to('Hz').magnitude >>> f(wl) 398.496240602 This decorator can be combined with **wraps** or **check** decorators described in `wrapping`_ Defining contexts in a file --------------------------- Like all units and dimensions in Pint, `contexts` are defined using an easy to read text syntax. For example, the definition of the spectroscopy context is:: @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 @end The `@context` directive indicates the beginning of the transformations which are finished by the `@end` statement. You can optionally specify parameters for the context in parenthesis. All parameters are named and default values are mandatory. Multiple parameters are separated by commas (like in a python function definition). Finally, you provide the name of the context (e.g. spectroscopy) and, optionally, a short version of the name (e.g. sp) separated by an equal sign. See the definition of the 'chemistry' context in default_en.txt for an example of a multiple-parameter context. Conversions rules are specified by providing source and destination dimensions separated using a colon (`:`) from the equation. A special variable named `value` will be replaced by the source quantity. Other names will be looked first in the context arguments and then in registry. A single forward arrow (`->`) indicates that the equations is used to transform from the first dimension to the second one. A double arrow (`<->`) is used to indicate that the transformation operates both ways. Context definitions are stored and imported exactly like custom units definition file (and can be included in the same file as unit definitions). See "Defining units" for details. Defining contexts programmatically ---------------------------------- You can create `Context` object, and populate the conversion rules using python functions. For example: .. doctest:: >>> ureg = pint.UnitRegistry() >>> c = pint.Context('ab') >>> c.add_transformation('[length]', '[time]', ... lambda ureg, x: ureg.speed_of_light / x) >>> c.add_transformation('[time]', '[length]', ... lambda ureg, x: ureg.speed_of_light * x) >>> ureg.add_context(c) pint-0.8.1/docs/contributing.rst000066400000000000000000000021021311537545300166650ustar00rootroot00000000000000.. _contributing: Contributing to Pint ==================== You can contribute in different ways: Report issues ------------- You can report any issues with the package, the documentation to the Pint `issue tracker`_. Also feel free to submit feature requests, comments or questions. Contribute code --------------- To contribute fixes, code or documentation to Pint, fork Pint in github_ and submit the changes using a pull request against the **master** branch. - If you are fixing a bug, add a test to test_issues.py Also add "Close # as described in the `github docs`_. - If you are submitting new code, add tests and documentation. Pint uses `bors-ng` as a merge bot and therefore every PR is tested before merging. In any case, feel free to use the `issue tracker`_ to discuss ideas for new features or improvements. .. _github: http://github.com/hgrecco/pint .. _`issue tracker`: https://github.com/hgrecco/pint/issues .. _`bors-ng`: https://github.com/bors-ng/bors-ng .. _`github docs`: https://help.github.com/articles/closing-issues-via-commit-messages/pint-0.8.1/docs/defining.rst000066400000000000000000000071501311537545300157510ustar00rootroot00000000000000.. _defining: Defining units ============== In a definition file -------------------- To define units in a persistent way you need to create a unit definition file. Such files are simple text files in which the units are defined as function of other units. For example this is how the minute and the hour are defined in `default_en.txt`:: hour = 60 * minute = h = hr 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. 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 you transverse all definitions, a reference unit is reached. A reference unit is not defined as a function of another units but of a dimension. For the time in `default_en.txt`, this is the `second`:: second = [time] = s = sec By defining `second` as equal to a string `time` in square brackets we indicate that: * `time` is a physical dimension. * `second` is a reference unit. The ability to define basic physical dimensions as well as reference units allows to construct arbitrary units systems. Pint is shipped with a default definition file named `default_en.txt` where `en` stands for English. You can add your own definitions to the end of this file but you will have to be careful to merge when you update Pint. An easier way is to create a new file (e.g. `mydef.txt`) with your definitions:: dog_year = 52 * day = dy and then in Python, you can load it as: >>> from pint import UnitRegistry >>> # First we create the registry. >>> ureg = UnitRegistry() >>> # Then we append the new definitions >>> ureg.load_definitions('/your/path/to/my_def.txt') # doctest: +SKIP If you make a translation of the default units or define a completely new set, you don't want to append the translated definitions so you just give the filename to the constructor:: >>> from pint import UnitRegistry >>> ureg = UnitRegistry('/your/path/to/default_es.txt') # doctest: +SKIP In the definition file, prefixes are identified by a trailing dash:: yocto- = 10.0**-24 = y- It is important to note that prefixed defined in this way can be used with any 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. 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: .. doctest:: >>> from pint import UnitRegistry >>> # We first instantiate the registry. >>> # If we do not provide any parameter, the default unit definitions are used. >>> ureg = UnitRegistry() >>> Q_ = ureg.Quantity # Here we add the unit >>> ureg.define('dog_year = 52 * day = dy') # We create a quantity based on that unit and we convert to years. >>> lassie_lifespan = Q_(10, 'year') >>> print(lassie_lifespan.to('dog_years')) 70.23888438100961 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. You can also add prefixes programmatically: .. doctest:: >>> ureg.define('myprefix- = 30 = my-') where the number indicates the multiplication factor. .. warning:: Units and prefixes added programmatically are forgotten when the program ends. pint-0.8.1/docs/faq.rst000066400000000000000000000020171311537545300147320ustar00rootroot00000000000000.. _faq: Frequently asked questions ========================== Why the name *Pint*? -------------------- Pint is a unit and sounds like Python in the first syllable. Most important, it is a good unit for beer. You mention other similar Python libraries. Can you point me to those? ---------------------------------------------------------------------- `natu `_ `Buckingham `_ `Magnitude `_ `SciMath `_ `Python-quantities `_ `Unum `_ `Units `_ `udunitspy `_ `SymPy `_ `Units `_ `cf units `_ If your are aware of another one, please contribute a patch to the docs. pint-0.8.1/docs/getting.rst000066400000000000000000000042011311537545300156210ustar00rootroot00000000000000.. _getting: Installation ============ Pint has no dependencies except Python_ itself. In runs on Python 2.6 and 3.0+. You can install it (or upgrade to the latest version) using pip_:: $ pip install -U pint That's all! You can check that Pint is correctly installed by starting up python, and importing pint: .. testcode:: >>> import pint # doctest: +SKIP >>> pint.__version__ # doctest: +SKIP .. 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: $ conda install -c conda-forge pint You can check the installation with the following command: >>> pint.test() # doctest: +SKIP On Arch Linux, you can alternatively install Pint from the Arch User Repository (AUR). The latest release is available as `python-pint`_, and packages tracking the master branch of the GitHub repository are available as `python-pint-git`_ and `python2-pint-git`_. Getting the code ---------------- You can also get the code from PyPI_ or GitHub_. You can either clone the public repository:: $ git clone git://github.com/hgrecco/pint.git Download the tarball:: $ curl -OL https://github.com/hgrecco/pint/tarball/master Or, download the zipball:: $ curl -OL https://github.com/hgrecco/pint/zipball/master Once you have a copy of the source, you can embed it in your Python package, or install it into your site-packages easily:: $ python setup.py install .. _easy_install: http://pypi.python.org/pypi/setuptools .. _Python: http://www.python.org/ .. _pip: http://www.pip-installer.org/ .. _`Anaconda CE`: https://store.continuum.io/cshop/anaconda .. _`python-pint`: https://aur.archlinux.org/packages/python-pint/ .. _`python-pint-git`: https://aur.archlinux.org/packages/python-pint-git/ .. _`python2-pint-git`: https://aur.archlinux.org/packages/python2-pint-git/ .. _PyPI: https://pypi.python.org/pypi/Pint/ .. _GitHub: https://github.com/hgrecco/pint pint-0.8.1/docs/index.rst000066400000000000000000000124411311537545300152740ustar00rootroot00000000000000:orphan: Pint: makes units easy ====================== .. image:: _static/logo-full.jpg :alt: Pint: **physical quantities** :class: floatingflask Pint is a Python package to define, operate and manipulate **physical quantities**: the product of a numerical value and a unit of measurement. It allows arithmetic operations between them and conversions from and to different units. It is distributed with a `comprehensive list of physical units, prefixes and constants`_. Due to its modular design, you can extend (or even rewrite!) the complete list without changing the source code. It supports a lot of numpy mathematical operations **without monkey patching or wrapping numpy**. It has a complete test coverage. It runs in Python 2.6+ and 3.2+ with no other dependency. It is licensed under BSD. It is extremely easy and natural to use: .. code-block:: python >>> import pint >>> ureg = pint.UnitRegistry() >>> 3 * ureg.meter + 4 * ureg.cm and you can make good use of numpy if you want: .. code-block:: python >>> import numpy as np >>> [3, 4] * ureg.meter + [4, 3] * ureg.cm >>> np.sum(_) Quick Installation ------------------ To install Pint, simply: .. code-block:: bash $ pip install pint or utilizing conda, with the conda-forge channel: .. code-block:: bash $ conda install -c conda-forge pint and then simply enjoy it! Design principles ----------------- Although there are already a few very good Python packages to handle physical quantities, no one was really fitting my needs. Like most developers, I programmed Pint to scratch my own itches. **Unit parsing**: prefixed and pluralized forms of units are recognized without explicitly defining them. In other words: as the prefix *kilo* and the unit *meter* are defined, Pint understands *kilometers*. This results in a much shorter and maintainable unit definition list as compared to other packages. **Standalone unit definitions**: units definitions are loaded from a text file which is simple and easy to edit. Adding and changing units and their definitions does not involve changing the code. **Advanced string formatting**: a quantity can be formatted into string using `PEP 3101`_ syntax. Extended conversion flags are given to provide symbolic, LaTeX and pretty formatting. Unit name translation is available if Babel_ is installed. **Free to choose the numerical type**: You can use any numerical type (`fraction`, `float`, `decimal`, `numpy.ndarray`, etc). NumPy_ is not required but supported. **NumPy integration**: When you choose to use a NumPy_ ndarray, its methods and ufuncs are supported including automatic conversion of units. For example `numpy.arccos(q)` will require a dimensionless `q` and the units of the output quantity will be radian. **Uncertainties integration**: transparently handles calculations with quantities with uncertainties (like 3.14±0.01) meter via the `uncertainties package`_. **Handle temperature**: conversion between units with different reference points, like positions on a map or absolute temperature scales. **Small codebase**: easy to maintain codebase with a flat hierarchy. **Dependency free**: it depends only on Python and its standard library. **Python 2 and 3**: a single codebase that runs unchanged in Python 2.7+ and Python 3.0+. User Guide ---------- .. toctree:: :maxdepth: 1 getting tutorial numpy nonmult wrapping serialization pitheorem contexts measurement defining performance systems More information ---------------- .. toctree:: :maxdepth: 1 contributing faq One last thing -------------- .. epigraph:: 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 `_ .. _`comprehensive list of physical units, prefixes and constants`: https://github.com/hgrecco/pint/blob/master/pint/default_en.txt .. _`uncertainties package`: https://pythonhosted.org/uncertainties/ .. _`NumPy`: http://www.numpy.org/ .. _`PEP 3101`: https://www.python.org/dev/peps/pep-3101/ .. _`Babel`: http://babel.pocoo.org/ pint-0.8.1/docs/make.bat000066400000000000000000000117441311537545300150450ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pint.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pint.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end pint-0.8.1/docs/measurement.rst000066400000000000000000000033231311537545300165110ustar00rootroot00000000000000.. _measurement: Using Measurements ================== Measurements are the combination of two quantities: the mean value and the error (or uncertainty). The easiest ways to generate a measurement object is from a quantity using the `plus_minus` operator. .. doctest:: >>> import numpy as np >>> from pint import UnitRegistry >>> ureg = UnitRegistry() >>> book_length = (20. * ureg.centimeter).plus_minus(2.) >>> print(book_length) (20.0 +/- 2.0) centimeter .. testsetup:: * import numpy as np from pint import UnitRegistry ureg = UnitRegistry() Q_ = ureg.Quantity You can inspect the mean value, the absolute error and the relative error: .. doctest:: >>> print(book_length.value) 20.0 centimeter >>> print(book_length.error) 2.0 centimeter >>> print(book_length.rel) 0.1 You can also create a Measurement object giving the relative error: .. doctest:: >>> book_length = (20. * ureg.centimeter).plus_minus(.1, relative=True) >>> print(book_length) (20.0 +/- 2.0) centimeter Measurements support the same formatting codes as Quantity. For example, to pretty print a measurement with 2 decimal positions: .. doctest:: >>> print('{:.02fP}'.format(book_length)) (20.00 ± 2.00) centimeter Mathematical operations with Measurements, return new measurements following the `Propagation of uncertainty`_ rules. .. doctest:: >>> print(2 * book_length) (40.0 +/- 4.0) centimeter >>> width = (10 * ureg.centimeter).plus_minus(1) >>> print('{:.02f}'.format(book_length + width)) (30.00 +/- 2.24) centimeter .. note:: only linear combinations are currently supported. .. _`Propagation of uncertainty`: http://en.wikipedia.org/wiki/Propagation_of_uncertainty pint-0.8.1/docs/nonmult.rst000066400000000000000000000136251311537545300156660ustar00rootroot00000000000000.. _nonmult: Temperature conversion ====================== Unlike meters and seconds, the temperature units fahrenheits and celsius are non-multiplicative units. These temperature units are expressed in a system with a reference point, and relations between temperature units include not only a scaling factor but also an offset. Pint supports these type of units and conversions between them. The default definition file includes fahrenheits, celsius, kelvin and rankine abbreviated as degF, degC, degK, and degR. For example, to convert from celsius to fahrenheit: .. testsetup:: from pint import UnitRegistry ureg = UnitRegistry() ureg.default_format = '.3f' Q_ = ureg.Quantity .. doctest:: >>> from pint import UnitRegistry >>> ureg = UnitRegistry() >>> Q_ = ureg.Quantity >>> home = Q_(25.4, ureg.degC) >>> print(home.to('degF')) 77.7200004 degF or to other kelvin or rankine: .. doctest:: >>> print(home.to('kelvin')) 298.55 kelvin >>> print(home.to('degR')) 537.39 degR Additionally, for every non-multiplicative temperature unit in the registry, there is also a *delta* counterpart to specify differences. Absolute units have no *delta* counterpart. For example, the change in celsius is equal to the change in kelvin, but not in fahrenheit (as the scaling factor is different). .. doctest:: >>> increase = 12.3 * ureg.delta_degC >>> print(increase.to(ureg.kelvin)) 12.3 kelvin >>> print(increase.to(ureg.delta_degF)) 22.14 delta_degF Subtraction of two temperatures given in offset units yields a *delta* unit: .. doctest:: >>> Q_(25.4, ureg.degC) - Q_(10., ureg.degC) You can add or subtract a quantity with *delta* unit and a quantity with offset unit: .. doctest:: >>> Q_(25.4, ureg.degC) + Q_(10., ureg.delta_degC) >>> Q_(25.4, ureg.degC) - Q_(10., ureg.delta_degC) If you want to add a quantity with absolute unit to one with offset unit, like here .. doctest:: >>> heating_rate = 0.5 * ureg.kelvin/ureg.min >>> 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). you have to avoid the ambiguity by either converting the offset unit to the absolute unit before addition .. doctest:: >>> Q_(10., ureg.degC).to(ureg.kelvin) + heating_rate * Q_(30, ureg.min) or convert the absolute unit to a *delta* unit: .. doctest:: >>> Q_(10., ureg.degC) + heating_rate.to('delta_degC/min') * Q_(30, ureg.min) In contrast to subtraction, the addition of quantities with offset units is ambiguous, e.g. for *10 degC + 100 degC* two different result are reasonable depending on the context, *110 degC* or *383.15 °C (= 283.15 K + 373.15 K)*. Because of this ambiguity pint raises an error for the addition of two quantities with offset units (since pint-0.6). Quantities with *delta* units are multiplicative: .. doctest:: >>> speed = 60. * ureg.delta_degC / ureg.min >>> print(speed.to('delta_degC/second')) 1.0 delta_degC / second However, multiplication, division and exponentiation of quantities with offset units is problematic just like addition. Pint (since version 0.6) will by default raise an error when a quantity with offset unit is used in these operations. Due to this quantities with offset units cannot be created like other quantities by multiplication of magnitude and unit but have to be explicitly created: .. doctest:: >>> ureg = UnitRegistry() >>> home = 25.4 * ureg.degC Traceback (most recent call last): ... pint.errors.OffsetUnitCalculusError: Ambiguous operation with offset unit (degC). >>> Q_(25.4, ureg.degC) As an alternative to raising an error, pint can be configured to work more relaxed via setting the UnitRegistry parameter *autoconvert_offset_to_baseunit* to true. In this mode, pint behaves differently: * Multiplication of a quantity with a single offset unit with order +1 by a number or ndarray yields the quantity in the given unit. .. doctest:: >>> ureg = UnitRegistry(autoconvert_offset_to_baseunit = True) >>> T = 25.4 * ureg.degC >>> T * Before all other multiplications, all divisions and in case of exponentiation [#f1]_ involving quantities with offset-units, pint will convert the quantities with offset units automatically to the corresponding base unit before performing the operation. >>> 1/T >>> T * 10 * ureg.meter You can change the behaviour at any time: >>> ureg.autoconvert_offset_to_baseunit = False >>> 1/T Traceback (most recent call last): ... pint.errors.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: .. doctest:: >>> print(ureg.parse_units('degC/meter')) delta_degC / meter but not here: .. doctest:: >>> print(ureg.parse_units('degC')) degC You can override this behaviour: .. doctest:: >>> print(ureg.parse_units('degC/meter', as_delta=False)) degC / meter Note that the magnitude is left unchanged: .. doctest:: >>> Q_(10, 'degC/meter') To define a new temperature, you need to specify the offset. For example, this is the definition of the celsius and fahrenheit:: degC = degK; offset: 273.15 = celsius degF = 5 / 9 * degK; offset: 255.372222 = fahrenheit You do not need to define *delta* units, as they are defined automatically. .. [#f1] If the exponent is +1, the quantity will not be converted to base unit but remains unchanged. pint-0.8.1/docs/numpy.rst000066400000000000000000000125761311537545300153460ustar00rootroot00000000000000.. _numpy: NumPy support ============= The magnitude of a Pint quantity can be of any numerical type and you are free to choose it according to your needs. In numerical applications, it is quite convenient to use `NumPy ndarray`_ and therefore they are supported by Pint. First, we import the relevant packages: .. doctest:: >>> import numpy as np >>> from pint import UnitRegistry >>> ureg = UnitRegistry() >>> Q_ = ureg.Quantity .. testsetup:: * import numpy as np from pint import UnitRegistry ureg = UnitRegistry() Q_ = ureg.Quantity and then we create a quantity the standard way .. doctest:: >>> legs1 = Q_(np.asarray([3., 4.]), 'meter') >>> print(legs1) [ 3. 4.] meter or we use the property that Pint converts iterables into NumPy ndarrays to simply write: .. doctest:: >>> legs1 = [3., 4.] * ureg.meter >>> print(legs1) [ 3. 4.] meter All usual Pint methods can be used with this quantity. For example: .. doctest:: >>> print(legs1.to('kilometer')) [ 0.003 0.004] kilometer >>> print(legs1.dimensionality) [length] >>> legs1.to('joule') Traceback (most recent call last): ... pint.errors.DimensionalityError: Cannot convert from 'meter' ([length]) to 'joule' ([length] ** 2 * [mass] / [time] ** 2) NumPy functions are supported by Pint. For example if we define: .. doctest:: >>> legs2 = [400., 300.] * ureg.centimeter >>> print(legs2) [ 400. 300.] centimeter we can calculate the hypotenuse of the right triangles with legs1 and legs2. .. doctest:: >>> hyps = np.hypot(legs1, legs2) >>> print(hyps) [ 5. 5.] meter Notice that before the `np.hypot` was used, the numerical value of legs2 was internally converted to the units of legs1 as expected. Similarly, when you apply a function that expects angles in radians, a conversion is applied before the requested calculation: .. doctest:: >>> angles = np.arccos(legs2/hyps) >>> print(angles) [ 0.64350111 0.92729522] radian You can convert the result to degrees using the corresponding NumPy function: .. doctest:: >>> print(np.rad2deg(angles)) [ 36.86989765 53.13010235] degree Applying a function that expects angles to a quantity with a different dimensionality results in an error: .. doctest:: >>> np.arccos(legs2) Traceback (most recent call last): ... pint.errors.DimensionalityError: Cannot convert from 'centimeter' ([length]) to 'dimensionless' (dimensionless) Support -------- The following ufuncs_ can be applied to a Quantity object: - **Math operations**: add, subtract, multiply, divide, logaddexp, logaddexp2, true_divide, floor_divide, negative, remainder mod, fmod, absolute, rint, sign, conj, exp, exp2, log, log2, log10, expm1, log1p, sqrt, square, reciprocal - **Trigonometric functions**: sin, cos, tan, arcsin, arccos, arctan, arctan2, hypot, sinh, cosh, tanh, arcsinh, arccosh, arctanh, deg2rad, rad2deg - **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: - 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 `Quantity` is not a subclass of `ndarray`. This might change in the future, but for this reason functions that call `numpy.asanyarray` are currently not supported. These functions are: - unwrap, trapz, diff, ediff1d, fix, gradient, cross, ones_like Comments -------- What follows is a short discussion about how NumPy support is implemented in Pint's `Quantity` Object. For the supported functions, Pint expects certain units and attempts to convert the input (or inputs). For example, the argument of the exponential function (`numpy.exp`) must be dimensionless. Units will be simplified (converting the magnitude appropriately) and `numpy.exp` will be applied to the resulting magnitude. If the input is not dimensionless, a `DimensionalityError` exception will be raised. In some functions that take 2 or more arguments (e.g. `arctan2`), the second argument is converted to the units of the first. Again, a `DimensionalityError` exception will be raised if this is not possible. This behaviour introduces some performance penalties and increased memory usage. Quantities that must be converted to other units require additional memory and CPU cycles. On top of this, all `ufuncs` are implemented in the `Quantity` class by overriding `__array_wrap__`, a NumPy hook that is executed 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 converted to the right units. Therefore, for numerically intensive code, you 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 pint-0.8.1/docs/performance.rst000066400000000000000000000060541311537545300164710ustar00rootroot00000000000000.. _performance: Optimizing Performance ====================== Pint can impose a significant performance overhead on computationally-intensive problems. The following are some suggestions for getting the best performance. .. note:: Examples below are based on the IPython shell (which provides the handy %timeit extension), so they will not work in a standard Python interpreter. Use magnitudes when possible ---------------------------- It's significantly faster to perform mathematical operations on magnitudes (even though your'e still using pint to retrieve them from a quantity object). .. doctest:: In [1]: from pint import UnitRegistry In [2]: ureg = UnitRegistry() In [3]: q1 =ureg('1m') In [5]: q2=ureg('2m') In [6]: %timeit (q1-q2) 100000 loops, best of 3: 7.9 µs per loop In [7]: %timeit (q1.magnitude-q2.magnitude) 1000000 loops, best of 3: 356 ns per loop This is especially important when using pint Quantities in conjunction with an iterative solver, such as the `brentq method`_ from scipy: .. doctest:: In [1]: from scipy.optimize import brentq In [2]: def foobar_with_quantity(x): # find the value of x that equals q2 # assign x the same units as q2 qx = ureg(str(x)+str(q2.units)) # compare the two quantities, then take their magnitude because # brentq requires a dimensionless return type return (qx - q2).magnitude In [3]: def foobar_with_magnitude(x): # find the value of x that equals q2 # don't bother converting x to a quantity, just compare it with q2's magnitude return x - q2.magnitude In [4]: %timeit brentq(foobar_with_quantity,0,q2.magnitude) 1000 loops, best of 3: 310 µs per loop In [5]: %timeit brentq(foobar_with_magnitude,0,q2.magnitude) 1000000 loops, best of 3: 1.63 µs per loop Bear in mind that altering computations like this **loses the benefits of automatic unit conversion**, so use with care. A safer method: wrapping ------------------------ A better way to use magnitudes is to use pint's wraps decorator (See :ref:`wrapping`). By decorating a function with wraps, you pass only the magnitude of an argument to the function body according to units you specify. As such this method is safer in that you are sure the magnitude is supplied in the correct units. .. doctest:: In [1]: import pint In [2]: ureg = pint.UnitRegistry() In [3]: import numpy as np In [4]: def f(x, y): return (x - y) / (x + y) * np.log(x/y) In [5]: @ureg.wraps(None, ('meter', 'meter')) def g(x, y): return (x - y) / (x + y) * np.log(x/y) In [6]: a = 1 * ureg.meter In [7]: b = 1 * ureg.centimeter In [8]: %timeit f(a, b) 1000 loops, best of 3: 312 µs per loop In [9]: %timeit g(a, b) 10000 loops, best of 3: 65.4 µs per loop .. _`brentq method`: http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.brentq.html pint-0.8.1/docs/pitheorem.rst000066400000000000000000000071111311537545300161570ustar00rootroot00000000000000.. _pitheorem: Buckingham Pi Theorem ===================== `Buckingham π theorem`_ states that an equation involving *n* number of physical variables which are expressible in terms of *k* independent fundamental physical quantities can be expressed in terms of *p = n - k* dimensionless parameters. .. testsetup:: * from pint import UnitRegistry ureg = UnitRegistry() Q_ = ureg.Quantity To start with a very simple case, consider that you want to find a dimensionless quantity involving the magnitudes `V`, `T` and `L` with dimensions `[length]/[time]`, `[time]` and `[length]` respectively. .. doctest:: >>> from pint import pi_theorem >>> pi_theorem({'V': '[length]/[time]', 'T': '[time]', 'L': '[length]'}) [{'V': 1.0, 'T': 1.0, 'L': -1.0}] The result indicates that a dimensionless quantity can be obtained by multiplying `V` by `T` and the inverse of `L`. Which can be pretty printed using the `Pint` formatter: .. doctest:: >>> from pint import formatter >>> result = pi_theorem({'V': '[length]/[time]', 'T': '[time]', 'L': '[length]'}) >>> print(formatter(result[0].items())) T * V / L You can also apply the Buckingham π theorem associated to a Registry. In this case, you can use derived dimensions such as speed: .. doctest:: >>> from pint import UnitRegistry >>> ureg = UnitRegistry() >>> ureg.pi_theorem({'V': '[speed]', 'T': '[time]', 'L': '[length]'}) [{'V': 1.0, 'T': 1.0, 'L': -1.0}] or unit names: .. doctest:: >>> ureg.pi_theorem({'V': 'meter/second', 'T': 'second', 'L': 'meter'}) [{'V': 1.0, 'T': 1.0, 'L': -1.0}] or quantities: >>> Q_ = ureg.Quantity >>> ureg.pi_theorem({'V': Q_(1, 'meter/second'), ... 'T': Q_(1, 'second'), ... 'L': Q_(1, 'meter')}) [{'V': 1.0, 'T': 1.0, 'L': -1.0}] Application to the pendulum --------------------------- There are 3 fundamental physical units in this equation: time, mass, and length, and 4 dimensional variables, T (oscillation period), M (mass), L (the length of the string), and g (earth gravity). Thus we need only 4 − 3 = 1 dimensionless parameter. .. doctest:: >>> ureg.pi_theorem({'T': '[time]', ... 'M': '[mass]', ... 'L': '[length]', ... 'g': '[acceleration]'}) [{'T': 2.0, 'g': 1.0, 'L': -1.0}] which means that the dimensionless quantity is: .. math:: \Pi = \frac{g T^2}{L} and therefore: .. math:: T = constant \sqrt{\frac{L}{g}} (In case you wonder, the constant is equal to 2 π, but this is outside the scope of this help) Pressure loss in a pipe ----------------------- What is the pressure loss `p` in a pipe with length `L` and diameter `D` for a fluid with density `d`, and viscosity `m` travelling with speed `v`? As pressure, mass, volume, viscosity and speed are defined as derived dimensions in the registry, we only need to explicitly write the density dimensions. .. doctest:: >>> ureg.pi_theorem({'p': '[pressure]', ... 'L': '[length]', ... 'D': '[length]', ... 'd': '[mass]/[volume]', ... 'm': '[viscosity]', ... 'v': '[speed]' ... }) # doctest: +SKIP [{'p': 1.0, 'm': -2.0, 'd': 1.0, 'L': 2.0}, {'v': 1.0, 'm': -1.0, 'd': 1.0, 'L': 1.0}, {'L': -1.0, 'D': 1.0}] The second dimensionless quantity is the `Reynolds Number`_ .. _`Buckingham π theorem`: http://en.wikipedia.org/wiki/Buckingham_%CF%80_theorem .. _`Reynolds Number`: http://en.wikipedia.org/wiki/Reynolds_number pint-0.8.1/docs/serialization.rst000066400000000000000000000070561311537545300170500ustar00rootroot00000000000000.. _serialization: Serialization ============= In order to dump a **Quantity** to disk, store it in a database or transmit it over the wire you need to be able to serialize and then deserialize the object. The easiest way to do this is by converting the quantity to a string: .. testsetup:: * import pint .. doctest:: >>> import pint >>> ureg = pint.UnitRegistry() >>> duration = 24.2 * ureg.years >>> duration >>> serialized = str(duration) >>> print(serialized) 24.2 year Remember that you can easily control the number of digits in the representation as shown in :ref:`sec-string-formatting`. You dump/store/transmit the content of serialized ('24.2 year'). When you want to recover it in another process/machine, you just: .. doctest:: >>> import pint >>> ureg = pint.UnitRegistry() >>> duration = ureg('24.2 year') >>> print(duration) 24.2 year Notice that the serialized quantity is likely to be parsed in **another** registry as shown in this example. Pint Quantities do not exist on their own but they are always related to a **UnitRegistry**. Everything will work as expected if both registries, are compatible (e.g. they were created using the same definition file). However, things could go wrong if the registries are incompatible. For example, **year** could not be defined in the target registry. Or what is even worse, it could be defined in a different way. Always have to keep in mind that the interpretation and conversion of Quantities are 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: .. doctest:: >>> to_serialize = duration.to_tuple() >>> print(to_serialize) (24.2, (('year', 1.0),)) And then you can just pickle that: >>> import pickle >>> serialized = pickle.dumps(to_serialize, -1) To unpickle, just >>> loaded = pickle.loads(serialized) >>> ureg.Quantity.from_tuple(loaded) (To pickle to and from a file just use the dump and load method as described in _Pickle) You can use the same mechanism with any serialization protocol, not only with binary ones. (In fact, version 0 of the Pickle protocol is ASCII). Other common serialization protocols/packages are json_, yaml_, shelve_, hdf5_ (or via PyTables_) and dill_. Notice that not all of these packages will serialize properly the magnitude (which can be any numerical type such as `numpy.ndarray`). Using the serialize_ package you can load and read from multiple formats: >>> from serialize import dump, load, register_class >>> register_class(ureg.Quantity, ureg.Quantity.to_tuple, ureg.Quantity.from_tuple) >>> dump(duration, 'output.yaml') >>> r = load('output.yaml') (Check out the serialize_ docs for more information) .. _serialize: https://github.com/hgrecco/serialize .. _Pickle: http://docs.python.org/3/library/pickle.html .. _json: http://docs.python.org/3/library/json.html .. _yaml: http://pyyaml.org/ .. _shelve: http://docs.python.org/3.4/library/shelve.html .. _hdf5: http://www.h5py.org/ .. _PyTables: http://www.pytables.org .. _dill: https://pypi.python.org/pypi/dill pint-0.8.1/docs/systems.rst000066400000000000000000000050041311537545300156710ustar00rootroot00000000000000.. _systems: Different Unit Systems (and default units) ========================================== Pint Unit Registry has the concept of system, which is a group of units >>> import pint >>> ureg = pint.UnitRegistry(system='mks') >>> ureg.default_system 'mks' This has an effect in the base units. For example: >>> q = 3600. * ureg.meter / ureg.hour >>> q.to_base_units() But if you change to cgs: >>> ureg.default_system = 'cgs' >>> q.to_base_units() or more drastically to: >>> ureg.default_system = 'imperial' >>> '{:.3f}'.format(q.to_base_units()) '1.094 yard / second' ..warning: In versions previous to 0.7 `to_base_units` returns quantities in the units of the definition files (which are called root units). For the definition file bundled with pint this is meter/gram/second. To get back this behaviour use `to_root_units`, set `ureg.system = None` You can also use system to narrow down the list of compatible units: >>> ureg.default_system = 'mks' >>> ureg.get_compatible_units('meter') frozenset({, }) or for imperial units: >>> ureg.default_system = 'imperial' >>> ureg.get_compatible_units('meter') frozenset({, , , , , , }) You can check which unit systems are available: >>> dir(ureg.sys) ['US', 'cgs', 'imperial', 'mks'] Or which units are available within a particular system: >>> dir(ureg.sys.imperial) ['UK_hundredweight', 'UK_ton', 'acre_foot', 'cubic_foot', 'cubic_inch', 'cubic_yard', 'drachm', 'foot', 'grain', 'imperial_barrel', 'imperial_bushel', 'imperial_cup', 'imperial_fluid_drachm', 'imperial_fluid_ounce', 'imperial_gallon', 'imperial_gill', 'imperial_peck', 'imperial_pint', 'imperial_quart', 'inch', 'long_hunderweight', 'long_ton', 'mile', 'ounce', 'pound', 'quarter', 'short_hunderdweight', 'short_ton', 'square_foot', 'square_inch', 'square_mile', 'square_yard', 'stone', 'yard'] Notice that this give you the opportunity to choose within units with colliding names: >>> (1 * ureg.sys.imperial.pint).to('liter') >>> (1 * ureg.sys.US.pint).to('liter') >>> (1 * ureg.sys.US.pint).to(ureg.sys.imperial.pint) pint-0.8.1/docs/tutorial.rst000066400000000000000000000216221311537545300160310ustar00rootroot00000000000000.. _tutorial: Tutorial ======== Converting Quantities --------------------- Pint has the concept of Unit Registry, an object within which units are defined and handled. You start by creating your registry: >>> from pint import UnitRegistry >>> ureg = UnitRegistry() .. testsetup:: * from pint import UnitRegistry ureg = UnitRegistry() Q_ = ureg.Quantity If no parameter is given to the constructor, the unit registry is populated with the default list of units and prefixes. You can now simply use the registry in the following way: .. doctest:: >>> distance = 24.0 * ureg.meter >>> print(distance) 24.0 meter >>> time = 8.0 * ureg.second >>> print(time) 8.0 second >>> print(repr(time)) In this code `distance` and `time` are physical quantity objects (`Quantity`). Physical quantities can be queried for their magnitude, units, and dimensionality: .. doctest:: >>> print(distance.magnitude) 24.0 >>> print(distance.units) meter >>> print(distance.dimensionality) [length] and can handle mathematical operations between: .. doctest:: >>> speed = distance / time >>> print(speed) 3.0 meter / second As unit registry knows about the relationship between different units, you can convert quantities to the unit of choice: .. doctest:: >>> speed.to(ureg.inch / ureg.minute ) This method returns a new object leaving the original intact as can be seen by: .. doctest:: >>> print(speed) 3.0 meter / second If you want to convert in-place (i.e. without creating another object), you can use the `ito` method: .. doctest:: >>> speed.ito(ureg.inch / ureg.minute ) >>> speed >>> print(speed) 7086.614173228345 inch / minute If you ask Pint to perform an invalid conversion: .. doctest:: >>> 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) There are also methods 'to_base_units' and 'ito_base_units' which automatically convert to the reference units with the correct dimensionality: .. doctest:: >>> height = 5.0 * ureg.foot + 9.0 * ureg.inch >>> print(height) 5.75 foot >>> print(height.to_base_units()) 1.7526 meter >>> print(height) 5.75 foot >>> height.ito_base_units() >>> print(height) 1.7526 meter In some cases it is useful to define physical quantities objects using the class constructor: .. doctest:: >>> Q_ = ureg.Quantity >>> Q_(1.78, ureg.meter) == 1.78 * ureg.meter True (I tend to abbreviate Quantity as `Q_`) The built-in parser recognizes prefixed and pluralized units even though they are not in the definition list: .. doctest:: >>> distance = 42 * ureg.kilometers >>> print(distance) 42 kilometer >>> print(distance.to(ureg.meter)) 42000.0 meter If you try to use a unit which is not in the registry: .. doctest:: >>> speed = 23 * ureg.snail_speed Traceback (most recent call last): ... pint.errors.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` String parsing -------------- Pint can also handle units provided as strings: .. doctest:: >>> 2.54 * ureg.parse_expression('centimeter') or using the registry as a callable for a short form for `parse_expression`: .. doctest:: >>> 2.54 * ureg('centimeter') or using the `Quantity` constructor: .. doctest:: >>> Q_(2.54, 'centimeter') Numbers are also parsed, so you can use an expression: .. doctest:: >>> ureg('2.54 * centimeter') or: .. doctest:: >>> Q_('2.54 * centimeter') or leave out the `*` altogether: .. doctest:: >>> Q_('2.54cm') This enables you to build a simple unit converter in 3 lines: .. doctest:: >>> user_input = '2.54 * centimeter to inch' >>> src, dst = user_input.split(' to ') >>> Q_(src).to(dst) Dimensionless quantities can also be parsed into an appropriate object: .. doctest:: >>> ureg('2.54') 2.54 >>> type(ureg('2.54')) or .. doctest:: >>> Q_('2.54') >>> type(Q_('2.54')) .Quantity'> .. note:: Since version 0.7, Pint **does not** use eval_ under the hood. This change removes the `serious security problems`_ that the system is exposed to when parsing information from untrusted sources. .. _sec-string-formatting: String formatting ----------------- Pint's physical quantities can be easily printed: .. doctest:: >>> accel = 1.3 * ureg['meter/second**2'] >>> # The standard string formatting code >>> print('The str is {!s}'.format(accel)) The str is 1.3 meter / second ** 2 >>> # The standard representation formatting code >>> print('The repr is {!r}'.format(accel)) The repr is >>> # Accessing useful attributes >>> print('The magnitude is {0.magnitude} with units {0.units}'.format(accel)) The magnitude is 1.3 with units meter / second ** 2 .. note:: In Python 2.6, unnumbered placeholders are invalid. Therefore you need to write `{0}` instead of `{}`, `{0!s}` instead of `{!s}` in string formatting operations. But Pint also extends the standard formatting capabilities for unicode and LaTeX representations: .. doctest:: >>> accel = 1.3 * ureg['meter/second**2'] >>> # Pretty print >>> 'The pretty representation is {:P}'.format(accel) 'The pretty representation is 1.3 meter/second²' >>> # Latex print >>> 'The latex representation is {:L}'.format(accel) 'The latex representation is 1.3\\ \\frac{\\mathrm{meter}}{\\mathrm{second}^{2}}' >>> # HTML print >>> 'The HTML representation is {:H}'.format(accel) 'The HTML representation is 1.3 meter/second2' .. note:: In Python 2, run ``from __future__ import unicode_literals`` or prefix pretty formatted strings with `u` to prevent ``UnicodeEncodeError``. If you want to use abbreviated unit names, prefix the specification with `~`: .. doctest:: >>> 'The str is {:~}'.format(accel) 'The str is 1.3 m / s ** 2' >>> 'The pretty representation is {:~P}'.format(accel) 'The pretty representation is 1.3 m/s²' The same is true for latex (`L`) and HTML (`H`) specs. Pint also supports the LaTeX siunitx package: .. doctest:: >>> accel = 1.3 * ureg['meter/second**2'] >>> # siunitx Latex print >>> print('The siunitx representation is {:Lx}'.format(accel)) The siunitx representation is \SI[]{1.3}{\meter\per\second\squared} Additionally, you can specify a default format specification: .. doctest:: >>> 'The acceleration is {}'.format(accel) 'The acceleration is 1.3 meter / second ** 2' >>> ureg.default_format = 'P' >>> 'The acceleration is {}'.format(accel) 'The acceleration is 1.3 meter/second²' Finally, if Babel_ is installed you can translate unit names to any language .. doctest:: >>> accel.format_babel(locale='fr_FR') '1.3 mètre par seconde²' Using Pint in your projects --------------------------- If you use Pint in multiple modules within your Python package, you normally want to avoid creating multiple instances of the unit registry. The best way to do this is by instantiating the registry in a single place. For example, you can add the following code to your package `__init__.py`:: from pint import UnitRegistry ureg = UnitRegistry() Q_ = ureg.Quantity Then in `yourmodule.py` the code would be:: from . import ureg, Q_ length = 10 * ureg.meter my_speed = Q_(20, 'm/s') If you are pickling and unplicking Quantities within your project, you should also define the registry as the application registry:: from pint import UnitRegistry, set_application_registry ureg = UnitRegistry() set_application_registry(ureg) .. warning:: There are no global units in Pint. All units belong to a registry and you can have multiple registries instantiated at the same time. However, you are not supposed to operate between quantities that belong to different registries. Never do things like this: .. doctest:: >>> q1 = 10 * UnitRegistry().meter >>> q2 = 10 * UnitRegistry().meter >>> q1 + q2 Traceback (most recent call last): ... ValueError: Cannot operate with Quantity and Quantity of different registries. >>> id(q1._REGISTRY) == id(q2._REGISTRY) False .. _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/ pint-0.8.1/docs/wrapping.rst000066400000000000000000000137111311537545300160150ustar00rootroot00000000000000.. _wrapping: Wrapping and checking functions =============================== In some cases you might want to use pint with a pre-existing web service or library which is not units aware. Or you might want to write a fast implementation of a numerical algorithm that requires the input values in some specific units. For example, consider a function to return the period of the pendulum within a hypothetical physics library. The library does not use units, but instead requires you to provide numerical values in certain units: .. testsetup:: * import math G = 9.806650 def pendulum_period(length): return 2*math.pi*math.sqrt(length/G) def pendulum_period2(length, swing_amplitude): pass def pendulum_period_maxspeed(length, swing_amplitude): pass def pendulum_period_error(length): pass .. doctest:: >>> from simple_physics import pendulum_period # doctest: +SKIP >>> help(pendulum_period) # doctest: +SKIP Help on function pendulum_period in module simple_physics: pendulum_period(length) Return the pendulum period in seconds. The length of the pendulum must be provided in meters. >>> pendulum_period(1) 2.0064092925890407 This behaviour is very error prone, in particular when combining multiple libraries. You could wrap this function to use Quantities instead: .. doctest:: >>> from pint import UnitRegistry >>> ureg = UnitRegistry() >>> def mypp_caveman(length): ... return pendulum_period(length.to(ureg.meter).magnitude) * ureg.second and: .. doctest:: >>> mypp_caveman(100 * ureg.centimeter) Pint provides a more convenient way to do this: .. doctest:: >>> mypp = ureg.wraps(ureg.second, ureg.meter)(pendulum_period) Or in the decorator format: .. doctest:: >>> @ureg.wraps(ureg.second, ureg.meter) ... def mypp(length): ... return pendulum_period(length) >>> mypp(100 * ureg.centimeter) `wraps` takes 3 input arguments: - **ret**: the return units. Use None to skip conversion. - **args**: the inputs units for each argument, as an iterable. Use None to skip conversion of any given element. - **strict**: if `True` all convertible arguments must be a Quantity and others will raise a ValueError (True by default) Strict Mode ----------- By default, the function is wrapped in `strict` mode. In this mode, the input arguments assigned to units must be a Quantities. .. doctest:: >>> mypp(1. * ureg.meter) >>> mypp(1.) Traceback (most recent call last): ... ValueError: A wrapped function using strict=True requires quantity for all arguments with not None units. (error found for meter, 1.0) To enable using non-Quantity numerical values, set strict to False`. .. doctest:: >>> mypp_ns = ureg.wraps(ureg.second, ureg.meter, False)(pendulum_period) >>> mypp_ns(1. * ureg.meter) >>> mypp_ns(1.) In this mode, the value is assumed to have the correct units. Multiple arguments or return values ----------------------------------- For a function with more arguments, use a tuple: .. doctest:: >>> from simple_physics import pendulum_period2 # doctest: +SKIP >>> help(pendulum_period2) # doctest: +SKIP Help on function pendulum_period2 in module simple_physics: pendulum_period2(length, swing_amplitude) Return the pendulum period in seconds. The length of the pendulum must be provided in meters. The swing_amplitude must be in radians. >>> mypp2 = ureg.wraps(ureg.second, (ureg.meter, ureg.radians))(pendulum_period2) ... Or if the function has multiple outputs: .. doctest:: >>> mypp3 = ureg.wraps((ureg.second, ureg.meter / ureg.second), ... (ureg.meter, ureg.radians))(pendulum_period_maxspeed) ... If there are more return values than specified units, ``None`` is assumed for 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') def solar_position(lat, lon, press, tamb, timestamp): return zenith, azimuth, airmass Specifying relations between arguments -------------------------------------- In certain cases the actual units but just their relation. This is done using string starting with the equal sign `=`: .. doctest:: >>> @ureg.wraps('=A**2', ('=A', '=A')) ... def sqsum(x, y): ... return x * x + 2 * x * y + y * y which can be read as the first argument (`x`) has certain units (we labeled them `A`), the second argument (`y`) has the same units as the first (`A` again). The return value has the unit of `x` squared (`A**2`) You can use more than one label: >>> @ureg.wraps('=A**2*B', ('=A', '=A*B', '=B')) ... def some_function(x, y, z): ... pass Ignoring an argument or return value ------------------------------------ To avoid the conversion of an argument or return value, use None .. doctest:: >>> mypp3 = ureg.wraps((ureg.second, None), ureg.meter)(pendulum_period_error) Checking dimensionality ======================= When you want pint quantities to be used as inputs to your functions, pint provides a wrapper to ensure units are of correct type - or more precisely, they match the expected dimensionality of the physical quantity. Similar to wraps(), you can pass None to skip checking of some parameters, but the return parameter type is not checked. .. doctest:: >>> mypp = ureg.check('[length]')(pendulum_period) In the decorator format: .. doctest:: >>> @ureg.check('[length]') ... def pendulum_period(length): ... return 2*math.pi*math.sqrt(length/G) pint-0.8.1/pint/000077500000000000000000000000001311537545300134535ustar00rootroot00000000000000pint-0.8.1/pint/__init__.py000066400000000000000000000052601311537545300155670ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint ~~~~ Pint is Python module/package to define, operate and manipulate **physical quantities**: the product of a numerical value and a unit of measurement. It allows arithmetic operations between them and conversions from and to different units. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import with_statement import pkg_resources from .formatting import formatter from .registry import (UnitRegistry, LazyRegistry) from .errors import (DimensionalityError, OffsetUnitCalculusError, UndefinedUnitError) from .util import pi_theorem, logger from .context import Context try: # pragma: no cover __version__ = pkg_resources.get_distribution('pint').version except: # pragma: no cover # we seem to have a local copy not installed without setuptools # so the reported version will be unknown __version__ = "unknown" #: A Registry with the default units and constants. _DEFAULT_REGISTRY = LazyRegistry() #: Registry used for unpickling operations. _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 _build_unit(units): """Build Unit 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.Unit(units) def set_application_registry(registry): """Set the application registry which is used for unpickling operations. :param registry: a UnitRegistry instance. """ assert isinstance(registry, UnitRegistry) global _APP_REGISTRY logger.debug('Changing app registry from %r to %r.', _APP_REGISTRY, registry) _APP_REGISTRY = registry def test(): """Run all tests. :return: a :class:`unittest.TestResult` object """ from .testsuite import run return run() pint-0.8.1/pint/babel_names.py000066400000000000000000000102421311537545300162540ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.babel ~~~~~~~~~~ :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from pint.compat import HAS_PROPER_BABEL _babel_units = dict( standard_gravity='acceleration-g-force', millibar='pressure-millibar', metric_ton='mass-metric-ton', megawatt='power-megawatt', degF='temperature-fahrenheit', dietary_calorie='energy-foodcalorie', millisecond='duration-millisecond', mph='speed-mile-per-hour', acre_foot='volume-acre-foot', mebibit='digital-megabit', gibibit='digital-gigabit', tebibit='digital-terabit', mebibyte='digital-megabyte', kibibyte='digital-kilobyte', mm_Hg='pressure-millimeter-of-mercury', month='duration-month', kilocalorie='energy-kilocalorie', cubic_mile='volume-cubic-mile', arcsecond='angle-arc-second', byte='digital-byte', metric_cup='volume-cup-metric', kilojoule='energy-kilojoule', meter_per_second_squared='acceleration-meter-per-second-squared', pint='volume-pint', square_centimeter='area-square-centimeter', in_Hg='pressure-inch-hg', milliampere='electric-milliampere', arcminute='angle-arc-minute', MPG='consumption-mile-per-gallon', hertz='frequency-hertz', day='duration-day', mps='speed-meter-per-second', kilometer='length-kilometer', square_yard='area-square-yard', kelvin='temperature-kelvin', kilogram='mass-kilogram', kilohertz='frequency-kilohertz', megahertz='frequency-megahertz', meter='length-meter', cubic_inch='volume-cubic-inch', kilowatt_hour='energy-kilowatt-hour', second='duration-second', yard='length-yard', light_year='length-light-year', millimeter='length-millimeter', metric_horsepower='power-horsepower', gibibyte='digital-gigabyte', ## 'temperature-generic', liter='volume-liter', turn='angle-revolution', microsecond='duration-microsecond', pound='mass-pound', ounce='mass-ounce', calorie='energy-calorie', centimeter='length-centimeter', inch='length-inch', centiliter='volume-centiliter', troy_ounce='mass-ounce-troy', gream='mass-gram', kilowatt='power-kilowatt', knot='speed-knot', lux='light-lux', hectoliter='volume-hectoliter', microgram='mass-microgram', degC='temperature-celsius', tablespoon='volume-tablespoon', cubic_yard='volume-cubic-yard', square_foot='area-square-foot', tebibyte='digital-terabyte', square_inch='area-square-inch', carat='mass-carat', hectopascal='pressure-hectopascal', gigawatt='power-gigawatt', watt='power-watt', micrometer='length-micrometer', volt='electric-volt', bit='digital-bit', gigahertz='frequency-gigahertz', teaspoon='volume-teaspoon', ohm='electric-ohm', joule='energy-joule', cup='volume-cup', square_mile='area-square-mile', nautical_mile='length-nautical-mile', square_meter='area-square-meter', mile='length-mile', acre='area-acre', nanometer='length-nanometer', hour='duration-hour', astronomical_unit='length-astronomical-unit', liter_per_100kilometers ='consumption-liter-per-100kilometers', megaliter='volume-megaliter', ton='mass-ton', hectare='area-hectare', square_kilometer='area-square-kilometer', kibibit='digital-kilobit', mile_scandinavian='length-mile-scandinavian', liter_per_kilometer='consumption-liter-per-kilometer', century='duration-century', cubic_foot='volume-cubic-foot', deciliter='volume-deciliter', ##pint='volume-pint-metric', cubic_meter='volume-cubic-meter', cubic_kilometer='volume-cubic-kilometer', quart='volume-quart', cc='volume-cubic-centimeter', pound_force_per_square_inch='pressure-pound-per-square-inch', milligram='mass-milligram', kph='speed-kilometer-per-hour', minute='duration-minute', parsec='length-parsec', picometer='length-picometer', degree='angle-degree', milliwatt='power-milliwatt', week='duration-week', ampere='electric-ampere', milliliter='volume-milliliter', decimeter='length-decimeter', fluid_ounce='volume-fluid-ounce', nanosecond='duration-nanosecond', foot='length-foot', karat='proportion-karat', year='duration-year', gallon='volume-gallon', radian='angle-radian', ) if not HAS_PROPER_BABEL: _babel_units = dict() _babel_systems = dict( mks='metric', imperial='uksystem', US='ussystem', ) _babel_lengths = ['narrow', 'short', 'long'] pint-0.8.1/pint/compat/000077500000000000000000000000001311537545300147365ustar00rootroot00000000000000pint-0.8.1/pint/compat/__init__.py000066400000000000000000000062301311537545300170500ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.compat ~~~~~~~~~~~ Compatibility layer. :copyright: 2013 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import division, unicode_literals, print_function, absolute_import import sys from io import BytesIO from numbers import Number from decimal import Decimal from . import tokenize ENCODING_TOKEN = tokenize.ENCODING PYTHON3 = sys.version >= '3' def tokenizer(input_string): for tokinfo in tokenize.tokenize(BytesIO(input_string.encode('utf-8')).readline): if tokinfo.type == ENCODING_TOKEN: continue yield tokinfo if PYTHON3: string_types = str def u(x): return x maketrans = str.maketrans long_type = int else: string_types = basestring import codecs def u(x): return codecs.unicode_escape_decode(x)[0] maketrans = lambda f, t: dict((ord(a), b) for a, b in zip(f, t)) long_type = long try: from collections import Chainmap except ImportError: from .chainmap import ChainMap try: from functools import lru_cache except ImportError: from .lrucache import lru_cache try: from logging import NullHandler except ImportError: from .nullhandler import NullHandler try: from itertools import zip_longest except ImportError: from itertools import izip_longest as zip_longest try: import numpy as np from numpy import ndarray HAS_NUMPY = True NUMPY_VER = np.__version__ NUMERIC_TYPES = (Number, Decimal, ndarray, np.number) def _to_magnitude(value, force_ndarray=False): if isinstance(value, (dict, bool)) or value is None: raise TypeError('Invalid magnitude for Quantity: {0!r}'.format(value)) elif isinstance(value, string_types) and value == '': raise ValueError('Quantity magnitude cannot be an empty string.') elif isinstance(value, (list, tuple)): return np.asarray(value) if force_ndarray: return np.asarray(value) return value except ImportError: np = None class ndarray(object): pass HAS_NUMPY = False NUMPY_VER = '0' NUMERIC_TYPES = (Number, Decimal) def _to_magnitude(value, force_ndarray=False): if isinstance(value, (dict, bool)) or value is None: raise TypeError('Invalid magnitude for Quantity: {0!r}'.format(value)) elif isinstance(value, string_types) and value == '': raise ValueError('Quantity magnitude cannot be an empty string.') elif isinstance(value, (list, tuple)): raise TypeError('lists and tuples are valid magnitudes for ' 'Quantity only when NumPy is present.') return value try: from uncertainties import ufloat HAS_UNCERTAINTIES = True except ImportError: ufloat = None HAS_UNCERTAINTIES = False try: from babel import Locale as Loc from babel import units as babel_units HAS_BABEL = True HAS_PROPER_BABEL = hasattr(babel_units, 'format_unit') except ImportError: HAS_PROPER_BABEL = HAS_BABEL = False if not HAS_PROPER_BABEL: Loc = babel_units = None pint-0.8.1/pint/compat/chainmap.py000066400000000000000000000114071311537545300170730ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.compat.chainmap ~~~~~~~~~~~~~~~~~~~~ Taken from the Python 3.3 source code. :copyright: 2013, PSF :license: PSF License """ from __future__ import division, unicode_literals, print_function, absolute_import import sys from collections import MutableMapping if sys.version_info < (3, 0): from thread import get_ident elif sys.version_info < (3, 3): from _thread import get_ident else: from threading import get_ident def _recursive_repr(fillvalue='...'): 'Decorator to make a repr function return fillvalue for a recursive call' def decorating_function(user_function): repr_running = set() def wrapper(self): key = id(self), get_ident() if key in repr_running: return fillvalue repr_running.add(key) try: result = user_function(self) finally: repr_running.discard(key) return result # Can't use functools.wraps() here because of bootstrap issues wrapper.__module__ = getattr(user_function, '__module__') wrapper.__doc__ = getattr(user_function, '__doc__') wrapper.__name__ = getattr(user_function, '__name__') wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) return wrapper return decorating_function class ChainMap(MutableMapping): ''' A ChainMap groups multiple dicts (or other mappings) together to create a single, updateable view. The underlying mappings are stored in a list. That list is public and can accessed or updated using the *maps* attribute. There is no other state. Lookups search the underlying mappings successively until a key is found. In contrast, writes, updates, and deletions only operate on the first mapping. ''' def __init__(self, *maps): '''Initialize a ChainMap by setting *maps* to the given mappings. If no mappings are provided, a single empty dictionary is used. ''' self.maps = list(maps) or [{}] # always at least one map def __missing__(self, key): raise KeyError(key) def __getitem__(self, key): for mapping in self.maps: try: return mapping[key] # can't use 'key in mapping' with defaultdict except KeyError: pass return self.__missing__(key) # support subclasses that define __missing__ def get(self, key, default=None): return self[key] if key in self else default def __len__(self): return len(set().union(*self.maps)) # reuses stored hash values if possible def __iter__(self): return iter(set().union(*self.maps)) def __contains__(self, key): return any(key in m for m in self.maps) def __bool__(self): return any(self.maps) @_recursive_repr() def __repr__(self): return '{0.__class__.__name__}({1})'.format( self, ', '.join(map(repr, self.maps))) @classmethod def fromkeys(cls, iterable, *args): 'Create a ChainMap with a single dict created from the iterable.' return cls(dict.fromkeys(iterable, *args)) def copy(self): 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' return self.__class__(self.maps[0].copy(), *self.maps[1:]) __copy__ = copy def new_child(self, m=None): # like Django's _Context.push() ''' New ChainMap with a new map followed by all previous maps. If no map is provided, an empty dict is used. ''' if m is None: m = {} return self.__class__(m, *self.maps) @property def parents(self): # like Django's _Context.pop() 'New ChainMap from maps[1:].' return self.__class__(*self.maps[1:]) def __setitem__(self, key, value): self.maps[0][key] = value def __delitem__(self, key): try: del self.maps[0][key] except KeyError: raise KeyError('Key not found in the first mapping: {!r}'.format(key)) def popitem(self): 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' try: return self.maps[0].popitem() except KeyError: raise KeyError('No keys found in the first mapping.') def pop(self, key, *args): 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' try: return self.maps[0].pop(key, *args) except KeyError: raise KeyError('Key not found in the first mapping: {!r}'.format(key)) def clear(self): 'Clear maps[0], leaving maps[1:] intact.' self.maps[0].clear() pint-0.8.1/pint/compat/lrucache.py000066400000000000000000000156071311537545300171070ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.compat.lrucache ~~~~~~~~~~~~~~~~~~~~ LRU (least recently used) cache backport. From https://code.activestate.com/recipes/578078-py26-and-py30-backport-of-python-33s-lru-cache/ :copyright: 2004, Raymond Hettinger, :license: MIT License """ from collections import namedtuple from functools import update_wrapper from threading import RLock _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) class _HashedSeq(list): __slots__ = 'hashvalue' def __init__(self, tup, hash=hash): self[:] = tup self.hashvalue = hash(tup) def __hash__(self): return self.hashvalue def _make_key(args, kwds, typed, kwd_mark = (object(),), fasttypes = set((int, str, frozenset, type(None))), sorted=sorted, tuple=tuple, type=type, len=len): 'Make a cache key from optionally typed positional and keyword arguments' key = args if kwds: sorted_items = sorted(kwds.items()) key += kwd_mark for item in sorted_items: key += item if typed: key += tuple(type(v) for v in args) if kwds: key += tuple(type(v) for k, v in sorted_items) elif len(key) == 1 and type(key[0]) in fasttypes: return key[0] return _HashedSeq(key) def lru_cache(maxsize=100, typed=False): """Least-recently-used cache decorator. If *maxsize* is set to None, the LRU features are disabled and the cache can grow without bound. If *typed* is True, arguments of different types will be cached separately. For example, f(3.0) and f(3) will be treated as distinct calls with distinct results. Arguments to the cached function must be hashable. View the cache statistics named tuple (hits, misses, maxsize, currsize) with f.cache_info(). Clear the cache and statistics with f.cache_clear(). Access the underlying function with f.__wrapped__. See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used """ # Users should only access the lru_cache through its public API: # cache_info, cache_clear, and f.__wrapped__ # The internals of the lru_cache are encapsulated for thread safety and # to allow the implementation to change (including a possible C version). def decorating_function(user_function): cache = dict() stats = [0, 0] # make statistics updateable non-locally HITS, MISSES = 0, 1 # names for the stats fields make_key = _make_key cache_get = cache.get # bound method to lookup key or return None _len = len # localize the global len() function lock = RLock() # because linkedlist updates aren't threadsafe root = [] # root of the circular doubly linked list root[:] = [root, root, None, None] # initialize by pointing to self nonlocal_root = [root] # make updateable non-locally PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields if maxsize == 0: def wrapper(*args, **kwds): # no caching, just do a statistics update after a successful call result = user_function(*args, **kwds) stats[MISSES] += 1 return result elif maxsize is None: def wrapper(*args, **kwds): # simple caching without ordering or size limit key = make_key(args, kwds, typed) result = cache_get(key, root) # root used here as a unique not-found sentinel if result is not root: stats[HITS] += 1 return result result = user_function(*args, **kwds) cache[key] = result stats[MISSES] += 1 return result else: def wrapper(*args, **kwds): # size limited caching that tracks accesses by recency key = make_key(args, kwds, typed) if kwds or typed else args with lock: link = cache_get(key) if link is not None: # record recent use of the key by moving it to the front of the list root, = nonlocal_root link_prev, link_next, key, result = link link_prev[NEXT] = link_next link_next[PREV] = link_prev last = root[PREV] last[NEXT] = root[PREV] = link link[PREV] = last link[NEXT] = root stats[HITS] += 1 return result result = user_function(*args, **kwds) with lock: root, = nonlocal_root if key in cache: # getting here means that this same key was added to the # cache while the lock was released. since the link # update is already done, we need only return the # computed result and update the count of misses. pass elif _len(cache) >= maxsize: # use the old root to store the new key and result oldroot = root oldroot[KEY] = key oldroot[RESULT] = result # empty the oldest link and make it the new root root = nonlocal_root[0] = oldroot[NEXT] oldkey = root[KEY] oldvalue = root[RESULT] root[KEY] = root[RESULT] = None # now update the cache dictionary for the new links del cache[oldkey] cache[key] = oldroot else: # put result in a new link at the front of the list last = root[PREV] link = [last, root, key, result] last[NEXT] = root[PREV] = cache[key] = link stats[MISSES] += 1 return result def cache_info(): """Report cache statistics""" with lock: return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache)) def cache_clear(): """Clear the cache and cache statistics""" with lock: cache.clear() root = nonlocal_root[0] root[:] = [root, root, None, None] stats[:] = [0, 0] wrapper.__wrapped__ = user_function wrapper.cache_info = cache_info wrapper.cache_clear = cache_clear return update_wrapper(wrapper, user_function) return decorating_function pint-0.8.1/pint/compat/meta.py000066400000000000000000000012541311537545300162400ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.compat.meta ~~~~~~~~~~~~~~~~ Compatibility layer. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ def with_metaclass(meta, *bases): """Create a base class with a metaclass.""" # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. # Taken from six class metaclass(meta): def __new__(cls, name, this_bases, d): return meta(name, bases, d) return type.__new__(metaclass, 'temporary_class', (), {})pint-0.8.1/pint/compat/nullhandler.py000066400000000000000000000015341311537545300176230ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.compat.nullhandler ~~~~~~~~~~~~~~~~~~~~~~~ Taken from the Python 2.7 source code. :copyright: 2013, PSF :license: PSF License """ import logging class NullHandler(logging.Handler): """ This handler does nothing. It's intended to be used to avoid the "No handlers could be found for logger XXX" one-off warning. This is important for library code, which may contain code to log events. If a user of the library does not configure logging, the one-off warning might be produced; to avoid this, the library developer simply needs to instantiate a NullHandler and add it to the top-level logger of the library module or package. """ def handle(self, record): pass def emit(self, record): pass def createLock(self): self.lock = None pint-0.8.1/pint/compat/tokenize.py000066400000000000000000000544031311537545300171460ustar00rootroot00000000000000"""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 determining source file encoding. It accepts a readline-like method which is called repeatedly to get the next line of input (or b"" for EOF). It generates 5-tuples with these members: the token type (see token.py) the token (a string) the starting (row, column) indices of the token (a 2-tuple of ints) the ending (row, column) indices of the token (a 2-tuple of ints) the original line (string) It is designed to match the working of the Python tokenizer exactly, except that it produces COMMENT tokens for comments and gives type OP for all operators. Additionally, all token lists start with an ENCODING token which tells you which encoding was used to decode the bytes stream. """ __author__ = 'Ka-Ping Yee ' __credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, ' 'Skip Montanaro, Raymond Hettinger, Trent Nelson, ' 'Michael Foord') from codecs import lookup, BOM_UTF8 import collections import io from io import TextIOWrapper from itertools import chain import re import sys from token import * try: reASCII = re.ASCII except: reASCII = 0 try: unicode _name_re = re.compile(r"\w*$", re.UNICODE) def isidentifier(s): if s[0] in '0123456789': return False return bool(_name_re.match(s)) except NameError: def isidentifier(s): return s.isidentifier() cookie_re = re.compile(r'^[ \t\f]*#.*coding[:=][ \t]*([-\w.]+)', reASCII) blank_re = re.compile(br'^[ \t\f]*(?:[#\r\n]|$)', reASCII) COMMENT = N_TOKENS tok_name[COMMENT] = 'COMMENT' NL = N_TOKENS + 1 tok_name[NL] = 'NL' ENCODING = N_TOKENS + 2 tok_name[ENCODING] = 'ENCODING' N_TOKENS += 3 EXACT_TOKEN_TYPES = { '(': LPAR, ')': RPAR, '[': LSQB, ']': RSQB, ':': COLON, ',': COMMA, ';': SEMI, '+': PLUS, '-': MINUS, '*': STAR, '/': SLASH, '|': VBAR, '&': AMPER, '<': LESS, '>': GREATER, '=': EQUAL, '.': DOT, '%': PERCENT, '{': LBRACE, '}': RBRACE, '==': EQEQUAL, '!=': NOTEQUAL, '<=': LESSEQUAL, '>=': GREATEREQUAL, '~': TILDE, '^': CIRCUMFLEX, '<<': LEFTSHIFT, '>>': RIGHTSHIFT, '**': DOUBLESTAR, '+=': PLUSEQUAL, '-=': MINEQUAL, '*=': STAREQUAL, '/=': SLASHEQUAL, '%=': PERCENTEQUAL, '&=': AMPEREQUAL, '|=': VBAREQUAL, '^=': CIRCUMFLEXEQUAL, '<<=': LEFTSHIFTEQUAL, '>>=': RIGHTSHIFTEQUAL, '**=': DOUBLESTAREQUAL, '//': DOUBLESLASH, '//=': DOUBLESLASHEQUAL, '@': AT } class TokenInfo(collections.namedtuple('TokenInfo', 'type string start end line')): def __repr__(self): annotated_type = '%d (%s)' % (self.type, tok_name[self.type]) return ('TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)' % self._replace(type=annotated_type)) @property def exact_type(self): if self.type == OP and self.string in EXACT_TOKEN_TYPES: return EXACT_TOKEN_TYPES[self.string] else: return self.type def group(*choices): return '(' + '|'.join(choices) + ')' def any(*choices): return group(*choices) + '*' def maybe(*choices): return group(*choices) + '?' # Note: we use unicode matching for names ("\w") but ascii matching for # number literals. Whitespace = r'[ \f\t]*' Comment = r'#[^\r\n]*' Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) Name = r'\w+' Hexnumber = r'0[xX][0-9a-fA-F]+' Binnumber = r'0[bB][01]+' Octnumber = r'0[oO][0-7]+' Decnumber = r'(?:0+|[1-9][0-9]*)' Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber) Exponent = r'[eE][-+]?[0-9]+' Pointfloat = group(r'[0-9]+\.[0-9]*', r'\.[0-9]+') + maybe(Exponent) Expfloat = r'[0-9]+' + Exponent Floatnumber = group(Pointfloat, Expfloat) Imagnumber = group(r'[0-9]+[jJ]', Floatnumber + r'[jJ]') Number = group(Imagnumber, Floatnumber, Intnumber) StringPrefix = r'(?:[bB][rR]?|[rR][bB]?|[uU])?' # Tail end of ' string. Single = r"[^'\\]*(?:\\.[^'\\]*)*'" # Tail end of " string. Double = r'[^"\\]*(?:\\.[^"\\]*)*"' # Tail end of ''' string. Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''" # Tail end of """ string. Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""' Triple = group(StringPrefix + "'''", StringPrefix + '"""') # Single-line ' or " string. String = group(StringPrefix + r"'[^\n'\\]*(?:\\.[^\n'\\]*)*'", StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*"') # Because of leftmost-then-longest match semantics, be sure to put the # longest operators first (e.g., if = came before ==, == would get # recognized as two instances of =). Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"!=", r"//=?", r"->", r"[+\-*/%&|^=<>]=?", r"~") Bracket = '[][(){}]' Special = group(r'\r?\n', r'\.\.\.', r'[:;.,@]') Funny = group(Operator, Bracket, Special) PlainToken = group(Number, Funny, String, Name) Token = Ignore + PlainToken # First (or only) line of ' or " string. ContStr = group(StringPrefix + r"'[^\n'\\]*(?:\\.[^\n'\\]*)*" + group("'", r'\\\r?\n'), StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*' + group('"', r'\\\r?\n')) PseudoExtras = group(r'\\\r?\n|\Z', Comment, Triple) PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name) def _compile(expr): return re.compile(expr, re.UNICODE) endpats = {"'": Single, '"': Double, "'''": Single3, '"""': Double3, "r'''": Single3, 'r"""': Double3, "b'''": Single3, 'b"""': Double3, "R'''": Single3, 'R"""': Double3, "B'''": Single3, 'B"""': Double3, "br'''": Single3, 'br"""': Double3, "bR'''": Single3, 'bR"""': Double3, "Br'''": Single3, 'Br"""': Double3, "BR'''": Single3, 'BR"""': Double3, "rb'''": Single3, 'rb"""': Double3, "Rb'''": Single3, 'Rb"""': Double3, "rB'''": Single3, 'rB"""': Double3, "RB'''": Single3, 'RB"""': Double3, "u'''": Single3, 'u"""': Double3, "R'''": Single3, 'R"""': Double3, "U'''": Single3, 'U"""': Double3, 'r': None, 'R': None, 'b': None, 'B': None, 'u': None, 'U': None} triple_quoted = {} for t in ("'''", '"""', "r'''", 'r"""', "R'''", 'R"""', "b'''", 'b"""', "B'''", 'B"""', "br'''", 'br"""', "Br'''", 'Br"""', "bR'''", 'bR"""', "BR'''", 'BR"""', "rb'''", 'rb"""', "rB'''", 'rB"""', "Rb'''", 'Rb"""', "RB'''", 'RB"""', "u'''", 'u"""', "U'''", 'U"""', ): triple_quoted[t] = t single_quoted = {} for t in ("'", '"', "r'", 'r"', "R'", 'R"', "b'", 'b"', "B'", 'B"', "br'", 'br"', "Br'", 'Br"', "bR'", 'bR"', "BR'", 'BR"' , "rb'", 'rb"', "rB'", 'rB"', "Rb'", 'Rb"', "RB'", 'RB"' , "u'", 'u"', "U'", 'U"', ): single_quoted[t] = t tabsize = 8 class TokenError(Exception): pass class StopTokenizing(Exception): pass class Untokenizer: def __init__(self): self.tokens = [] self.prev_row = 1 self.prev_col = 0 self.encoding = None def add_whitespace(self, start): row, col = start if row < self.prev_row or row == self.prev_row and col < self.prev_col: raise ValueError("start ({},{}) precedes previous end ({},{})" .format(row, col, self.prev_row, self.prev_col)) row_offset = row - self.prev_row if row_offset: self.tokens.append("\\\n" * row_offset) self.prev_col = 0 col_offset = col - self.prev_col if col_offset: self.tokens.append(" " * col_offset) def untokenize(self, iterable): it = iter(iterable) for t in it: if len(t) == 2: self.compat(t, it) break tok_type, token, start, end, line = t if tok_type == ENCODING: self.encoding = token continue if tok_type == ENDMARKER: break self.add_whitespace(start) self.tokens.append(token) self.prev_row, self.prev_col = end if tok_type in (NEWLINE, NL): self.prev_row += 1 self.prev_col = 0 return "".join(self.tokens) def compat(self, token, iterable): indents = [] toks_append = self.tokens.append startline = token[0] in (NEWLINE, NL) prevstring = False for tok in chain([token], iterable): toknum, tokval = tok[:2] if toknum == ENCODING: self.encoding = tokval continue if toknum in (NAME, NUMBER): tokval += ' ' # Insert a space between two consecutive strings if toknum == STRING: if prevstring: tokval = ' ' + tokval prevstring = True else: prevstring = False if toknum == INDENT: indents.append(tokval) continue elif toknum == DEDENT: indents.pop() continue elif toknum in (NEWLINE, NL): startline = True elif startline and indents: toks_append(indents[-1]) startline = False toks_append(tokval) def untokenize(iterable): """Transform tokens back into Python source code. It returns a bytes object, encoded using the ENCODING token, which is the first token sequence output by tokenize. Each element returned by the iterable must be a token sequence with at least two elements, a token number and token value. If only two tokens are passed, the resulting output is poor. Round-trip invariant for full input: Untokenized source will match input source exactly Round-trip invariant for limited intput: # Output bytes will tokenize the back to the input t1 = [tok[:2] for tok in tokenize(f.readline)] newcode = untokenize(t1) readline = BytesIO(newcode).readline t2 = [tok[:2] for tok in tokenize(readline)] assert t1 == t2 """ ut = Untokenizer() out = ut.untokenize(iterable) if ut.encoding is not None: out = out.encode(ut.encoding) return out def _get_normal_name(orig_enc): """Imitates get_normal_name in tokenizer.c.""" # Only care about the first 12 characters. enc = orig_enc[:12].lower().replace("_", "-") if enc == "utf-8" or enc.startswith("utf-8-"): return "utf-8" if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): return "iso-8859-1" return orig_enc def detect_encoding(readline): """ The detect_encoding() function is used to detect the encoding that should be used to decode a Python source file. It requires one argument, readline, in the same way as the tokenize() generator. It will call readline a maximum of twice, and return the encoding used (as a string) and a list of any lines (left as bytes) it has read in. It detects the encoding from the presence of a utf-8 bom or an encoding cookie as specified in pep-0263. If both a bom and a cookie are present, but disagree, a SyntaxError will be raised. If the encoding cookie is an invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, 'utf-8-sig' is returned. If no encoding is specified, then the default of 'utf-8' will be returned. """ try: filename = readline.__self__.name except AttributeError: filename = None bom_found = False encoding = None default = 'utf-8' def read_or_stop(): try: return readline() except StopIteration: return b'' def find_cookie(line): try: # Decode as UTF-8. Either the line is an encoding declaration, # in which case it should be pure ASCII, or it must be UTF-8 # per default encoding. line_string = line.decode('utf-8') except UnicodeDecodeError: msg = "invalid or missing encoding declaration" if filename is not None: msg = '{} for {!r}'.format(msg, filename) raise SyntaxError(msg) match = cookie_re.match(line_string) if not match: return None encoding = _get_normal_name(match.group(1)) try: codec = lookup(encoding) except LookupError: # This behaviour mimics the Python interpreter if filename is None: msg = "unknown encoding: " + encoding else: msg = "unknown encoding for {!r}: {}".format(filename, encoding) raise SyntaxError(msg) if bom_found: if encoding != 'utf-8': # This behaviour mimics the Python interpreter if filename is None: msg = 'encoding problem: utf-8' else: msg = 'encoding problem for {!r}: utf-8'.format(filename) raise SyntaxError(msg) encoding += '-sig' return encoding first = read_or_stop() if first.startswith(BOM_UTF8): bom_found = True first = first[3:] default = 'utf-8-sig' if not first: return default, [] encoding = find_cookie(first) if encoding: return encoding, [first] if not blank_re.match(first): return default, [first] second = read_or_stop() if not second: return default, [first] encoding = find_cookie(second) if encoding: return encoding, [first, second] return default, [first, second] def open(filename): """Open a file in read only mode using the encoding detected by detect_encoding(). """ buffer = io.open(filename, 'rb') encoding, lines = detect_encoding(buffer.readline) buffer.seek(0) text = TextIOWrapper(buffer, encoding, line_buffering=True) text.mode = 'r' return text def tokenize(readline): """ The tokenize() generator requires one argment, readline, which 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: readline = open(myfile, 'rb').__next__ # Example of alternate readline The generator produces 5-tuples with these members: the token type; the token string; a 2-tuple (srow, scol) of ints specifying the row and column where the token begins in the source; a 2-tuple (erow, ecol) of ints specifying the row and column where the token ends in the source; and the line on which the token was found. The line passed is the logical line; continuation lines are included. The first token sequence will always be an ENCODING token which tells you which encoding was used to decode the bytes stream. """ # This import is here to avoid problems when the itertools module is not # built yet and tokenize is imported. from itertools import chain, repeat encoding, consumed = detect_encoding(readline) rl_gen = iter(readline, b"") empty = repeat(b"") try: return _tokenize(chain(consumed, rl_gen, empty).__next__, encoding) except AttributeError: return _tokenize(chain(consumed, rl_gen, empty).next, encoding) def _tokenize(readline, encoding): lnum = parenlev = continued = 0 numchars = '0123456789' contstr, needcont = '', 0 contline = None indents = [0] if encoding is not None: if encoding == "utf-8-sig": # BOM will already have been stripped. encoding = "utf-8" yield TokenInfo(ENCODING, encoding, (0, 0), (0, 0), '') while True: # loop over lines in stream try: line = readline() except StopIteration: line = b'' if encoding is not None: line = line.decode(encoding) lnum += 1 pos, max = 0, len(line) if contstr: # continued string if not line: raise TokenError("EOF in multi-line string", strstart) endmatch = endprog.match(line) if endmatch: pos = end = endmatch.end(0) yield TokenInfo(STRING, contstr + line[:end], strstart, (lnum, end), contline + line) contstr, needcont = '', 0 contline = None elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n': yield TokenInfo(ERRORTOKEN, contstr + line, strstart, (lnum, len(line)), contline) contstr = '' contline = None continue else: contstr = contstr + line contline = contline + line continue elif parenlev == 0 and not continued: # new statement if not line: break column = 0 while pos < max: # measure leading whitespace if line[pos] == ' ': column += 1 elif line[pos] == '\t': column = (column//tabsize + 1)*tabsize elif line[pos] == '\f': column = 0 else: break pos += 1 if pos == max: break if line[pos] in '#\r\n': # skip comments or blank lines if line[pos] == '#': comment_token = line[pos:].rstrip('\r\n') nl_pos = pos + len(comment_token) yield TokenInfo(COMMENT, comment_token, (lnum, pos), (lnum, pos + len(comment_token)), line) yield TokenInfo(NL, line[nl_pos:], (lnum, nl_pos), (lnum, len(line)), line) else: yield TokenInfo((NL, COMMENT)[line[pos] == '#'], line[pos:], (lnum, pos), (lnum, len(line)), line) continue if column > indents[-1]: # count indents or dedents indents.append(column) yield TokenInfo(INDENT, line[:pos], (lnum, 0), (lnum, pos), line) while column < indents[-1]: if column not in indents: raise IndentationError( "unindent does not match any outer indentation level", ("", lnum, pos, line)) indents = indents[:-1] yield TokenInfo(DEDENT, '', (lnum, pos), (lnum, pos), line) else: # continued statement if not line: raise TokenError("EOF in multi-line statement", (lnum, 0)) continued = 0 while pos < max: pseudomatch = _compile(PseudoToken).match(line, pos) if pseudomatch: # scan for tokens start, end = pseudomatch.span(1) spos, epos, pos = (lnum, start), (lnum, end), end if start == end: continue token, initial = line[start:end], line[start] if (initial in numchars or # ordinary number (initial == '.' and token != '.' and token != '...')): yield TokenInfo(NUMBER, token, spos, epos, line) elif initial in '\r\n': yield TokenInfo(NL if parenlev > 0 else NEWLINE, token, spos, epos, line) elif initial == '#': assert not token.endswith("\n") yield TokenInfo(COMMENT, token, spos, epos, line) elif token in triple_quoted: endprog = _compile(endpats[token]) endmatch = endprog.match(line, pos) if endmatch: # all on one line pos = endmatch.end(0) token = line[start:pos] yield TokenInfo(STRING, token, spos, (lnum, pos), line) else: strstart = (lnum, start) # multiple lines contstr = line[start:] contline = line break elif initial in single_quoted or \ token[:2] in single_quoted or \ token[:3] in single_quoted: if token[-1] == '\n': # continued string strstart = (lnum, start) endprog = _compile(endpats[initial] or endpats[token[1]] or endpats[token[2]]) contstr, needcont = line[start:], 1 contline = line break else: # ordinary string yield TokenInfo(STRING, token, spos, epos, line) elif isidentifier(initial): # ordinary name yield TokenInfo(NAME, token, spos, epos, line) elif initial == '\\': # continued stmt continued = 1 else: if initial in '([{': parenlev += 1 elif initial in ')]}': parenlev -= 1 yield TokenInfo(OP, token, spos, epos, line) else: yield TokenInfo(ERRORTOKEN, line[pos], (lnum, pos), (lnum, pos+1), line) pos += 1 for indent in indents[1:]: # pop remaining indent levels yield TokenInfo(DEDENT, '', (lnum, 0), (lnum, 0), '') yield TokenInfo(ENDMARKER, '', (lnum, 0), (lnum, 0), '') pint-0.8.1/pint/constants_en.txt000066400000000000000000000027261311537545300167210ustar00rootroot00000000000000# 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. 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 # 0.000 000 29 e-34 planck_constant = 6.62606957e-34 J s = h hbar = planck_constant / (2 * pi) = ħ # 0.000 80 e-11 newtonian_constant_of_gravitation = 6.67384e-11 m^3 kg^-1 s^-2 # 0.000 000 035 e-19 # elementary_charge = 1.602176565e-19 C = e # 0.000 0075 molar_gas_constant = 8.3144621 J mol^-1 K^-1 = R # 0.000 000 0024 e-3 fine_structure_constant = 7.2973525698e-3 # 0.000 000 27 e23 avogadro_number = 6.02214129e23 mol^-1 =N_A # 0.000 0013 e-23 boltzmann_constant = 1.3806488e-23 J K^-1 = k # 0.000 021 e-8 stefan_boltzmann_constant = 5.670373e-8 W m^-2 K^-4 = σ # 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 pint-0.8.1/pint/context.py000066400000000000000000000205451311537545300155170ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.context ~~~~~~~~~~~~ Functions and classes related to context definitions and application. :copyright: 2016 by Pint Authors, see AUTHORS for more details.. :license: BSD, see LICENSE for more details. """ from __future__ import division, unicode_literals, print_function, absolute_import import re from collections import defaultdict import weakref from .compat import ChainMap from .util import (ParserHelper, UnitsContainer, string_types, to_units_container, SourceIterator) from .errors import DefinitionSyntaxError #: Regex to match the header parts of a context. _header_re = re.compile('@context\s*(?P\(.*\))?\s+(?P\w+)\s*(=(?P.*))*') #: Regex to match variable names in an equation. _varname_re = re.compile('[A-Za-z_][A-Za-z0-9_]*') def _expression_to_function(eq): def func(ureg, value, **kwargs): return ureg.parse_expression(eq, value=value, **kwargs) return func class Context(object): """A specialized container that defines transformation functions from one dimension to another. Each Dimension are specified using a UnitsContainer. Simple transformation are given with a function taking a single parameter. >>> timedim = UnitsContainer({'[time]': 1}) >>> spacedim = UnitsContainer({'[length]': 1}) >>> def f(time): ... 'Time to length converter' ... return 3. * time >>> c = Context() >>> c.add_transformation(timedim, spacedim, f) >>> c.transform(timedim, spacedim, 2) 6 Conversion functions may take optional keyword arguments and the context can have default values for these arguments. >>> def f(time, n): ... 'Time to length converter, n is the index of refraction of the material' ... return 3. * time / n >>> c = Context(n=3) >>> c.add_transformation(timedim, spacedim, f) >>> c.transform(timedim, spacedim, 2) 2 """ def __init__(self, name, aliases=(), defaults=None): self.name = name self.aliases = aliases #: Maps (src, dst) -> transformation function self.funcs = {} #: Maps defaults variable names to values self.defaults = defaults or {} #: Maps (src, dst) -> self #: Used as a convenience dictionary to be composed by ContextChain self.relation_to_context = weakref.WeakValueDictionary() @classmethod def from_context(cls, context, **defaults): """Creates a new context that shares the funcs dictionary with the original context. The default values are copied from the original context and updated with the new defaults. If defaults is empty, return the same context. """ if defaults: newdef = dict(context.defaults, **defaults) c = cls(context.name, context.aliases, newdef) c.funcs = context.funcs for edge in context.funcs.keys(): c.relation_to_context[edge] = c return c return context @classmethod def from_lines(cls, lines, to_base_func=None): lines = SourceIterator(lines) lineno, header = next(lines) try: r = _header_re.search(header) name = r.groupdict()['name'].strip() aliases = r.groupdict()['aliases'] if aliases: aliases = tuple(a.strip() for a in r.groupdict()['aliases'].split('=')) else: aliases = () defaults = r.groupdict()['defaults'] except: raise DefinitionSyntaxError("Could not parse the Context header '%s'" % header, lineno=lineno) if defaults: def to_num(val): val = complex(val) if not val.imag: return val.real return val _txt = defaults try: defaults = (part.split('=') for part in defaults.strip('()').split(',')) defaults = dict((str(k).strip(), to_num(v)) for k, v in defaults) except (ValueError, TypeError): raise DefinitionSyntaxError("Could not parse Context definition defaults: '%s'", _txt, lineno=lineno) ctx = cls(name, aliases, defaults) else: ctx = cls(name, aliases) names = set() for lineno, line in lines: try: rel, eq = line.split(':') names.update(_varname_re.findall(eq)) func = _expression_to_function(eq) if '<->' in rel: src, dst = (ParserHelper.from_string(s) for s in rel.split('<->')) if to_base_func: src = to_base_func(src) dst = to_base_func(dst) ctx.add_transformation(src, dst, func) ctx.add_transformation(dst, src, func) elif '->' in rel: src, dst = (ParserHelper.from_string(s) for s in rel.split('->')) if to_base_func: src = to_base_func(src) dst = to_base_func(dst) ctx.add_transformation(src, dst, func) else: raise Exception except: raise DefinitionSyntaxError("Could not parse Context %s relation '%s'" % (name, line), lineno=lineno) if defaults: missing_pars = set(defaults.keys()).difference(set(names)) if missing_pars: raise DefinitionSyntaxError('Context parameters {0} not found in any equation.'.format(missing_pars)) return ctx def add_transformation(self, src, dst, func): """Add a transformation function to the context. """ _key = self.__keytransform__(src, dst) self.funcs[_key] = func self.relation_to_context[_key] = self def remove_transformation(self, src, dst): """Add a transformation function to the context. """ _key = self.__keytransform__(src, dst) del self.funcs[_key] del self.relation_to_context[_key] @staticmethod def __keytransform__(src, dst): return to_units_container(src), to_units_container(dst) def transform(self, src, dst, registry, value): """Transform a value. """ _key = self.__keytransform__(src, dst) return self.funcs[_key](registry, value, **self.defaults) class ContextChain(ChainMap): """A specialized ChainMap for contexts that simplifies finding rules to transform from one dimension to another. """ def __init__(self, *args, **kwargs): super(ContextChain, self).__init__(*args, **kwargs) self._graph = None self._contexts = [] def insert_contexts(self, *contexts): """Insert one or more contexts in reversed order the chained map. (A rule in last context will take precedence) To facilitate the identification of the context with the matching rule, the *relation_to_context* dictionary of the context is used. """ self._contexts.insert(0, contexts) self.maps = [ctx.relation_to_context for ctx in reversed(contexts)] + self.maps self._graph = None def remove_contexts(self, n): """Remove the last n inserted contexts from the chain. """ self._contexts = self._contexts[n:] self.maps = self.maps[n:] self._graph = None @property def defaults(self): if self: return list(self.maps[0].values())[0].defaults return {} @property def graph(self): """The graph relating """ if self._graph is None: self._graph = defaultdict(set) for fr_, to_ in self: self._graph[fr_].add(to_) return self._graph def transform(self, src, dst, registry, value): """Transform the value, finding the rule in the chained context. (A rule in last context will take precedence) :raises: KeyError if the rule is not found. """ return self[(src, dst)].transform(src, dst, registry, value) pint-0.8.1/pint/converters.py000066400000000000000000000034431311537545300162230ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.converters ~~~~~~~~~ Functions and classes related to unit conversions. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import (division, unicode_literals, print_function, absolute_import) class Converter(object): """Base class for value converters. """ is_multiplicative = True def to_reference(self, value, inplace=False): return value def from_reference(self, value, inplace=False): return value class ScaleConverter(Converter): """A linear transformation """ is_multiplicative = True def __init__(self, scale): self.scale = scale def to_reference(self, value, inplace=False): if inplace: value *= self.scale else: value = value * self.scale return value def from_reference(self, value, inplace=False): if inplace: value /= self.scale else: value = value / self.scale return value class OffsetConverter(Converter): """An affine transformation """ def __init__(self, scale, offset): self.scale = scale self.offset = offset @property def is_multiplicative(self): return self.offset == 0 def to_reference(self, value, inplace=False): if inplace: value *= self.scale value += self.offset else: value = value * self.scale + self.offset return value def from_reference(self, value, inplace=False): if inplace: value -= self.offset value /= self.scale else: value = (value - self.offset) / self.scale return value pint-0.8.1/pint/default_en.txt000066400000000000000000000337331311537545300163330ustar00rootroot00000000000000# Default Pint units definition file # Based on the International System of Units # Language: english # :copyright: 2013 by Pint Authors, see AUTHORS for more details. @defaults group = international system = mks @end # decimal prefixes yocto- = 1e-24 = y- zepto- = 1e-21 = z- atto- = 1e-18 = a- femto- = 1e-15 = f- pico- = 1e-12 = p- nano- = 1e-9 = n- micro- = 1e-6 = u- = µ- milli- = 1e-3 = m- centi- = 1e-2 = c- deci- = 1e-1 = d- deca- = 1e+1 = da- = deka hecto- = 1e2 = h- kilo- = 1e3 = k- mega- = 1e6 = M- giga- = 1e9 = G- tera- = 1e12 = T- peta- = 1e15 = P- exa- = 1e18 = E- zetta- = 1e21 = Z- yotta- = 1e24 = Y- # binary_prefixes kibi- = 2**10 = Ki- mebi- = 2**20 = Mi- gibi- = 2**30 = Gi- tebi- = 2**40 = Ti- pebi- = 2**50 = Pi- exbi- = 2**60 = Ei- zebi- = 2**70 = Zi- yobi- = 2**80 = Yi- # reference 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 radian = [] = rad bit = [] count = [] @import constants_en.txt # acceleration [acceleration] = [length] / [time] ** 2 # 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 steradian = radian ** 2 = sr # 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 hectare = 100 * are = ha # 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 # 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 = E_h = hartree_energy toe = 41.868e9 * joule = tonne_of_oil_equivalent # Force [force] = [mass] * [acceleration] 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_ton = 2000 * force_pound = ton_force poundal = lb * feet / second ** 2 = pdl kip = 1000*lbf # Frequency [frequency] = 1 / [time] hertz = 1 / second = Hz = rps revolutions_per_minute = revolution / minute = rpm counts_per_second = count / second = cps # Heat #RSI = degK * meter ** 2 / watt #clo = 0.155 * RSI = clos #R_value = foot ** 2 * degF * hour / btu # Information byte = 8 * bit = B = octet baud = bit / second = Bd = bps # Irradiance peak_sun_hour = 1000 * watt_hour / meter**2 = PSH langley = thermochemical_calorie / centimeter**2 = Langley # 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 # Mass carat = 200 * milligram metric_ton = 1000 * kilogram = t = tonne atomic_mass_unit = 1.660538782e-27 * kilogram = u = amu = dalton = Da bag = 94 * lb # Textile denier = gram / (9000 * meter) tex = gram / (1000 * meter) dtex = decitex # Photometry lumen = candela * steradian = lm 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 # 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 # 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 # Temperature degC = kelvin; offset: 273.15 = celsius degR = 5 / 9 * kelvin; offset: 0 = rankine degF = 5 / 9 * kelvin; offset: 255.372222 = fahrenheit # 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 # 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 # Viscosity [viscosity] = [pressure] * [time] poise = 1e-1 * Pa * second = P stokes = 1e-4 * meter ** 2 / second = St rhe = 10 / (Pa * s) # Volume [volume] = [length] ** 3 liter = 1e-3 * m ** 3 = l = L = litre cc = centimeter ** 3 = cubic_centimeter stere = meter ** 3 @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 @context boltzmann [temperature] -> [energy]: boltzmann_constant * value [energy] -> [temperature]: value / boltzmann_constant @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 # Most of the definitions that follows are derived from: # See http://www.nist.gov/pml/wmd/pubs/hb44.cfm @group USCSLengthInternational inch = yard / 36 = in = international_inch = inches = international_inches 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 @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 @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 @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 @end @group USCSVolumeOther teaspoon = tablespoon / 3 = tsp tablespoon = floz / 2 = tbsp = Tbsp = Tblsp = tblsp = tbs = Tbl shot = 3 * tablespoon = jig = US_shot cup = 8 fluid_ounce = cp = liquid_cup = US_liquid_cup barrel = 31.5 * gallon = bbl oil_barrel = 42 * gallon = oil_bbl beer_barrel = 31 * gallon = beer_bbl hogshead = 63 * gallon @end @group Avoirdupois grain = avdp_pound / 7000 = gr drachm = pound / 256 = dr = avoirdupois_dram = avdp_dram = dram 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 @end @group Troy pennyweight = 24 grain = dwt troy_ounce = 480 grain = toz troy_pound = 12 troy_ounce = tlb @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 @end @group AvoirdupoisUK using Avoirdupois stone = 14 pound quarter = 28 stone UK_hundredweight = long_hunderweight = UK_cwt UK_ton = long_ton @end @group AvoirdupoisUS using Avoirdupois US_hundredweight = short_hunderdweight = US_cwt US_ton = short_ton = ton @end @group Printer # Length pixel = [printing_unit] = dot = px = 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 point = yard / 216 / 12 = pp = printers_point thou = yard / 36000 = th = mil pica = yard / 216 = P̸ = printers_pica @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 @end @system mks using international meter kilogram second @end @system cgs using international centimeter gram second @end @system imperial using ImperialVolume, USCSLengthInternational, AvoirdupoisUK yard pound @end @system US using USCSLiquidVolume, USCSDryVolume, USCSVolumeOther, USCSLengthInternational, USCSLengthSurvey, AvoirdupoisUS yard pound @end pint-0.8.1/pint/default_en_0.6.txt000066400000000000000000000267671311537545300167270ustar00rootroot00000000000000# Default Pint units definition file # Based on the International System of Units # Language: english # :copyright: 2013 by Pint Authors, see AUTHORS for more details. # decimal prefixes yocto- = 1e-24 = y- zepto- = 1e-21 = z- atto- = 1e-18 = a- femto- = 1e-15 = f- pico- = 1e-12 = p- nano- = 1e-9 = n- micro- = 1e-6 = u- = µ- milli- = 1e-3 = m- centi- = 1e-2 = c- deci- = 1e-1 = d- deca- = 1e+1 = da- hecto- = 1e2 = h- kilo- = 1e3 = k- mega- = 1e6 = M- giga- = 1e9 = G- tera- = 1e12 = T- peta- = 1e15 = P- exa- = 1e18 = E- zetta- = 1e21 = Z- yotta- = 1e24 = Y- # binary_prefixes kibi- = 2**10 = Ki- mebi- = 2**20 = Mi- gibi- = 2**30 = Gi- tebi- = 2**40 = Ti- pebi- = 2**50 = Pi- exbi- = 2**60 = Ei- zebi- = 2**70 = Zi- yobi- = 2**80 = Yi- # reference 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 radian = [] = rad bit = [] count = [] @import constants_en.txt # acceleration [acceleration] = [length] / [time] ** 2 # 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 steradian = radian ** 2 = sr # 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 acre = 4046.8564224 * m ** 2 = international_acre hectare = 100 * are = ha US_survey_acre = 160 * rod ** 2 # 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 # 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 cal = 4.184 * joule = calorie = 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 = E_h = hartree_energy # Force [force] = [mass] * [acceleration] 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_ton = 2000 * force_pound = ton_force poundal = lb * feet / second ** 2 = pdl kip = 1000*lbf # Frequency [frequency] = 1 / [time] hertz = 1 / second = Hz = rps revolutions_per_minute = revolution / minute = rpm counts_per_second = count / second = cps # Heat #RSI = degK * meter ** 2 / watt #clo = 0.155 * RSI = clos #R_value = foot ** 2 * degF * hour / btu # Information byte = 8 * bit = B = octet baud = bit / second = Bd = bps # Irradiance peak_sun_hour = 1000 * watt_hour / meter**2 = PSH langley = thermochemical_calorie / centimeter**2 = Langley # Length angstrom = 1e-10 * meter = Å = ångström = Å inch = 2.54 * centimeter = in = international_inch = inches = international_inches foot = 12 * inch = ft = international_foot = feet = international_feet mile = 5280 * foot = mi = international_mile yard = 3 * feet = yd = international_yard mil = inch / 1000 = thou parsec = 3.08568025e16 * meter = pc light_year = speed_of_light * julian_year = ly = lightyear astronomical_unit = 149597870691 * meter = au nautical_mile = 1.852e3 * meter = nmi printers_point = 127 * millimeter / 360 = point printers_pica = 12 * printers_point = pica US_survey_foot = 1200 * meter / 3937 US_survey_yard = 3 * US_survey_foot US_survey_mile = 5280 * US_survey_foot = US_statute_mile rod = 16.5 * US_survey_foot = pole = perch furlong = 660 * US_survey_foot fathom = 6 * US_survey_foot chain = 66 * US_survey_foot barleycorn = inch / 3 arpentlin = 191.835 * feet kayser = 1 / centimeter = wavenumber # Mass dram = oz / 16 = dr = avoirdupois_dram ounce = 28.349523125 * gram = oz = avoirdupois_ounce pound = 0.45359237 * kilogram = lb = avoirdupois_pound stone = 14 * lb = st carat = 200 * milligram grain = 64.79891 * milligram = gr long_hundredweight = 112 * lb short_hundredweight = 100 * lb metric_ton = 1000 * kilogram = t = tonne pennyweight = 24 * gram = dwt slug = 14.59390 * kilogram troy_ounce = 480 * grain = toz = apounce = apothecary_ounce troy_pound = 12 * toz = tlb = appound = apothecary_pound drachm = 60 * grain = apdram = apothecary_dram atomic_mass_unit = 1.660538782e-27 * kilogram = u = amu = dalton = Da scruple = 20 * grain bag = 94 * lb ton = 2000 * lb = short_ton # Textile denier = gram / (9000 * meter) tex = gram / (1000 * meter) dtex = decitex # Photometry lumen = candela * steradian = lm 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 # 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 # Radiation Bq = Hz = becquerel curie = 3.7e10 * Bq = Ci rutherford = 1e6*Bq = rd = Rd Gy = joule / kilogram = gray = Sv = sievert rem = 1e-2 * sievert rads = 1e-2 * gray roentgen = 2.58e-4 * coulomb / kilogram # Temperature degC = kelvin; offset: 273.15 = celsius degR = 5 / 9 * kelvin; offset: 0 = rankine degF = 5 / 9 * kelvin; offset: 255.372222 = fahrenheit # 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 # Velocity [speed] = [length] / [time] knot = nautical_mile / hour = kt = knot_international = international_knot = nautical_miles_per_hour mph = mile / hour = MPH kph = kilometer / hour = KPH # Viscosity [viscosity] = [pressure] * [time] poise = 1e-1 * Pa * second = P stokes = 1e-4 * meter ** 2 / second = St rhe = 10 / (Pa * s) # Volume [volume] = [length] ** 3 liter = 1e-3 * m ** 3 = l = L = litre cc = centimeter ** 3 = cubic_centimeter stere = meter ** 3 gross_register_ton = 100 * foot ** 3 = register_ton = GRT acre_foot = acre * foot = acre_feet board_foot = foot ** 2 * inch = FBM bushel = 2150.42 * inch ** 3 = bu = US_bushel dry_gallon = bushel / 8 = US_dry_gallon dry_quart = dry_gallon / 4 = US_dry_quart dry_pint = dry_quart / 2 = US_dry_pint gallon = 231 * inch ** 3 = liquid_gallon = US_liquid_gallon quart = gallon / 4 = liquid_quart = US_liquid_quart pint = quart / 2 = pt = liquid_pint = US_liquid_pint cup = pint / 2 = liquid_cup = US_liquid_cup gill = cup / 2 = liquid_gill = US_liquid_gill fluid_ounce = gill / 4 = floz = US_fluid_ounce = US_liquid_ounce imperial_bushel = 36.36872 * liter = UK_bushel imperial_gallon = imperial_bushel / 8 = UK_gallon imperial_quart = imperial_gallon / 4 = UK_quart imperial_pint = imperial_quart / 2 = UK_pint imperial_cup = imperial_pint / 2 = UK_cup imperial_gill = imperial_cup / 2 = UK_gill imperial_floz = imperial_gill / 5 = UK_fluid_ounce = imperial_fluid_ounce barrel = 42 * gallon = bbl tablespoon = floz / 2 = tbsp = Tbsp = Tblsp = tblsp = tbs = Tbl teaspoon = tablespoon / 3 = tsp peck = bushel / 4 = pk fluid_dram = floz / 8 = fldr = fluidram firkin = barrel / 4 @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 @end @context boltzmann [temperature] -> [energy]: boltzmann_constant * value [energy] -> [temperature]: value / boltzmann_constant @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 pint-0.8.1/pint/definitions.py000066400000000000000000000123111311537545300163360ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.definitions ~~~~~~~~~ Functions and classes related to unit definitions. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import (division, unicode_literals, print_function, absolute_import) from .converters import ScaleConverter, OffsetConverter from .util import UnitsContainer, _is_dim, ParserHelper from .compat import string_types class Definition(object): """Base class for definitions. :param name: name. :param symbol: a short name or symbol for the definition :param aliases: iterable of other names. :param converter: an instance of Converter. """ def __init__(self, name, symbol, aliases, converter): self._name = name self._symbol = symbol self._aliases = aliases self._converter = converter @property def is_multiplicative(self): return self._converter.is_multiplicative @classmethod def from_string(cls, definition): """Parse a definition """ name, definition = definition.split('=', 1) name = name.strip() result = [res.strip() for res in definition.split('=')] value, aliases = result[0], tuple(result[1:]) symbol, aliases = (aliases[0], aliases[1:]) if aliases else (None, aliases) if name.startswith('['): return DimensionDefinition(name, symbol, aliases, value) elif name.endswith('-'): name = name.rstrip('-') return PrefixDefinition(name, symbol, aliases, value) else: return UnitDefinition(name, symbol, aliases, value) @property def name(self): return self._name @property def symbol(self): return self._symbol or self._name @property def has_symbol(self): return bool(self._symbol) @property def aliases(self): return self._aliases @property def converter(self): return self._converter def __str__(self): return self.name class PrefixDefinition(Definition): """Definition of a prefix. """ def __init__(self, name, symbol, aliases, converter): if isinstance(converter, string_types): converter = ScaleConverter(eval(converter)) aliases = tuple(alias.strip('-') for alias in aliases) if symbol: symbol = symbol.strip('-') super(PrefixDefinition, self).__init__(name, symbol, aliases, converter) class UnitDefinition(Definition): """Definition of a unit. :param reference: Units container with reference units. :param is_base: indicates if it is a base unit. """ def __init__(self, name, symbol, aliases, converter, reference=None, is_base=False): self.reference = reference self.is_base = is_base if isinstance(converter, string_types): if ';' in converter: [converter, modifiers] = converter.split(';', 2) modifiers = dict((key.strip(), eval(value)) for key, value in (part.split(':') for part in modifiers.split(';'))) else: 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()): self.is_base = False else: raise ValueError('Cannot mix dimensions and units in the same definition. ' 'Base units must be referenced only to dimensions. ' 'Derived units must be referenced only to units.') self.reference = UnitsContainer(converter) if modifiers.get('offset', 0.) != 0.: converter = OffsetConverter(converter.scale, modifiers['offset']) else: converter = ScaleConverter(converter.scale) super(UnitDefinition, self).__init__(name, symbol, aliases, converter) class DimensionDefinition(Definition): """Definition of a dimension. """ def __init__(self, name, symbol, aliases, converter, reference=None, is_base=False): self.reference = reference self.is_base = is_base if isinstance(converter, string_types): converter = ParserHelper.from_string(converter) if not converter: self.is_base = True elif all(_is_dim(key) for key in converter.keys()): self.is_base = False else: raise ValueError('Base dimensions must be referenced to None. ' 'Derived dimensions must only be referenced ' 'to dimensions.') self.reference = UnitsContainer(converter) super(DimensionDefinition, self).__init__(name, symbol, aliases, converter=None) pint-0.8.1/pint/errors.py000066400000000000000000000071021311537545300153410ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.errors ~~~~~~~~~ Functions and classes related to unit definitions and conversions. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import (division, unicode_literals, print_function, absolute_import) from .compat import string_types class DefinitionSyntaxError(ValueError): """Raised when a textual definition has a syntax error. """ def __init__(self, msg, filename=None, lineno=None): super(DefinitionSyntaxError, self).__init__() self.msg = msg self.filename = None self.lineno = None def __str__(self): mess = "While opening {0}, in line {1}: " return mess.format(self.filename, self.lineno) + self.msg class RedefinitionError(ValueError): """Raised when a unit or prefix is redefined. """ def __init__(self, name, definition_type): super(RedefinitionError, self).__init__() self.name = name self.definition_type = definition_type self.filename = None self.lineno = None def __str__(self): msg = "cannot redefine '{0}' ({1})".format(self.name, self.definition_type) if self.filename: mess = "While opening {0}, in line {1}: " return mess.format(self.filename, self.lineno) + msg return msg class UndefinedUnitError(AttributeError): """Raised when the units are not defined in the unit registry. """ def __init__(self, unit_names): super(UndefinedUnitError, self).__init__() self.unit_names = unit_names def __str__(self): mess = "'{0}' is not defined in the unit registry" mess_plural = "'{0}' are not defined in the unit registry" if isinstance(self.unit_names, string_types): return mess.format(self.unit_names) elif isinstance(self.unit_names, (list, tuple))\ and len(self.unit_names) == 1: return mess.format(self.unit_names[0]) elif isinstance(self.unit_names, set) and len(self.unit_names) == 1: uname = list(self.unit_names)[0] return mess.format(uname) else: return mess_plural.format(self.unit_names) class DimensionalityError(ValueError): """Raised when trying to convert between incompatible units. """ def __init__(self, units1, units2, dim1=None, dim2=None, extra_msg=''): super(DimensionalityError, self).__init__() self.units1 = units1 self.units2 = units2 self.dim1 = dim1 self.dim2 = dim2 self.extra_msg = extra_msg def __str__(self): if self.dim1 or self.dim2: dim1 = ' ({0})'.format(self.dim1) dim2 = ' ({0})'.format(self.dim2) else: dim1 = '' dim2 = '' msg = "Cannot convert from '{0}'{1} to '{2}'{3}" + self.extra_msg return msg.format(self.units1, dim1, self.units2, dim2) class OffsetUnitCalculusError(ValueError): """Raised on ambiguous operations with offset units. """ def __init__(self, units1, units2='', extra_msg=''): super(ValueError, self).__init__() self.units1 = units1 self.units2 = units2 self.extra_msg = extra_msg def __str__(self): msg = ("Ambiguous operation with offset unit (%s)." % ', '.join(['%s' % u for u in [self.units1, self.units2] if u]) + self.extra_msg) return msg.format(self.units1, self.units2) pint-0.8.1/pint/formatting.py000066400000000000000000000233361311537545300162060ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.formatter ~~~~~~~~~~~~~~ Format units for pint. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import division, unicode_literals, print_function, absolute_import import re from .babel_names import _babel_units, _babel_lengths from pint.compat import babel_units, Loc, string_types __JOIN_REG_EXP = re.compile("\{\d*\}") def _join(fmt, iterable): """Join an iterable with the format specified in fmt. The format can be specified in two ways: - PEP3101 format with two replacement fields (eg. '{0} * {1}') - The concatenating string (eg. ' * ') """ if not iterable: return '' if not __JOIN_REG_EXP.search(fmt): return fmt.join(iterable) miter = iter(iterable) first = next(miter) for val in miter: ret = fmt.format(first, val) first = ret return first _PRETTY_EXPONENTS = '⁰¹²³⁴⁵⁶⁷⁸⁹' def _pretty_fmt_exponent(num): """Format an number into a pretty printed exponent. """ # TODO: Will not work for decimals ret = '{0:n}'.format(num).replace('-', '⁻') for n in range(10): ret = ret.replace(str(n), _PRETTY_EXPONENTS[n]) return ret #: _FORMATS maps format specifications to the corresponding argument set to #: formatter(). _FORMATS = { 'P': { # Pretty format. 'as_ratio': True, 'single_denominator': False, 'product_fmt': '·', 'division_fmt': '/', 'power_fmt': '{0}{1}', 'parentheses_fmt': '({0})', 'exp_call': _pretty_fmt_exponent, }, 'L': { # Latex format. 'as_ratio': True, 'single_denominator': True, 'product_fmt': r' \cdot ', 'division_fmt': r'\frac[{0}][{1}]', 'power_fmt': '{0}^[{1}]', 'parentheses_fmt': r'\left({0}\right)', }, 'H': { # HTML format. 'as_ratio': True, 'single_denominator': True, 'product_fmt': r' ', 'division_fmt': r'{0}/{1}', 'power_fmt': '{0}{1}', 'parentheses_fmt': r'({0})', }, '': { # Default format. 'as_ratio': True, 'single_denominator': False, 'product_fmt': ' * ', 'division_fmt': ' / ', 'power_fmt': '{0} ** {1}', 'parentheses_fmt': r'({0})', }, 'C': { # Compact format. 'as_ratio': True, 'single_denominator': False, 'product_fmt': '*', # TODO: Should this just be ''? 'division_fmt': '/', 'power_fmt': '{0}**{1}', 'parentheses_fmt': r'({0})', }, } def formatter(items, as_ratio=True, single_denominator=False, product_fmt=' * ', division_fmt=' / ', power_fmt='{0} ** {1}', parentheses_fmt='({0})', exp_call=lambda x: '{0:n}'.format(x), locale=None, babel_length='long', babel_plural_form='one'): """Format a list of (name, exponent) pairs. :param items: a list of (name, exponent) pairs. :param as_ratio: True to display as ratio, False as negative powers. :param single_denominator: all with terms with negative exponents are collected together. :param product_fmt: the format used for multiplication. :param division_fmt: the format used for division. :param power_fmt: the format used for exponentiation. :param parentheses_fmt: the format used for parenthesis. :param locale: the locale object as defined in babel. :param babel_length: the length of the translated unit, as defined in babel cldr. :param babel_plural_form: the plural form, calculated as defined in babel. :return: the formula as a string. """ if not items: return '' if as_ratio: fun = lambda x: exp_call(abs(x)) else: fun = exp_call pos_terms, neg_terms = [], [] for key, value in sorted(items): if locale and babel_length and babel_plural_form and key in _babel_units: _key = _babel_units[key] locale = Loc.parse(locale) unit_patterns = locale._data['unit_patterns'] compound_unit_patterns = locale._data["compound_unit_patterns"] plural = 'one' if abs(value) <= 0 else babel_plural_form if babel_length not in _babel_lengths: other_lengths = [ _babel_length for _babel_length in reversed(_babel_lengths) \ if babel_length != _babel_length ] else: other_lengths = [] for _babel_length in [babel_length] + other_lengths: pat = unit_patterns.get(_key, {}).get(_babel_length, {}).get(plural) if pat is not None: key = pat.replace('{0}', '').strip() break division_fmt = compound_unit_patterns.get("per", {}).get(babel_length, division_fmt) power_fmt = '{0}{1}' exp_call = _pretty_fmt_exponent if value == 1: pos_terms.append(key) elif value > 0: pos_terms.append(power_fmt.format(key, fun(value))) elif value == -1 and as_ratio: neg_terms.append(key) else: neg_terms.append(power_fmt.format(key, fun(value))) if not as_ratio: # Show as Product: positive * negative terms ** -1 return _join(product_fmt, pos_terms + neg_terms) # Show as Ratio: positive terms / negative terms pos_ret = _join(product_fmt, pos_terms) or '1' if not neg_terms: return pos_ret if single_denominator: neg_ret = _join(product_fmt, neg_terms) if len(neg_terms) > 1: neg_ret = parentheses_fmt.format(neg_ret) else: neg_ret = _join(division_fmt, neg_terms) return _join(division_fmt, [pos_ret, neg_ret]) # Extract just the type from the specification mini-langage: see # 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: if result: raise ValueError("expected ':' after format specifier") else: result = ch elif ch.isalpha(): raise ValueError("Unknown conversion specified " + ch) else: break return result def format_unit(unit, spec, **kwspec): if not unit: return 'dimensionless' spec = _parse_spec(spec) fmt = dict(_FORMATS[spec]) fmt.update(kwspec) if spec == 'L': rm = [(r'\mathrm{{{0}}}'.format(u), p) for u, p in unit.items()] result = formatter(rm, **fmt) else: result = formatter(unit.items(), **fmt) if spec == 'L': result = result.replace('[', '{').replace(']', '}') return result def siunitx_format_unit(units): '''Returns LaTeX code for the unit that can be put into an siunitx command.''' # NOTE: unit registry is required to identify unit prefixes. registry = units._REGISTRY def _tothe(power): if isinstance(power, int) or (isinstance(power, float) and power.is_integer()): if power == 1: return '' elif power == 2: return r'\squared' elif power == 3: return r'\cubed' else: return r'\tothe{{{:d}}}'.format(int(power)) else: # limit float powers to 3 decimal places return r'\tothe{{{:.3f}}}'.format(power).rstrip('0') lpos = [] lneg = [] # loop through all units in the container for unit, power in sorted(units._units.items()): # remove unit prefix if it exists # siunitx supports \prefix commands l = lpos if power >= 0 else lneg prefix = None for p in registry._prefixes.values(): p = str(p) if len(p) > 0 and unit.find(p) == 0: prefix = p unit = unit.replace(prefix, '', 1) if power < 0: l.append(r'\per') if prefix is not None: l.append(r'\{0}'.format(prefix)) l.append(r'\{0}'.format(unit)) l.append(r'{0}'.format(_tothe(abs(power)))) return ''.join(lpos) + ''.join(lneg) def remove_custom_flags(spec): for flag in _KNOWN_TYPES: if flag: spec = spec.replace(flag, '') return spec def vector_to_latex(vec, fmtfun=lambda x: format(x, '.2f')): return matrix_to_latex([vec], fmtfun) def matrix_to_latex(matrix, fmtfun=lambda x: format(x, '.2f')): ret = [] for row in matrix: ret += [' & '.join(fmtfun(f) for f in row)] return r'\begin{pmatrix}%s\end{pmatrix}' % '\\\\ \n'.join(ret) def ndarray_to_latex_parts(ndarr, fmtfun=lambda x: format(x, '.2f'), dim=()): if isinstance(fmtfun, string_types): fmt = fmtfun fmtfun = lambda x: format(x, fmt) if ndarr.ndim == 1: return [vector_to_latex(ndarr, fmtfun)] if ndarr.ndim == 2: return [matrix_to_latex(ndarr, fmtfun)] else: ret = [] if ndarr.ndim == 3: header = ('arr[%s,' % ','.join('%d' % d for d in dim)) + '%d,:,:]' for elno, el in enumerate(ndarr): ret += [header % elno + ' = ' + matrix_to_latex(el, fmtfun)] else: for elno, el in enumerate(ndarr): ret += ndarray_to_latex_parts(el, fmtfun, dim + (elno, )) return ret def ndarray_to_latex(ndarr, fmtfun=lambda x: format(x, '.2f'), dim=()): return '\n'.join(ndarray_to_latex_parts(ndarr, fmtfun, dim)) pint-0.8.1/pint/measurement.py000066400000000000000000000112031311537545300163470ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.measurement ~~~~~~~~~~~~~~~~ :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import division, unicode_literals, print_function, absolute_import from .compat import ufloat from .formatting import _FORMATS, siunitx_format_unit MISSING = object() class _Measurement(object): """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 """ def __new__(cls, value, error, units=MISSING): if units is MISSING: try: value, units = value.magnitude, value.units except AttributeError: #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"): units = error 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) return inst @property def value(self): return self._REGISTRY.Quantity(self.magnitude.nominal_value, self.units) @property def error(self): return self._REGISTRY.Quantity(self.magnitude.std_dev, self.units) @property def rel(self): return float(abs(self.magnitude.std_dev / self.magnitude.nominal_value)) def __repr__(self): return "".format(self.magnitude.nominal_value, self.magnitude.std_dev, self.units) def __str__(self): return '{0}'.format(self) def __format__(self, spec): # special cases if 'Lx' in spec: # the LaTeX siunitx code # the uncertainties module supports formatting # numbers in value(unc) notation (i.e. 1.23(45) instead of 1.23 +/- 0.45), # which siunitx actually accepts as input. we just need to give the 'S' # formatting option for the uncertainties module. spec = spec.replace('Lx','S') # todo: add support for extracting options opts = 'separate-uncertainty=true' mstr = format( self.magnitude, spec ) ustr = siunitx_format_unit(self.units) ret = r'\SI[%s]{%s}{%s}'%( opts, mstr, ustr ) return ret # standard cases if 'L' in spec: newpm = pm = r' \pm ' pars = _FORMATS['L']['parentheses_fmt'] elif 'P' in spec: newpm = pm = '±' pars = _FORMATS['P']['parentheses_fmt'] else: newpm = pm = '+/-' pars = _FORMATS['']['parentheses_fmt'] if 'C' in spec: sp = '' newspec = spec.replace('C', '') pars = _FORMATS['C']['parentheses_fmt'] else: sp = ' ' newspec = spec if 'H' in spec: newpm = '±' newspec = spec.replace('H', '') pars = _FORMATS['H']['parentheses_fmt'] mag = format(self.magnitude, newspec).replace(pm, sp + newpm + sp) if 'L' in newspec and 'S' in newspec: mag = mag.replace('(', r'\left(').replace(')', r'\right)') if 'L' in newspec: space = r'\ ' else: space = ' ' if 'uS' in newspec or 'ue' in newspec or 'u%' in newspec: return mag + space + format(self.units, spec) else: return pars.format(mag) + space + format(self.units, spec) def build_measurement_class(registry, force_ndarray=False): if ufloat is None: class Measurement(object): 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 return Measurement pint-0.8.1/pint/pint_eval.py000066400000000000000000000161601311537545300160120ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.pint_eval ~~~~~~~~~~~~~~ An expression evaluator to be used as a safe replacement for builtin eval. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from decimal import Decimal import math import operator import token as tokenlib # For controlling order of operations _OP_PRIORITY = { '**': 3, '^': 3, 'unary': 2, '*': 1, '': 1, # operator for implicit ops '/': 1, '+': 0, '-' : 0 } _BINARY_OPERATOR_MAP = { '**': operator.pow, '*': operator.mul, '': operator.mul, # operator for implicit ops '/': operator.truediv, '+': operator.add, '-': operator.sub } _UNARY_OPERATOR_MAP = { '+': lambda x: x, '-': lambda x: x * -1 } class EvalTreeNode(object): def __init__(self, left, operator=None, right=None): """ left + operator + right --> binary op left + operator --> unary op left + right --> implicit op left --> single value """ self.left = left self.operator = operator self.right = right def to_string(self): # For debugging purposes if self.right: comps = [self.left.to_string()] if self.operator: comps.append(self.operator[1]) comps.append(self.right.to_string()) elif self.operator: comps = [self.operator[1], self.left.to_string()] else: return self.left[1] return '(%s)' % ' '.join(comps) def evaluate(self, define_op, bin_op=_BINARY_OPERATOR_MAP, un_op=_UNARY_OPERATOR_MAP): """ define_op is a callable that translates tokens into objects bin_op and un_op provide functions for performing binary and unary operations """ if self.right: # binary or implicit operator op_text = self.operator[1] if self.operator else '' if op_text not in bin_op: raise Exception('missing binary operator "%s"' % op_text) left = self.left.evaluate(define_op, bin_op, un_op) return bin_op[op_text](left, self.right.evaluate(define_op, bin_op, un_op)) elif self.operator: # unary operator op_text = self.operator[1] if op_text not in un_op: raise Exception('missing unary operator "%s"' % op_text) return un_op[op_text](self.left.evaluate(define_op, bin_op, un_op)) else: # single value return define_op(self.left) def build_eval_tree(tokens, op_priority=_OP_PRIORITY, index=0, depth=0, prev_op=None, ): """ Params: Index, depth, and prev_op used recursively, so don't touch. Tokens is an iterable of tokens from an expression to be evaluated. Transform the tokens from an expression into a recursive parse tree, following order of operations. Operations can include binary ops (3 + 4), implicit ops (3 kg), or unary ops (-1). General Strategy: 1) Get left side of operator 2) If no tokens left, return final result 3) Get operator 4) Use recursion to create tree starting at token on right side of operator (start at step #1) 4.1) If recursive call encounters an operator with lower or equal priority to step #2, exit recursion 5) Combine left side, operator, and right side into a new left side 6) Go back to step #2 """ if depth == 0 and prev_op == None: # ensure tokens is list so we can access by index tokens = list(tokens) result = None while True: current_token = tokens[index] token_type = current_token[0] token_text = current_token[1] if token_type == tokenlib.OP: if token_text == ')': if prev_op is None: raise Exception('unopened parentheses in tokens: %s' % current_token) elif prev_op == '(': # close parenthetical group return result, index else: # parenthetical group ending, but we need to close sub-operations within group return result, index - 1 elif token_text == '(': # gather parenthetical group right, index = build_eval_tree(tokens, op_priority, index+1, 0, token_text) if not tokens[index][1] == ')': raise Exception('weird exit from parentheses') if result: # implicit op with a parenthetical group, i.e. "3 (kg ** 2)" result = EvalTreeNode(left=result, right=right) else: # get first token result = right elif token_text in op_priority: if result: # equal-priority operators are grouped in a left-to-right order, unless they're # exponentiation, in which case they're grouped right-to-left # this allows us to get the expected behavior for multiple exponents # (2^3^4) --> (2^(3^4)) # (2 * 3 / 4) --> ((2 * 3) / 4) if op_priority[token_text] <= op_priority.get(prev_op, -1) and token_text not in ['**', '^']: # previous operator is higher priority, so end previous binary op return result, index - 1 # get right side of binary op right, index = build_eval_tree(tokens, op_priority, index+1, depth+1, token_text) result = EvalTreeNode(left=result, operator=current_token, right=right) else: # unary operator right, index = build_eval_tree(tokens, op_priority, index+1, depth+1, 'unary') result = EvalTreeNode(left=right, operator=current_token) elif token_type == tokenlib.NUMBER or token_type == tokenlib.NAME: if result: # tokens with an implicit operation i.e. "1 kg" if op_priority[''] <= op_priority.get(prev_op, -1): # previous operator is higher priority than implicit, so end previous binary op return result, index - 1 right, index = build_eval_tree(tokens, op_priority, index, depth+1, '') result = EvalTreeNode(left=result, right=right) else: # get first token result = EvalTreeNode(left=current_token) if tokens[index][0] == tokenlib.ENDMARKER: if prev_op == '(': raise Exception('unclosed parentheses in tokens') if depth > 0 or prev_op: # have to close recursion return result, index else: # recursion all closed, so just return the final result return result if index + 1 >= len(tokens): # should hit ENDMARKER before this ever happens raise Exception('unexpected end to tokens') index += 1 pint-0.8.1/pint/quantity.py000066400000000000000000001677411311537545300157230ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.quantity ~~~~~~~~~~~~~ :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import division, unicode_literals, print_function, absolute_import import copy import datetime import math import operator import functools import bisect import warnings import numbers from .formatting import (remove_custom_flags, siunitx_format_unit, ndarray_to_latex, ndarray_to_latex_parts) from .errors import (DimensionalityError, OffsetUnitCalculusError, UndefinedUnitError) from .definitions import UnitDefinition from .compat import string_types, ndarray, np, _to_magnitude, long_type from .util import (logger, UnitsContainer, SharedRegistryObject, to_units_container, infer_base_unit, fix_str_conversions) from pint.compat import Loc def _eq(first, second, check_all): """Comparison of scalars and arrays """ out = first == second if check_all and isinstance(out, ndarray): return np.all(out) return out class _Exception(Exception): # pragma: no cover def __init__(self, internal): self.internal = internal def reduce_dimensions(f): def wrapped(self, *args, **kwargs): result = f(self, *args, **kwargs) if result._REGISTRY.auto_reduce_dimensions: return result.to_root_units() else: return result return wrapped def ireduce_dimensions(f): def wrapped(self, *args, **kwargs): result = f(self, *args, **kwargs) if result._REGISTRY.auto_reduce_dimensions: result.ito_root_units() return result return wrapped @fix_str_conversions class _Quantity(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. """ #: Default formatting string. default_format = '' def __reduce__(self): from . import _build_quantity return _build_quantity, (self.magnitude, self._units) def __new__(cls, value, units=None): if units is None: if isinstance(value, string_types): if value == '': raise ValueError('Expression to parse as Quantity cannot ' 'be an empty string.') inst = cls._REGISTRY.parse_expression(value) return cls.__new__(cls, inst) elif isinstance(value, cls): inst = copy.copy(value) else: inst = object.__new__(cls) inst._magnitude = _to_magnitude(value, inst.force_ndarray) inst._units = UnitsContainer() elif isinstance(units, (UnitsContainer, UnitDefinition)): inst = object.__new__(cls) inst._magnitude = _to_magnitude(value, inst.force_ndarray) inst._units = units elif isinstance(units, string_types): inst = object.__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: inst = copy.copy(units) logger.warning('Creating new Quantity using a non unity ' 'Quantity as units.') else: inst = object.__new__(cls) inst._units = units._units inst._magnitude = _to_magnitude(value, inst.force_ndarray) else: raise TypeError('units must be of type str, Quantity or ' 'UnitsContainer; not {0}.'.format(type(units))) inst.__used = False inst.__handling = None return inst @property def debug_used(self): return self.__used def __copy__(self): ret = self.__class__(copy.copy(self._magnitude), self._units) ret.__used = self.__used return ret def __deepcopy__(self, memo): ret = self.__class__(copy.deepcopy(self._magnitude, memo), copy.deepcopy(self._units, memo)) ret.__used = self.__used return ret def __str__(self): return format(self) def __repr__(self): return "".format(self._magnitude, self._units) def __format__(self, spec): spec = spec or self.default_format if 'L' in spec: allf = plain_allf = r'{0}\ {1}' else: allf = plain_allf = '{0} {1}' mstr, ustr = None, None # If Compact is selected, do it at the beginning if '#' in spec: spec = spec.replace('#', '') obj = self.to_compact() else: obj = self # the LaTeX siunitx code if 'Lx' in spec: spec = spec.replace('Lx','') # todo: add support for extracting options opts = '' ustr = siunitx_format_unit(obj.units) allf = r'\SI[%s]{{{0}}}{{{1}}}'% opts else: ustr = format(obj.units, spec) mspec = remove_custom_flags(spec) if isinstance(self.magnitude, ndarray): if 'L' in spec: mstr = ndarray_to_latex(obj.magnitude, mspec) elif 'H' in spec: # this is required to have the magnitude and unit in the same line allf = r'\[{0} {1}\]' parts = ndarray_to_latex_parts(obj.magnitude, mspec) if len(parts) > 1: return '\n'.join(allf.format(part, ustr) for part in parts) mstr = parts[0] else: mstr = format(obj.magnitude, mspec).replace('\n', '') else: mstr = format(obj.magnitude, mspec).replace('\n', '') if allf == plain_allf and ustr.startswith('1 /'): # Write e.g. "3 / s" instead of "3 1 / s" ustr = ustr[2:] return allf.format(mstr, ustr).strip() def format_babel(self, spec='', **kwspec): spec = spec or self.default_format # standard cases if '#' in spec: spec = spec.replace('#', '') obj = self.to_compact() else: obj = self kwspec = dict(kwspec) if 'length' in kwspec: kwspec['babel_length'] = kwspec.pop('length') kwspec['locale'] = Loc.parse(kwspec['locale']) kwspec['babel_plural_form'] = kwspec['locale'].plural_form(obj.magnitude) return '{0} {1}'.format( format(obj.magnitude, remove_custom_flags(spec)), obj.units.format_babel(spec, **kwspec)).replace('\n', '') # IPython related code def _repr_html_(self): return self.__format__('H') def _repr_latex_(self): return "$" + self.__format__('L') + "$" @property def magnitude(self): """Quantity's magnitude. Long form for `m` """ return self._magnitude @property def m(self): """Quantity's magnitude. Short form for `magnitude` """ return self._magnitude def m_as(self, units): """Quantity's magnitude expressed in particular units. :param units: destination units :type units: Quantity, str or dict """ return self.to(units).magnitude @property def units(self): """Quantity's units. Long form for `u` :rtype: UnitContainer """ return self._REGISTRY.Unit(self._units) @property def u(self): """Quantity's units. Short form for `units` :rtype: UnitContainer """ return self._REGISTRY.Unit(self._units) @property def unitless(self): """Return true if the quantity does not have units. """ return not bool(self.to_root_units()._units) @property def dimensionless(self): """Return true if the quantity is dimensionless. """ tmp = self.to_root_units() return not bool(tmp.dimensionality) _dimensionality = None @property def dimensionality(self): """Quantity's dimensionality (e.g. {length: 1, time: -1}) """ if self._dimensionality is None: self._dimensionality = self._REGISTRY._get_dimensionality(self._units) return self._dimensionality @classmethod def from_tuple(cls, tup): return cls(tup[0], UnitsContainer(tup[1])) def to_tuple(self): return self.m, tuple(self._units.items()) def compatible_units(self, *contexts): if contexts: with self._REGISTRY.context(*contexts): return self._REGISTRY.get_compatible_units(self._units) return self._REGISTRY.get_compatible_units(self._units) def _convert_magnitude_not_inplace(self, other, *contexts, **ctx_kwargs): if contexts: with self._REGISTRY.context(*contexts, **ctx_kwargs): return self._REGISTRY.convert(self._magnitude, self._units, other) return self._REGISTRY.convert(self._magnitude, self._units, other) def _convert_magnitude(self, other, *contexts, **ctx_kwargs): if contexts: with self._REGISTRY.context(*contexts, **ctx_kwargs): return self._REGISTRY.convert(self._magnitude, self._units, other) return self._REGISTRY.convert(self._magnitude, self._units, other, inplace=isinstance(self._magnitude, ndarray)) def ito(self, other=None, *contexts, **ctx_kwargs): """Inplace rescale to different units. :param other: destination units. :type other: Quantity, str or dict """ other = to_units_container(other, self._REGISTRY) self._magnitude = self._convert_magnitude(other, *contexts, **ctx_kwargs) self._units = other return None def to(self, other=None, *contexts, **ctx_kwargs): """Return Quantity rescaled to different units. :param other: destination units. :type other: Quantity, str or dict """ other = to_units_container(other, self._REGISTRY) magnitude = self._convert_magnitude_not_inplace(other, *contexts, **ctx_kwargs) return self.__class__(magnitude, other) def ito_root_units(self): """Return Quantity rescaled to base units """ _, other = self._REGISTRY._get_root_units(self._units) self._magnitude = self._convert_magnitude(other) self._units = other return None def to_root_units(self): """Return Quantity rescaled to base units """ _, other = self._REGISTRY._get_root_units(self._units) magnitude = self._convert_magnitude_not_inplace(other) return self.__class__(magnitude, other) def ito_base_units(self): """Return Quantity rescaled to base units """ _, other = self._REGISTRY._get_base_units(self._units) self._magnitude = self._convert_magnitude(other) self._units = other return None def to_base_units(self): """Return Quantity rescaled to base units """ _, other = self._REGISTRY._get_base_units(self._units) magnitude = self._convert_magnitude_not_inplace(other) return self.__class__(magnitude, other) def to_compact(self, unit=None): """Return Quantity rescaled to compact, human-readable units. To get output in terms of a different unit, use the unit parameter. >>> import pint >>> ureg = pint.UnitRegistry() >>> (200e-9*ureg.s).to_compact() >>> (1e-2*ureg('kg m/s^2')).to_compact('N') """ if not isinstance(self.magnitude, numbers.Number): msg = ("to_compact applied to non numerical types " "has an undefined behavior.") w = RuntimeWarning(msg) warnings.warn(w, stacklevel=2) return self if (self.unitless or self.magnitude==0 or math.isnan(self.magnitude) or math.isinf(self.magnitude)): return self SI_prefixes = {} for prefix in self._REGISTRY._prefixes.values(): try: scale = prefix.converter.scale # Kludgy way to check if this is an SI prefix log10_scale = int(math.log10(scale)) if log10_scale == math.log10(scale): SI_prefixes[log10_scale] = prefix.name except: SI_prefixes[0] = '' SI_prefixes = sorted(SI_prefixes.items()) SI_powers = [item[0] for item in SI_prefixes] SI_bases = [item[1] for item in SI_prefixes] if unit is None: unit = infer_base_unit(self) q_base = self.to(unit) magnitude = q_base.magnitude # Only changes the prefix on the first unit in the UnitContainer unit_str = list(q_base._units.items())[0][0] unit_power = list(q_base._units.items())[0][1] if unit_power > 0: power = int(math.floor(math.log10(abs(magnitude)) / unit_power / 3)) * 3 else: power = int(math.ceil(math.log10(abs(magnitude)) / unit_power / 3)) * 3 prefix = SI_bases[bisect.bisect_left(SI_powers, power)] new_unit_str = prefix+unit_str new_unit_container = q_base._units.rename(unit_str, new_unit_str) return self.to(new_unit_container) # Mathematical operations def __int__(self): if self.dimensionless: return int(self._convert_magnitude_not_inplace(UnitsContainer())) raise DimensionalityError(self._units, 'dimensionless') def __long__(self): if self.dimensionless: return long_type(self._convert_magnitude_not_inplace(UnitsContainer())) raise DimensionalityError(self._units, 'dimensionless') def __float__(self): if self.dimensionless: return float(self._convert_magnitude_not_inplace(UnitsContainer())) raise DimensionalityError(self._units, 'dimensionless') def __complex__(self): if self.dimensionless: return complex(self._convert_magnitude_not_inplace(UnitsContainer())) raise DimensionalityError(self._units, 'dimensionless') def _iadd_sub(self, other, op): """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` :param op: operator function (e.g. operator.add, operator.isub) :type op: function """ if not self._check(other): # other not from same Registry or not a Quantity try: other_magnitude = _to_magnitude(other, self.force_ndarray) except TypeError: return NotImplemented if _eq(other, 0, True): # If the other value is 0 (but not Quantity 0) # do the operation without checking units. # We do the calculation instead of just returning the same # value to enforce any shape checking and type casting due to # the operation. self._magnitude = op(self._magnitude, other_magnitude) elif self.dimensionless: self.ito(UnitsContainer()) self._magnitude = op(self._magnitude, other_magnitude) else: raise DimensionalityError(self._units, 'dimensionless') return self if not self.dimensionality == other.dimensionality: raise DimensionalityError(self._units, other._units, self.dimensionality, other.dimensionality) # Next we define some variables to make if-clauses more readable. self_non_mul_units = self._get_non_multiplicative_units() is_self_multiplicative = len(self_non_mul_units) == 0 if len(self_non_mul_units) == 1: self_non_mul_unit = self_non_mul_units[0] other_non_mul_units = other._get_non_multiplicative_units() is_other_multiplicative = len(other_non_mul_units) == 0 if len(other_non_mul_units) == 1: other_non_mul_unit = other_non_mul_units[0] # Presence of non-multiplicative units gives rise to several cases. if is_self_multiplicative and is_other_multiplicative: if self._units == other._units: self._magnitude = op(self._magnitude, other._magnitude) # If only self has a delta unit, other determines unit of result. elif self._get_delta_units() and not other._get_delta_units(): self._magnitude = op(self._convert_magnitude(other._units), other._magnitude) self._units = other._units else: self._magnitude = op(self._magnitude, other.to(self._units)._magnitude) elif (op == operator.isub and len(self_non_mul_units) == 1 and self._units[self_non_mul_unit] == 1 and not other._has_compatible_delta(self_non_mul_unit)): if self._units == other._units: self._magnitude = op(self._magnitude, other._magnitude) else: self._magnitude = op(self._magnitude, other.to(self._units)._magnitude) self._units = self._units.rename(self_non_mul_unit, 'delta_' + self_non_mul_unit) elif (op == operator.isub and len(other_non_mul_units) == 1 and other._units[other_non_mul_unit] == 1 and not self._has_compatible_delta(other_non_mul_unit)): # we convert to self directly since it is multiplicative self._magnitude = op(self._magnitude, other.to(self._units)._magnitude) elif (len(self_non_mul_units) == 1 # order of the dimension of offset unit == 1 ? and self._units[self_non_mul_unit] == 1 and other._has_compatible_delta(self_non_mul_unit)): # Replace offset unit in self by the corresponding delta unit. # This is done to prevent a shift by offset in the to()-call. tu = self._units.rename(self_non_mul_unit, 'delta_' + self_non_mul_unit) self._magnitude = op(self._magnitude, other.to(tu)._magnitude) elif (len(other_non_mul_units) == 1 # order of the dimension of offset unit == 1 ? and other._units[other_non_mul_unit] == 1 and self._has_compatible_delta(other_non_mul_unit)): # Replace offset unit in other by the corresponding delta unit. # This is done to prevent a shift by offset in the to()-call. tu = other._units.rename(other_non_mul_unit, 'delta_' + other_non_mul_unit) self._magnitude = op(self._convert_magnitude(tu), other._magnitude) self._units = other._units else: raise OffsetUnitCalculusError(self._units, other._units) return self def _add_sub(self, other, op): """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` :param op: operator function (e.g. operator.add, operator.isub) :type op: function """ if not self._check(other): # other not from same Registry or not a Quantity if _eq(other, 0, True): # If the other value is 0 (but not Quantity 0) # do the operation without checking units. # We do the calculation instead of just returning the same # value to enforce any shape checking and type casting due to # the operation. units = self._units magnitude = op(self._magnitude, _to_magnitude(other, self.force_ndarray)) elif self.dimensionless: units = UnitsContainer() magnitude = op(self.to(units)._magnitude, _to_magnitude(other, self.force_ndarray)) else: raise DimensionalityError(self._units, 'dimensionless') return self.__class__(magnitude, units) if not self.dimensionality == other.dimensionality: raise DimensionalityError(self._units, other._units, self.dimensionality, other.dimensionality) # Next we define some variables to make if-clauses more readable. self_non_mul_units = self._get_non_multiplicative_units() is_self_multiplicative = len(self_non_mul_units) == 0 if len(self_non_mul_units) == 1: self_non_mul_unit = self_non_mul_units[0] other_non_mul_units = other._get_non_multiplicative_units() is_other_multiplicative = len(other_non_mul_units) == 0 if len(other_non_mul_units) == 1: other_non_mul_unit = other_non_mul_units[0] # Presence of non-multiplicative units gives rise to several cases. if is_self_multiplicative and is_other_multiplicative: if self._units == other._units: magnitude = op(self._magnitude, other._magnitude) units = self._units # If only self has a delta unit, other determines unit of result. elif self._get_delta_units() and not other._get_delta_units(): magnitude = op(self._convert_magnitude(other._units), other._magnitude) units = other._units else: units = self._units magnitude = op(self._magnitude, other.to(self._units).magnitude) elif (op == operator.sub and len(self_non_mul_units) == 1 and self._units[self_non_mul_unit] == 1 and not other._has_compatible_delta(self_non_mul_unit)): if self._units == other._units: magnitude = op(self._magnitude, other._magnitude) else: magnitude = op(self._magnitude, other.to(self._units)._magnitude) units = self._units.rename(self_non_mul_unit, 'delta_' + self_non_mul_unit) elif (op == operator.sub and len(other_non_mul_units) == 1 and other._units[other_non_mul_unit] == 1 and not self._has_compatible_delta(other_non_mul_unit)): # we convert to self directly since it is multiplicative magnitude = op(self._magnitude, other.to(self._units)._magnitude) units = self._units elif (len(self_non_mul_units) == 1 # order of the dimension of offset unit == 1 ? and self._units[self_non_mul_unit] == 1 and other._has_compatible_delta(self_non_mul_unit)): # Replace offset unit in self by the corresponding delta unit. # This is done to prevent a shift by offset in the to()-call. tu = self._units.rename(self_non_mul_unit, 'delta_' + self_non_mul_unit) magnitude = op(self._magnitude, other.to(tu).magnitude) units = self._units elif (len(other_non_mul_units) == 1 # order of the dimension of offset unit == 1 ? and other._units[other_non_mul_unit] == 1 and self._has_compatible_delta(other_non_mul_unit)): # Replace offset unit in other by the corresponding delta unit. # This is done to prevent a shift by offset in the to()-call. tu = other._units.rename(other_non_mul_unit, 'delta_' + other_non_mul_unit) magnitude = op(self._convert_magnitude(tu), other._magnitude) units = other._units else: raise OffsetUnitCalculusError(self._units, other._units) return self.__class__(magnitude, units) def __iadd__(self, other): if isinstance(other, datetime.datetime): return self.to_timedelta() + other elif not isinstance(self._magnitude, ndarray): return self._add_sub(other, operator.add) else: return self._iadd_sub(other, operator.iadd) def __add__(self, other): if isinstance(other, datetime.datetime): return self.to_timedelta() + other else: return self._add_sub(other, operator.add) __radd__ = __add__ def __isub__(self, other): if not isinstance(self._magnitude, ndarray): return self._add_sub(other, operator.sub) else: return self._iadd_sub(other, operator.isub) def __sub__(self, other): return self._add_sub(other, operator.sub) def __rsub__(self, other): if isinstance(other, datetime.datetime): return other - self.to_timedelta() else: return -self._add_sub(other, operator.sub) @ireduce_dimensions def _imul_div(self, other, magnitude_op, units_op=None): """Perform multiplication or division operation in-place and return the result. :param other: object to be multiplied/divided with self :type other: 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 :param units_op: operator function to perform on the units; if None, *magnitude_op* is used :type units_op: function or None """ if units_op is None: units_op = magnitude_op offset_units_self = self._get_non_multiplicative_units() no_offset_units_self = len(offset_units_self) if not self._check(other): if not self._ok_for_muldiv(no_offset_units_self): raise OffsetUnitCalculusError(self._units, getattr(other, 'units', '')) if len(offset_units_self) == 1: if (self._units[offset_units_self[0]] != 1 or magnitude_op not in [operator.mul, operator.imul]): raise OffsetUnitCalculusError(self._units, getattr(other, 'units', '')) try: other_magnitude = _to_magnitude(other, self.force_ndarray) except TypeError: return NotImplemented self._magnitude = magnitude_op(self._magnitude, other_magnitude) self._units = units_op(self._units, UnitsContainer()) return self if isinstance(other, self._REGISTRY.Unit): other = 1.0 * other if not self._ok_for_muldiv(no_offset_units_self): raise OffsetUnitCalculusError(self._units, other._units) elif no_offset_units_self == 1 and len(self._units) == 1: self.ito_root_units() no_offset_units_other = len(other._get_non_multiplicative_units()) if not other._ok_for_muldiv(no_offset_units_other): raise OffsetUnitCalculusError(self._units, other._units) elif no_offset_units_other == 1 and len(other._units) == 1: other.ito_root_units() self._magnitude = magnitude_op(self._magnitude, other._magnitude) self._units = units_op(self._units, other._units) return self @reduce_dimensions def _mul_div(self, other, magnitude_op, units_op=None): """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` :param magnitude_op: operator function to perform on the magnitudes (e.g. operator.mul) :type magnitude_op: function :param units_op: operator function to perform on the units; if None, *magnitude_op* is used :type units_op: function or None """ if units_op is None: units_op = magnitude_op offset_units_self = self._get_non_multiplicative_units() no_offset_units_self = len(offset_units_self) if not self._check(other): if not self._ok_for_muldiv(no_offset_units_self): raise OffsetUnitCalculusError(self._units, getattr(other, 'units', '')) if len(offset_units_self) == 1: if (self._units[offset_units_self[0]] != 1 or magnitude_op not in [operator.mul, operator.imul]): raise OffsetUnitCalculusError(self._units, getattr(other, 'units', '')) try: other_magnitude = _to_magnitude(other, self.force_ndarray) except TypeError: return NotImplemented magnitude = magnitude_op(self._magnitude, other_magnitude) units = units_op(self._units, UnitsContainer()) return self.__class__(magnitude, units) if isinstance(other, self._REGISTRY.Unit): other = 1.0 * other new_self = self if not self._ok_for_muldiv(no_offset_units_self): raise OffsetUnitCalculusError(self._units, other._units) elif no_offset_units_self == 1 and len(self._units) == 1: new_self = self.to_root_units() no_offset_units_other = len(other._get_non_multiplicative_units()) if not other._ok_for_muldiv(no_offset_units_other): raise OffsetUnitCalculusError(self._units, other._units) elif no_offset_units_other == 1 and len(other._units) == 1: other = other.to_root_units() magnitude = magnitude_op(new_self._magnitude, other._magnitude) units = units_op(new_self._units, other._units) return self.__class__(magnitude, units) def __imul__(self, other): if not isinstance(self._magnitude, ndarray): return self._mul_div(other, operator.mul) else: return self._imul_div(other, operator.imul) def __mul__(self, other): return self._mul_div(other, operator.mul) __rmul__ = __mul__ def __itruediv__(self, other): if not isinstance(self._magnitude, ndarray): return self._mul_div(other, operator.truediv) else: return self._imul_div(other, operator.itruediv) def __truediv__(self, other): return self._mul_div(other, operator.truediv) def __rtruediv__(self, other): try: other_magnitude = _to_magnitude(other, self.force_ndarray) except TypeError: return NotImplemented no_offset_units_self = len(self._get_non_multiplicative_units()) if not self._ok_for_muldiv(no_offset_units_self): raise OffsetUnitCalculusError(self._units, '') elif no_offset_units_self == 1 and len(self._units) == 1: self = self.to_root_units() return self.__class__(other_magnitude / self._magnitude, 1 / self._units) __div__ = __truediv__ __rdiv__ = __rtruediv__ __idiv__ = __itruediv__ def __ifloordiv__(self, other): if self._check(other): self._magnitude //= other.to(self._units)._magnitude elif self.dimensionless: self._magnitude = self.to('')._magnitude // other else: raise DimensionalityError(self._units, 'dimensionless') self._units = UnitsContainer({}) return self def __floordiv__(self, other): if self._check(other): magnitude = self._magnitude // other.to(self._units)._magnitude elif self.dimensionless: magnitude = self.to('')._magnitude // other else: raise DimensionalityError(self._units, 'dimensionless') return self.__class__(magnitude, UnitsContainer({})) def __rfloordiv__(self, other): if self._check(other): magnitude = other._magnitude // self.to(other._units)._magnitude elif self.dimensionless: magnitude = other // self.to('')._magnitude else: raise DimensionalityError(self._units, 'dimensionless') return self.__class__(magnitude, UnitsContainer({})) def __imod__(self, other): if not self._check(other): other = self.__class__(other, UnitsContainer({})) self._magnitude %= other.to(self._units)._magnitude return self def __mod__(self, other): if not self._check(other): other = self.__class__(other, UnitsContainer({})) magnitude = self._magnitude % other.to(self._units)._magnitude return self.__class__(magnitude, self._units) def __rmod__(self, other): if self._check(other): magnitude = other._magnitude % self.to(other._units)._magnitude return self.__class__(magnitude, other._units) elif self.dimensionless: magnitude = other % self.to('')._magnitude return self.__class__(magnitude, UnitsContainer({})) else: raise DimensionalityError(self._units, 'dimensionless') def __divmod__(self, other): if not self._check(other): other = self.__class__(other, UnitsContainer({})) q, r = divmod(self._magnitude, other.to(self._units)._magnitude) return (self.__class__(q, UnitsContainer({})), self.__class__(r, self._units)) def __rdivmod__(self, other): if self._check(other): q, r = divmod(other._magnitude, self.to(other._units)._magnitude) unit = other._units elif self.dimensionless: q, r = divmod(other, self.to('')._magnitude) unit = UnitsContainer({}) else: raise DimensionalityError(self._units, 'dimensionless') return (self.__class__(q, UnitsContainer({})), self.__class__(r, unit)) def __ipow__(self, other): if not isinstance(self._magnitude, ndarray): return self.__pow__(other) try: other_magnitude = _to_magnitude(other, self.force_ndarray) except TypeError: return NotImplemented else: if not self._ok_for_muldiv: raise OffsetUnitCalculusError(self._units) if isinstance(getattr(other, '_magnitude', other), ndarray): # arrays are refused as exponent, because they would create # len(array) quantities of len(set(array)) different units # unless the base is dimensionless. if self.dimensionless: if getattr(other, 'dimensionless', False): self._magnitude **= other.m_as('') return self elif not getattr(other, 'dimensionless', True): raise DimensionalityError(other._units, 'dimensionless') else: self._magnitude **= other return self elif np.size(other) > 1: raise DimensionalityError(self._units, 'dimensionless', extra_msg='Quantity array exponents are only allowed ' 'if the base is dimensionless') if other == 1: return self elif other == 0: self._units = UnitsContainer() else: if not self._is_multiplicative: if self._REGISTRY.autoconvert_offset_to_baseunit: self.ito_base_units() else: raise OffsetUnitCalculusError(self._units) if getattr(other, 'dimensionless', False): other = other.to_base_units() self._units **= other.magnitude elif not getattr(other, 'dimensionless', True): raise DimensionalityError(self._units, 'dimensionless') else: self._units **= other self._magnitude **= _to_magnitude(other, self.force_ndarray) return self def __pow__(self, other): try: other_magnitude = _to_magnitude(other, self.force_ndarray) except TypeError: return NotImplemented else: if not self._ok_for_muldiv: raise OffsetUnitCalculusError(self._units) if isinstance(getattr(other, '_magnitude', other), ndarray): # arrays are refused as exponent, because they would create # len(array) quantities of len(set(array)) different units # unless the base is dimensionless. if self.dimensionless: if getattr(other, 'dimensionless', False): return self.__class__(self.m ** other.m_as('')) elif not getattr(other, 'dimensionless', True): raise DimensionalityError(other._units, 'dimensionless') else: return self.__class__(self.m ** other) elif np.size(other) > 1: raise DimensionalityError(self._units, 'dimensionless', extra_msg='Quantity array exponents are only allowed ' 'if the base is dimensionless') new_self = self if other == 1: return self elif other == 0: units = UnitsContainer() else: if not self._is_multiplicative: if self._REGISTRY.autoconvert_offset_to_baseunit: new_self = self.to_root_units() else: raise OffsetUnitCalculusError(self._units) if getattr(other, 'dimensionless', False): units = new_self._units ** other.to_root_units().magnitude elif not getattr(other, 'dimensionless', True): raise DimensionalityError(self._units, 'dimensionless') else: units = new_self._units ** other magnitude = new_self._magnitude ** _to_magnitude(other, self.force_ndarray) return self.__class__(magnitude, units) def __rpow__(self, other): try: other_magnitude = _to_magnitude(other, self.force_ndarray) except TypeError: return NotImplemented else: if not self.dimensionless: raise DimensionalityError(self._units, 'dimensionless') if isinstance(self._magnitude, ndarray): if np.size(self._magnitude) > 1: raise DimensionalityError(self._units, 'dimensionless') new_self = self.to_root_units() return other**new_self._magnitude def __abs__(self): return self.__class__(abs(self._magnitude), self._units) def __round__(self, ndigits=0): return self.__class__(round(self._magnitude, ndigits=ndigits), self._units) def __pos__(self): return self.__class__(operator.pos(self._magnitude), self._units) def __neg__(self): return self.__class__(operator.neg(self._magnitude), self._units) def __eq__(self, other): # We compare to the base class of Quantity because # each Quantity class is unique. if not isinstance(other, _Quantity): return (self.dimensionless and _eq(self._convert_magnitude(UnitsContainer()), other, False)) if _eq(self._magnitude, 0, True) and _eq(other._magnitude, 0, True): return self.dimensionality == other.dimensionality if self._units == other._units: return _eq(self._magnitude, other._magnitude, False) try: return _eq(self._convert_magnitude_not_inplace(other._units), other._magnitude, False) except DimensionalityError: return False def __ne__(self, other): out = self.__eq__(other) if isinstance(out, ndarray): return np.logical_not(out) return not out def compare(self, other, op): if not isinstance(other, self.__class__): if self.dimensionless: return op(self._convert_magnitude_not_inplace(UnitsContainer()), other) else: raise ValueError('Cannot compare Quantity and {0}'.format(type(other))) if self._units == other._units: return op(self._magnitude, other._magnitude) if self.dimensionality != other.dimensionality: raise DimensionalityError(self._units, other._units, self.dimensionality, other.dimensionality) return op(self.to_root_units().magnitude, other.to_root_units().magnitude) __lt__ = lambda self, other: self.compare(other, op=operator.lt) __le__ = lambda self, other: self.compare(other, op=operator.le) __ge__ = lambda self, other: self.compare(other, op=operator.ge) __gt__ = lambda self, other: self.compare(other, op=operator.gt) def __bool__(self): return bool(self._magnitude) __nonzero__ = __bool__ # NumPy Support __radian = 'radian' __same_units = 'equal greater greater_equal less less_equal not_equal arctan2'.split() #: Dictionary mapping ufunc/attributes names to the units that they #: require (conversion will be tried). __require_units = {'cumprod': '', 'arccos': '', 'arcsin': '', 'arctan': '', 'arccosh': '', 'arcsinh': '', 'arctanh': '', 'exp': '', 'expm1': '', 'exp2': '', 'log': '', 'log10': '', 'log1p': '', 'log2': '', 'sin': __radian, 'cos': __radian, 'tan': __radian, 'sinh': __radian, 'cosh': __radian, 'tanh': __radian, 'radians': 'degree', 'degrees': __radian, 'deg2rad': 'degree', 'rad2deg': __radian, 'logaddexp': '', 'logaddexp2': ''} #: Dictionary mapping ufunc/attributes names to the units that they #: will set on output. __set_units = {'cos': '', 'sin': '', 'tan': '', 'cosh': '', 'sinh': '', 'tanh': '', 'log': '', 'exp': '', 'arccos': __radian, 'arcsin': __radian, 'arctan': __radian, 'arctan2': __radian, 'arccosh': __radian, 'arcsinh': __radian, 'arctanh': __radian, 'degrees': 'degree', 'radians': __radian, 'expm1': '', 'cumprod': '', 'rad2deg': 'degree', 'deg2rad': __radian} #: List of ufunc/attributes names in which units are copied from the #: original. __copy_units = 'compress conj conjugate copy cumsum diagonal flatten ' \ 'max mean min ptp ravel repeat reshape round ' \ 'squeeze std sum swapaxes take trace transpose ' \ 'ceil floor hypot rint ' \ 'add subtract ' \ 'copysign nextafter trunc ' \ 'frexp ldexp modf modf__1 ' \ 'absolute negative remainder fmod mod'.split() #: Dictionary mapping ufunc/attributes names to the units that they will #: set on output. The value is interpreted as the power to which the unit #: will be raised. __prod_units = {'var': 2, 'prod': 'size', 'multiply': 'mul', 'true_divide': 'div', 'divide': 'div', 'floor_divide': 'div', 'remainder': 'div', 'sqrt': .5, 'square': 2, 'reciprocal': -1} __skip_other_args = 'ldexp multiply ' \ 'true_divide divide floor_divide fmod mod ' \ 'remainder'.split() __handled = tuple(__same_units) + \ tuple(__require_units.keys()) + \ tuple(__prod_units.keys()) + \ tuple(__copy_units) + tuple(__skip_other_args) def clip(self, first=None, second=None, out=None, **kwargs): min = kwargs.get('min', first) max = kwargs.get('max', second) if min is None and max is None: raise TypeError('clip() takes at least 3 arguments (2 given)') if max is None and 'min' not in kwargs: min, max = max, min kwargs = {'out': out} if min is not None: if isinstance(min, self.__class__): kwargs['min'] = min.to(self).magnitude elif self.dimensionless: kwargs['min'] = min else: raise DimensionalityError('dimensionless', self._units) if max is not None: if isinstance(max, self.__class__): kwargs['max'] = max.to(self).magnitude elif self.dimensionless: kwargs['max'] = max else: raise DimensionalityError('dimensionless', self._units) return self.__class__(self.magnitude.clip(**kwargs), self._units) def fill(self, value): self._units = value._units return self.magnitude.fill(value.magnitude) def put(self, indices, values, mode='raise'): if isinstance(values, self.__class__): values = values.to(self).magnitude elif self.dimensionless: values = self.__class__(values, '').to(self) else: raise DimensionalityError('dimensionless', self._units) self.magnitude.put(indices, values, mode) @property def real(self): return self.__class__(self._magnitude.real, self._units) @property def imag(self): return self.__class__(self._magnitude.imag, self._units) @property def T(self): return self.__class__(self._magnitude.T, self._units) @property def flat(self): for v in self._magnitude.flat: yield self.__class__(v, self._units) @property def shape(self): return self._magnitude.shape @shape.setter def shape(self, value): self._magnitude.shape = value def searchsorted(self, v, side='left'): if isinstance(v, self.__class__): v = v.to(self).magnitude elif self.dimensionless: v = self.__class__(v, '').to(self) else: raise DimensionalityError('dimensionless', self._units) return self.magnitude.searchsorted(v, side) def __ito_if_needed(self, to_units): if self.unitless and to_units == 'radian': return self.ito(to_units) def __numpy_method_wrap(self, func, *args, **kwargs): """Convenience method to wrap on the fly numpy method taking care of the units. """ if func.__name__ in self.__require_units: self.__ito_if_needed(self.__require_units[func.__name__]) value = func(*args, **kwargs) if func.__name__ in self.__copy_units: return self.__class__(value, self._units) if func.__name__ in self.__prod_units: tmp = self.__prod_units[func.__name__] if tmp == 'size': return self.__class__(value, self._units ** self._magnitude.size) return self.__class__(value, self._units ** tmp) return value def __len__(self): return len(self._magnitude) def __iter__(self): # 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)) def __getattr__(self, item): # Attributes starting with `__array_` are common attributes of NumPy ndarray. # They are requested by numpy functions. if item.startswith('__array_'): if isinstance(self._magnitude, ndarray): return getattr(self._magnitude, item) else: # If an `__array_` attributes is requested but the magnitude is not an ndarray, # we convert the magnitude to a numpy ndarray. self._magnitude = _to_magnitude(self._magnitude, force_ndarray=True) return getattr(self._magnitude, item) elif item in self.__handled: if not isinstance(self._magnitude, ndarray): self._magnitude = _to_magnitude(self._magnitude, True) attr = getattr(self._magnitude, item) if callable(attr): return functools.partial(self.__numpy_method_wrap, attr) return attr try: return getattr(self._magnitude, item) except AttributeError as ex: raise AttributeError("Neither Quantity object nor its magnitude ({0}) " "has attribute '{1}'".format(self._magnitude, item)) def __getitem__(self, key): try: value = self._magnitude[key] return self.__class__(value, self._units) except TypeError: raise TypeError("Neither Quantity object nor its magnitude ({0})" "supports indexing".format(self._magnitude)) def __setitem__(self, key, value): try: if math.isnan(value): self._magnitude[key] = value return except (TypeError, DimensionalityError): pass try: if isinstance(value, self.__class__): factor = self.__class__(value.magnitude, value._units / self._units).to_root_units() else: factor = self.__class__(value, self._units ** (-1)).to_root_units() if isinstance(factor, self.__class__): if not factor.dimensionless: raise DimensionalityError(value, self.units, extra_msg='. Assign a quantity with the same dimensionality or ' 'access the magnitude directly as ' '`obj.magnitude[%s] = %s`' % (key, value)) self._magnitude[key] = factor.magnitude else: self._magnitude[key] = factor except TypeError: raise TypeError("Neither Quantity object nor its magnitude ({0})" "supports indexing".format(self._magnitude)) def tolist(self): units = self._units return [self.__class__(value, units).tolist() if isinstance(value, list) else self.__class__(value, units) for value in self._magnitude.tolist()] __array_priority__ = 17 def __array_prepare__(self, obj, context=None): # If this uf is handled by Pint, write it down in the handling dictionary. # name of the ufunc, argument of the ufunc, domain of the ufunc # In ufuncs with multiple outputs, domain indicates which output # is currently being prepared (eg. see modf). # In ufuncs with a single output, domain is 0 uf, objs, i_out = context if uf.__name__ in self.__handled and i_out == 0: # Only one ufunc should be handled at a time. # If a ufunc is already being handled (and this is not another domain), # something is wrong.. if self.__handling: raise Exception('Cannot handled nested ufuncs.\n' 'Current: {0}\n' 'New: {1}'.format(context, self.__handling)) self.__handling = context return obj def __array_wrap__(self, obj, context=None): uf, objs, i_out = context # if this ufunc is not handled by Pint, pass it to the magnitude. if uf.__name__ not in self.__handled: return self.magnitude.__array_wrap__(obj, context) try: ufname = uf.__name__ if i_out == 0 else '{0}__{1}'.format(uf.__name__, i_out) # First, we check the units of the input arguments. if i_out == 0: # Do this only when the wrap is called for the first ouput. # Store the destination units dst_units = None # List of magnitudes of Quantities with the right units # to be used as argument of the ufunc mobjs = None if uf.__name__ in self.__require_units: # ufuncs in __require_units # require specific units # This is more complex that it should be due to automatic # conversion between radians/dimensionless # TODO: maybe could be simplified using Contexts dst_units = self.__require_units[uf.__name__] if dst_units == 'radian': mobjs = [] for other in objs: unt = getattr(other, '_units', '') if unt == 'radian': mobjs.append(getattr(other, 'magnitude', other)) else: factor, units = self._REGISTRY._get_root_units(unt) if units and units != UnitsContainer({'radian': 1}): raise DimensionalityError(units, dst_units) mobjs.append(getattr(other, 'magnitude', other) * factor) mobjs = tuple(mobjs) else: dst_units = self._REGISTRY.parse_expression(dst_units)._units elif len(objs) > 1 and uf.__name__ not in self.__skip_other_args: # ufunc with multiple arguments require that all inputs have # the same arguments unless they are in __skip_other_args dst_units = objs[0]._units # Do the conversion (if needed) and extract the magnitude for each input. if mobjs is None: if dst_units is not None: mobjs = tuple(self._REGISTRY.convert(getattr(other, 'magnitude', other), getattr(other, 'units', ''), dst_units) for other in objs) else: mobjs = tuple(getattr(other, 'magnitude', other) for other in objs) # call the ufunc out = uf(*mobjs) # If there are multiple outputs, # store them in __handling (uf, objs, i_out, out0, out1, ...) # and return the first if uf.nout > 1: self.__handling += out out = out[0] else: # If this is not the first output, # just grab the result that was previously calculated. out = self.__handling[3 + i_out] # Second, we set the units of the output value. if ufname in self.__set_units: try: out = self.__class__(out, self.__set_units[ufname]) except: raise _Exception(ValueError) elif ufname in self.__copy_units: try: out = self.__class__(out, self._units) except: raise _Exception(ValueError) elif ufname in self.__prod_units: tmp = self.__prod_units[ufname] if tmp == 'size': out = self.__class__(out, self._units ** self._magnitude.size) elif tmp == 'div': units1 = objs[0]._units if isinstance(objs[0], self.__class__) else UnitsContainer() units2 = objs[1]._units if isinstance(objs[1], self.__class__) else UnitsContainer() out = self.__class__(out, units1 / units2) elif tmp == 'mul': units1 = objs[0]._units if isinstance(objs[0], self.__class__) else UnitsContainer() units2 = objs[1]._units if isinstance(objs[1], self.__class__) else UnitsContainer() out = self.__class__(out, units1 * units2) else: out = self.__class__(out, self._units ** tmp) return out except (DimensionalityError, UndefinedUnitError) as ex: raise ex except _Exception as ex: raise ex.internal except Exception as ex: print(ex) finally: # If this is the last output argument for the ufunc, # we are done handling this ufunc. if uf.nout == i_out + 1: self.__handling = None return self.magnitude.__array_wrap__(obj, context) # Measurement support def plus_minus(self, error, relative=False): if isinstance(error, self.__class__): if relative: raise ValueError('{} is not a valid relative error.'.format(error)) error = error.to(self._units).magnitude else: if relative: error = error * abs(self.magnitude) return self._REGISTRY.Measurement(copy.copy(self.magnitude), error, self._units) # methods/properties that help for math operations with offset units @property def _is_multiplicative(self): """Check if the Quantity object has only multiplicative units. """ return not self._get_non_multiplicative_units() def _get_non_multiplicative_units(self): """Return a list of the of non-multiplicative units of the Quantity object """ offset_units = [unit for unit in self._units.keys() if not self._REGISTRY._units[unit].is_multiplicative] return offset_units def _get_delta_units(self): """Return list of delta units ot the Quantity object """ delta_units = [u for u in self._units.keys() if u.startswith("delta_")] return delta_units def _has_compatible_delta(self, unit): """"Check if Quantity object has a delta_unit that is compatible with unit """ deltas = self._get_delta_units() if 'delta_' + unit in deltas: return True else: # Look for delta units with same dimension as the offset unit offset_unit_dim = self._REGISTRY._units[unit].reference for d in deltas: if self._REGISTRY._units[d].reference == offset_unit_dim: return True return False def _ok_for_muldiv(self, no_offset_units=None): """Checks if Quantity object can be multiplied or divided :q: quantity object that is checked :no_offset_units: number of offset units in q """ is_ok = True if no_offset_units is None: no_offset_units = len(self._get_non_multiplicative_units()) if no_offset_units > 1: is_ok = False if no_offset_units == 1: if len(self._units) > 1: is_ok = False if (len(self._units) == 1 and not self._REGISTRY.autoconvert_offset_to_baseunit): is_ok = False if next(iter(self._units.values())) != 1: is_ok = False return is_ok def to_timedelta(self): return datetime.timedelta(microseconds=self.to('microseconds').magnitude) def build_quantity_class(registry, force_ndarray=False): class Quantity(_Quantity): pass Quantity._REGISTRY = registry Quantity.force_ndarray = force_ndarray return Quantity pint-0.8.1/pint/registry.py000066400000000000000000001544411311537545300157060ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.registry ~~~~~~~~~~~~~ Defines the Registry, a class to contain units and their relations. The module actually defines 5 registries with different capabilites: - BaseRegistry: Basic unit definition and querying. Conversion between multiplicative units. - 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. (e.g. in MKS, meter, kilogram and second are base units.) * Inherits from BaseRegistry - UnitRegistry: Combine all previous capabilities, it is exposed by Pint. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import division, unicode_literals, print_function, absolute_import import os import re import math import functools import itertools import pkg_resources from decimal import Decimal from fractions import Fraction from contextlib import contextmanager, closing from io import open, StringIO from collections import defaultdict from tokenize import NUMBER, NAME from . import registry_helpers from .context import Context, ContextChain from .util import (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) from .converters import ScaleConverter from .errors import (DimensionalityError, UndefinedUnitError, DefinitionSyntaxError, RedefinitionError) from .pint_eval import build_eval_tree from . import systems _BLOCK_RE = re.compile(r' |\(') class _Meta(type): """This is just to call after_init at the right time instead of asking the developer to do it when subclassing. """ def __call__(self, *args, **kwargs): obj = super(_Meta, self).__call__(*args, **kwargs) obj._after_init() return obj class BaseRegistry(meta.with_metaclass(_Meta)): """Base class for all registries. Capabilities: - Register units, prefixes, and dimensions, and their relations. - Convert between units. - Find dimensionality of a unit. - Parse units with prefix and/or suffix. - Parse expressions. - Parse a definition file. - Allow extending the definition file parser by registering @ directives. :param filename: path of the units definition file to load. Empty to load the default definition file. None to leave the UnitRegistry empty. :type filename: str | 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' :type on_redefinition: str :param auto_reduce_dimensions: If True, reduce dimensionality on appropriate operations. """ #: Map context prefix to function #: type: Dict[str, (SourceIterator -> None)] _parsers = None #: List to be used in addition of units when dir(registry) is called. #: Also used for autocompletion in IPython. _dir = ['Quantity', 'Unit', 'Measurement', 'define', 'load_definitions', 'get_name', 'get_symbol', 'get_dimensionality', 'get_base_units', 'get_root_units', 'parse_unit_name', 'parse_units', 'parse_expression', 'convert'] 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._filename = filename #: Action to take in case a unit is redefined. 'warn', 'raise', 'ignore' self._on_redefinition = on_redefinition #: Determines if dimensionality should be reduced on appropriate operations. self.auto_reduce_dimensions = auto_reduce_dimensions #: Map between name (string) and value (string) of defaults stored in the definitions file. self._defaults = {} #: Map dimension name (string) to its definition (DimensionDefinition). self._dimensions = {} #: Map unit name (string) to its definition (UnitDefinition). #: Might contain prefixed units. self._units = {} #: Map unit name in lower case (string) to a set of unit names with the right case. #: Does not contain prefixed units. #: e.g: 'hz' - > set('Hz', ) self._units_casei = defaultdict(set) #: Map prefix name (string) to its definition (PrefixDefinition). self._prefixes = {'': PrefixDefinition('', '', (), 1)} #: Map suffix name (string) to canonical , and unit alias to canonical unit name self._suffixes = {'': None, 's': ''} #: Maps dimensionality (UnitsContainer) to Units (str) self._dimensional_equivalents = dict() #: Maps dimensionality (UnitsContainer) to Dimensionality (UnitsContainer) self._root_units_cache = dict() #: Maps dimensionality (UnitsContainer) to Units (UnitsContainer) self._dimensionality_cache = dict() #: Cache the unit name associated to user input. ('mV' -> 'millivolt') self._parse_unit_cache = dict() self._initialized = False def _after_init(self): """This should be called after all __init__ """ 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 def _register_parsers(self): self._register_parser('@defaults', self._parse_defaults) def _parse_defaults(self, ifile): """Loader for a @default section. :type ifile: SourceITerator """ next(ifile) for lineno, part in ifile.block_iter(): k, v = part.split('=') self._defaults[k.strip()] = v.strip() def __name__(self): return 'UnitRegistry' def __getattr__(self, item): if item[0] == '_': return super(BaseRegistry, self).__getattribute__(item) return self.Unit(item) def __getitem__(self, item): logger.warning('Calling the getitem method from a UnitRegistry is deprecated. ' 'use `parse_expression` method or use the registry as a callable.') return self.parse_expression(item) def __dir__(self): return list(self._units.keys()) + self._dir @property def default_format(self): """Default formatting string for quantities. """ return self.Quantity.default_format @default_format.setter def default_format(self, value): self.Unit.default_format = value self.Quantity.default_format = value def define(self, definition): """Add unit to the registry. :param definition: a dimension, unit or prefix definition. :type definition: str | Definition """ if isinstance(definition, string_types): for line in definition.split('\n'): self._define(Definition.from_string(line)) else: self._define(definition) def _define(self, definition): """Add unit to the registry. This method defines only multiplicative units, converting any other type to `delta_` units. :param definition: a dimension, unit or prefix definition. :type definition: Definition :return: Definition instance, case sensitive unit dict, case insensitive unit dict. :rtype: Definition, dict, dict """ if isinstance(definition, DimensionDefinition): d, di = self._dimensions, None elif isinstance(definition, UnitDefinition): d, di = self._units, self._units_casei # For a base units, we need to define the related dimension # (making sure there is only one to define) if definition.is_base: for dimension in definition.reference.keys(): if dimension in self._dimensions: if dimension != '[]': raise DefinitionSyntaxError('only one unit per dimension can be a base unit.') continue self.define(DimensionDefinition(dimension, '', (), None, is_base=True)) elif isinstance(definition, PrefixDefinition): d, di = self._prefixes, None else: raise TypeError('{0} is not a valid definition.'.format(definition)) # define "delta_" units for units with an offset if getattr(definition.converter, "offset", 0.0) != 0.0: if definition.name.startswith('['): d_name = '[delta_' + definition.name[1:] else: d_name = 'delta_' + definition.name if definition.symbol: d_symbol = 'Δ' + definition.symbol else: d_symbol = None d_aliases = tuple('Δ' + alias for alias in definition.aliases) d_reference = UnitsContainer(dict((ref, value) for ref, value in definition.reference.items())) d_def = UnitDefinition(d_name, d_symbol, d_aliases, ScaleConverter(definition.converter.scale), d_reference, definition.is_base) else: d_def = definition self._define_adder(d_def, d, di) return definition, d, di def _define_adder(self, definition, unit_dict, casei_unit_dict): """Helper function to store a definition in the internal dictionaries. It stores the definition under its name, symbol and aliases. """ self._define_single_adder(definition.name, definition, unit_dict, casei_unit_dict) if definition.has_symbol: self._define_single_adder(definition.symbol, definition, unit_dict, casei_unit_dict) for alias in definition.aliases: if ' ' in alias: logger.warn('Alias cannot contain a space: ' + alias) self._define_single_adder(alias, definition, unit_dict, casei_unit_dict) def _define_single_adder(self, key, value, unit_dict, casei_unit_dict): """Helper function to store a definition in the internal dictionaries. It warns or raise error on redefinition. """ if key in unit_dict: if self._on_redefinition == 'raise': raise RedefinitionError(key, type(value)) elif self._on_redefinition == 'warn': logger.warning("Redefining '%s' (%s)", key, type(value)) unit_dict[key] = value if casei_unit_dict is not None: casei_unit_dict[key.lower()].add(key) def _register_parser(self, prefix, parserfunc): """Register a loader for a given @ directive.. :param prefix: string identifying the section (e.g. @context) :param parserfunc: A function that is able to parse a Definition section. :type parserfunc: SourceIterator -> None """ if self._parsers is None: self._parsers = dict() if prefix and prefix[0] == '@': self._parsers[prefix] = parserfunc else: raise ValueError("Prefix directives must start with '@'") def load_definitions(self, file, is_resource=False): """Add units and prefixes defined in a definition text file. :param file: can be a filename or a line iterable. :param is_resource: used to indicate that the file is a resource file and therefore should be loaded from the package. """ # Permit both filenames and line-iterables if isinstance(file, string_types): try: if is_resource: with closing(pkg_resources.resource_stream(__name__, file)) as fp: rbytes = fp.read() return self.load_definitions(StringIO(rbytes.decode('utf-8')), is_resource) else: with open(file, encoding='utf-8') as fp: return self.load_definitions(fp, is_resource) except (RedefinitionError, DefinitionSyntaxError) as e: if e.filename is None: e.filename = file raise e except Exception as e: msg = getattr(e, 'message', '') or str(e) raise ValueError('While opening {0}\n{1}'.format(file, msg)) ifile = SourceIterator(file) for no, line in ifile: if line and line[0] == '@': if line.startswith('@import'): if is_resource: path = line[7:].strip() else: try: path = os.path.dirname(file.name) except AttributeError: path = os.getcwd() path = os.path.join(path, os.path.normpath(line[7:].strip())) self.load_definitions(path, is_resource) else: parts = _BLOCK_RE.split(line) loader = self._parsers.get(parts[0], None) if self._parsers else None if loader is None: raise DefinitionSyntaxError('Unknown directive %s' % line, lineno=no) try: loader(ifile) except DefinitionSyntaxError as ex: if ex.lineno is None: ex.lineno = no raise ex else: try: self.define(Definition.from_string(line)) except DefinitionSyntaxError as ex: if ex.lineno is None: ex.lineno = no raise ex except Exception as ex: logger.error("In line {0}, cannot add '{1}' {2}".format(no, line, ex)) def _build_cache(self): """Build a cache of dimensionality and base units. """ deps = dict((name, set(definition.reference.keys() if definition.reference else {})) for name, definition in self._units.items()) for unit_names in solve_dependencies(deps): for unit_name in unit_names: prefixed = False for p in self._prefixes.keys(): if p and unit_name.startswith(p): prefixed = True break if '[' in unit_name: continue try: uc = ParserHelper.from_word(unit_name) bu = self._get_root_units(uc) di = self._get_dimensionality(uc) self._root_units_cache[uc] = bu self._dimensionality_cache[uc] = di if not prefixed: if di not in self._dimensional_equivalents: self._dimensional_equivalents[di] = set() self._dimensional_equivalents[di].add(self._units[unit_name]._name) except Exception as e: logger.warning('Could not resolve {0}: {1!r}'.format(unit_name, e)) def _dedup_candidates(self, candidates): """Given a list of unit triplets (prefix, name, suffix), remove those with different names but equal value. e.g. ('kilo', 'gram', '') and ('', 'kilogram', '') """ candidates = tuple(candidates) if len(candidates) < 2: return candidates unique = [candidates[0]] for c in candidates[2:]: for u in unique: if c == u: break else: unique.append(c) return tuple(unique) def get_name(self, name_or_alias, case_sensitive=True): """Return the canonical name of a unit. """ if name_or_alias == 'dimensionless': return '' try: return self._units[name_or_alias]._name except KeyError: pass candidates = self._dedup_candidates(self.parse_unit_name(name_or_alias, case_sensitive)) if not candidates: raise UndefinedUnitError(name_or_alias) elif len(candidates) == 1: prefix, unit_name, _ = candidates[0] else: logger.warning('Parsing {0} yield multiple results. ' 'Options are: {1}'.format(name_or_alias, candidates)) prefix, unit_name, _ = candidates[0] if prefix: name = prefix + unit_name symbol = self.get_symbol(name) prefix_def = self._prefixes[prefix] self._units[name] = UnitDefinition(name, symbol, (), prefix_def.converter, UnitsContainer({unit_name: 1})) return prefix + unit_name return unit_name def get_symbol(self, name_or_alias): """Return the preferred alias for a unit """ candidates = self._dedup_candidates(self.parse_unit_name(name_or_alias)) if not candidates: raise UndefinedUnitError(name_or_alias) elif len(candidates) == 1: prefix, unit_name, _ = candidates[0] else: logger.warning('Parsing {0} yield multiple results. ' 'Options are: {1!r}'.format(name_or_alias, candidates)) prefix, unit_name, _ = candidates[0] return self._prefixes[prefix].symbol + self._units[unit_name].symbol def _get_symbol(self, name): return self._units[name].symbol def get_dimensionality(self, input_units): """Convert unit or dict of units or dimensions to a dict of base dimensions dimensions :param input_units: :return: dimensionality """ input_units = to_units_container(input_units) return self._get_dimensionality(input_units) def _get_dimensionality(self, input_units): """ Convert a UnitsContainer to base dimensions. :param input_units: :return: dimensionality """ if not input_units: return UnitsContainer() if input_units in self._dimensionality_cache: return self._dimensionality_cache[input_units] accumulator = defaultdict(float) self._get_dimensionality_recurse(input_units, 1.0, accumulator) if '[]' in accumulator: del accumulator['[]'] dims = UnitsContainer(dict((k, v) for k, v in accumulator.items() if v != 0.0)) self._dimensionality_cache[input_units] = dims return dims def _get_dimensionality_recurse(self, ref, exp, accumulator): for key in ref: exp2 = exp*ref[key] if _is_dim(key): reg = self._dimensions[key] if reg.is_base: accumulator[key] += exp2 elif reg.reference is not None: self._get_dimensionality_recurse(reg.reference, exp2, accumulator) else: reg = self._units[self.get_name(key)] if reg.reference is not None: self._get_dimensionality_recurse(reg.reference, exp2, accumulator) def get_root_units(self, input_units, check_nonmult=True): """Convert unit or dict of units to the root units. If any unit is non multiplicative and check_converter is True, then None is returned as the multiplicative factor. :param input_units: units :type input_units: UnitsContainer or str :param check_nonmult: if True, None will be returned as the multiplicative factor if a non-multiplicative units is found in the final Units. :return: multiplicative factor, base units """ input_units = to_units_container(input_units) f, units = self._get_root_units(input_units, check_nonmult) return f, self.Unit(units) def _get_root_units(self, input_units, check_nonmult=True): """Convert unit or dict of units to the root units. If any unit is non multiplicative and check_converter is True, then None is returned as the multiplicative factor. :param input_units: units :type input_units: UnitsContainer or dict :param check_nonmult: if True, None will be returned as the multiplicative factor if a non-multiplicative units is found in the final Units. :return: multiplicative factor, base units """ if not input_units: return 1., UnitsContainer() # The cache is only done for check_nonmult=True if check_nonmult and input_units in self._root_units_cache: return self._root_units_cache[input_units] accumulators = [1., defaultdict(float)] self._get_root_units_recurse(input_units, 1.0, accumulators) factor = accumulators[0] units = UnitsContainer(dict((k, v) for k, v in accumulators[1].items() if v != 0.)) # Check if any of the final units is non multiplicative and return None instead. if check_nonmult: for unit in units.keys(): if not self._units[unit].converter.is_multiplicative: return None, units if check_nonmult: self._root_units_cache[input_units] = factor, units return factor, units def get_base_units(self, input_units, check_nonmult=True, system=None): """Convert unit or dict of units to the base units. If any unit is non multiplicative and check_converter is True, then None is returned as the multiplicative factor. :param input_units: units :type input_units: UnitsContainer or str :param check_nonmult: if True, None will be returned as the multiplicative factor if a non-multiplicative units is found in the final Units. :return: multiplicative factor, base units """ return self.get_root_units(input_units, check_nonmult) def _get_root_units_recurse(self, ref, exp, accumulators): for key in sorted(ref): exp2 = exp*ref[key] key = self.get_name(key) reg = self._units[key] if reg.is_base: accumulators[1][key] += exp2 else: accumulators[0] *= reg._converter.scale ** exp2 if reg.reference is not None: self._get_root_units_recurse(reg.reference, exp2, accumulators) def get_compatible_units(self, input_units, group_or_system=None): """ """ input_units = to_units_container(input_units) equiv = self._get_compatible_units(input_units, group_or_system) return frozenset(self.Unit(eq) for eq in equiv) def _get_compatible_units(self, input_units, group_or_system): """ """ if not input_units: return frozenset() src_dim = self._get_dimensionality(input_units) ret = self._dimensional_equivalents[src_dim] return ret def convert(self, value, src, dst, inplace=False): """Convert value from some source to destination units. :param value: value :param src: source units. :type src: Quantity or str :param dst: destination units. :type dst: Quantity or str :return: converted value """ src = to_units_container(src, self) dst = to_units_container(dst, self) if src == dst: return value return self._convert(value, src, dst, inplace) def _convert(self, value, src, dst, inplace=False, check_dimensionality=True): """Convert value from some source to destination units. :param value: value :param src: source units. :type src: UnitsContainer :param dst: destination units. :type dst: UnitsContainer :return: converted value """ if check_dimensionality: src_dim = self._get_dimensionality(src) dst_dim = self._get_dimensionality(dst) # If the source and destination dimensionality are different, # then the conversion cannot be performed. if src_dim != dst_dim: raise DimensionalityError(src, dst, src_dim, dst_dim) # Here src and dst have only multiplicative units left. Thus we can # convert with a factor. factor, units = self._get_root_units(src / dst) # factor is type float and if our magnitude is type Decimal then # must first convert to Decimal before we can '*' the values if isinstance(value, Decimal): factor = Decimal(str(factor)) elif isinstance(value, Fraction): factor = Fraction(str(factor)) if inplace: value *= factor else: value = value * factor return value def parse_unit_name(self, unit_name, case_sensitive=True): """Parse a unit to identify prefix, unit name and suffix by walking the list of prefix and suffix. :rtype: (str, str, str) """ stw = unit_name.startswith edw = unit_name.endswith for suffix, prefix in itertools.product(self._suffixes, self._prefixes): if stw(prefix) and edw(suffix): name = unit_name[len(prefix):] if suffix: name = name[:-len(suffix)] if len(name) == 1: continue if case_sensitive: if name in self._units: yield (self._prefixes[prefix].name, self._units[name].name, self._suffixes[suffix]) else: for real_name in self._units_casei.get(name.lower(), ()): yield (self._prefixes[prefix].name, self._units[real_name].name, self._suffixes[suffix]) def parse_units(self, input_string, as_delta=None): """Parse a units expression and returns a UnitContainer with the canonical names. The expression can only contain products, ratios and powers of units. :param as_delta: if the expression has multiple units, the parser will interpret non multiplicative units as their `delta_` counterparts. :raises: :class:`pint.UndefinedUnitError` if a unit is not in the registry :class:`ValueError` if the expression is invalid. """ units = self._parse_units(input_string, as_delta) return self.Unit(units) def _parse_units(self, input_string, as_delta=None): """ """ if as_delta is None: as_delta = True if as_delta and input_string in self._parse_unit_cache: return self._parse_unit_cache[input_string] if not input_string: return UnitsContainer() # Sanitize input_string with whitespaces. input_string = input_string.strip() units = ParserHelper.from_string(input_string) if units.scale != 1: raise ValueError('Unit expression cannot have a scaling factor.') ret = {} many = len(units) > 1 for name in units: cname = self.get_name(name) value = units[name] if not cname: continue if as_delta and (many or (not many and value != 1)): definition = self._units[cname] if not definition.is_multiplicative: cname = 'delta_' + cname ret[cname] = value ret = UnitsContainer(ret) if as_delta: self._parse_unit_cache[input_string] = ret return ret def _eval_token(self, token, case_sensitive=True, **values): 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': return 1 * self.dimensionless elif token_text in values: return self.Quantity(values[token_text]) else: return self.Quantity(1, UnitsContainer({self.get_name(token_text, case_sensitive=case_sensitive) : 1})) elif token_type == NUMBER: return ParserHelper.eval_token(token) else: raise Exception('unknown token type') def parse_expression(self, input_string, case_sensitive=True, **values): """Parse a mathematical expression including units and return a quantity object. Numerical constants can be specified as keyword arguments and will take precedence over the names defined in the registry. """ if not input_string: return self.Quantity(1) input_string = string_preprocessor(input_string) gen = tokenizer(input_string) return build_eval_tree(gen).evaluate(lambda x: self._eval_token(x, case_sensitive=case_sensitive, **values)) __call__ = parse_expression class NonMultiplicativeRegistry(BaseRegistry): """Handle of non multiplicative units (e.g. Temperature). Capabilities: - Register non-multiplicative units and their relations. - Convert between non-multiplicative units. :param default_as_delta: If True, non-multiplicative units are interpreted as their *delta* counterparts in multiplications. :param autoconvert_offset_to_baseunit: If True, non-multiplicative units are converted to base units in multiplications. """ def __init__(self, default_as_delta=True, autoconvert_offset_to_baseunit=False, **kwargs): super(NonMultiplicativeRegistry, self).__init__(**kwargs) #: When performing a multiplication of units, interpret #: non-multiplicative units as their *delta* counterparts. self.default_as_delta = default_as_delta # Determines if quantities with offset units are converted to their # base units on multiplication and division. self.autoconvert_offset_to_baseunit = autoconvert_offset_to_baseunit def _parse_units(self, input_string, as_delta=None): """ """ if as_delta is None: as_delta = self.default_as_delta return super(NonMultiplicativeRegistry, self)._parse_units(input_string, as_delta) def _define(self, definition): """Add unit to the registry. In addition to what is done by the BaseRegistry, registers also non-multiplicative units. :param definition: a dimension, unit or prefix definition. :type definition: str | Definition :return: Definition instance, case sensitive unit dict, case insensitive unit dict. :rtype: Definition, dict, dict """ definition, d, di = super(NonMultiplicativeRegistry, self)._define(definition) # define additional units for units with an offset if getattr(definition.converter, "offset", 0.0) != 0.0: self._define_adder(definition, d, di) return definition, d, di def _is_multiplicative(self, u): if u in self._units: return self._units[u].is_multiplicative # If the unit is not in the registry might be because it is not # registered with its prefixed version. # TODO: Might be better to register them. l = self._dedup_candidates(self.parse_unit_name(u)) try: u = l[0][1] return self._units[u].is_multiplicative except KeyError: raise UndefinedUnitError(u) def _convert(self, value, src, dst, inplace=False): """Convert value from some source to destination units. In addition to what is done by the BaseRegistry, converts between non-multiplicative units. :param value: value :param src: source units. :type src: UnitsContainer :param dst: destination units. :type dst: UnitsContainer :return: converted value """ # Conversion needs to consider if non-multiplicative (AKA offset # units) are involved. Conversion is only possible if src and dst # have at most one offset unit per dimension. src_offset_units = [(u, e) for u, e in src.items() if not self._is_multiplicative(u)] dst_offset_units = [(u, e) for u, e in dst.items() if not self._is_multiplicative(u)] if not (src_offset_units or dst_offset_units): return super(NonMultiplicativeRegistry, self)._convert(value, src, dst, inplace) src_dim = self._get_dimensionality(src) dst_dim = self._get_dimensionality(dst) # If the source and destination dimensionality are different, # then the conversion cannot be performed. if src_dim != dst_dim: raise DimensionalityError(src, dst, src_dim, dst_dim) # For offset units we need to check if the conversion is allowed. if src_offset_units or dst_offset_units: # Validate that not more than one offset unit is present if len(src_offset_units) > 1 or len(dst_offset_units) > 1: raise DimensionalityError( src, dst, src_dim, dst_dim, extra_msg=' - more than one offset unit.') # validate that offset unit is not used in multiplicative context if ((len(src_offset_units) == 1 and len(src) > 1) or (len(dst_offset_units) == 1 and len(dst) > 1) and not self.autoconvert_offset_to_baseunit): raise DimensionalityError( src, dst, src_dim, dst_dim, extra_msg=' - offset unit used in multiplicative context.') # Validate that order of offset unit is exactly one. if src_offset_units: if src_offset_units[0][1] != 1: raise DimensionalityError( src, dst, src_dim, dst_dim, extra_msg=' - offset units in higher order.') else: if dst_offset_units[0][1] != 1: raise DimensionalityError( src, dst, src_dim, dst_dim, extra_msg=' - offset units in higher order.') # Here we convert only the offset quantities. Any remaining scaled # quantities will be converted later. # TODO: Shouldn't this (until factor, units) be inside the If above? # clean src from offset units by converting to reference for u, e in src_offset_units: value = self._units[u].converter.to_reference(value, inplace) src = src.remove([u for u, e in src_offset_units]) # clean dst units from offset units dst = dst.remove([u for u, e in dst_offset_units]) value = super(NonMultiplicativeRegistry, self)._convert(value, src, dst, inplace, False) # Finally convert to offset units specified in destination for u, e in dst_offset_units: value = self._units[u].converter.from_reference(value, inplace) return value class ContextRegistry(BaseRegistry): """Handle of Contexts. Conversion between units with different dimenstions according to previously established relations (contexts). (e.g. in the spectroscopy, conversion between frequency and energy is possible) Capabilities: - Register contexts. - Enable and disable contexts. - Parse @context directive. """ def __init__(self, **kwargs): super(ContextRegistry, self).__init__(**kwargs) #: Map context name (string) or abbreviation to context. self._contexts = {} #: Stores active contexts. self._active_ctx = ContextChain() def _register_parsers(self): super(ContextRegistry, self)._register_parsers() self._register_parser('@context', self._parse_context) def _parse_context(self, ifile): try: self.add_context(Context.from_lines(ifile.block_iter(), self.get_dimensionality)) except KeyError as e: raise DefinitionSyntaxError('unknown dimension {0} in context'.format(str(e))) def add_context(self, context): """Add a context object to the registry. The context will be accessible by its name and aliases. Notice that this method will NOT enable the context. Use `enable_contexts`. """ if context.name in self._contexts: logger.warning('The name %s was already registered for another context.', context.name) self._contexts[context.name] = context for alias in context.aliases: if alias in self._contexts: logger.warning('The name %s was already registered for another context', context.name) self._contexts[alias] = context def remove_context(self, name_or_alias): """Remove a context from the registry and return it. Notice that this methods will not disable the context. Use `disable_contexts`. """ context = self._contexts[name_or_alias] del self._contexts[context.name] for alias in context.aliases: del self._contexts[alias] return context def enable_contexts(self, *names_or_contexts, **kwargs): """Enable contexts provided by name or by object. :param names_or_contexts: sequence of the contexts or contexts names/alias :param kwargs: keyword arguments for the context """ # If present, copy the defaults from the containing contexts if self._active_ctx.defaults: kwargs = dict(self._active_ctx.defaults, **kwargs) # For each name, we first find the corresponding context ctxs = tuple((self._contexts[name] if isinstance(name, string_types) else name) for name in names_or_contexts) # Check if the contexts have been checked first, if not we make sure # that dimensions are expressed in terms of base dimensions. for ctx in ctxs: if getattr(ctx, '_checked', False): continue for (src, dst), func in ctx.funcs.items(): src_ = self._get_dimensionality(src) dst_ = self._get_dimensionality(dst) if src != src_ or dst != dst_: ctx.remove_transformation(src, dst) ctx.add_transformation(src_, dst_, func) ctx._checked = True # and create a new one with the new defaults. ctxs = tuple(Context.from_context(ctx, **kwargs) for ctx in ctxs) # Finally we add them to the active context. self._active_ctx.insert_contexts(*ctxs) def disable_contexts(self, n=None): """Disable the last n enabled contexts. """ if n is None: n = len(self._contexts) self._active_ctx.remove_contexts(n) @contextmanager def context(self, *names, **kwargs): """Used as a context manager, this function enables to activate a context which is removed after usage. :param names: name of the context. :param kwargs: keyword arguments for the contexts. Context are called by their name:: >>> with ureg.context('one'): ... pass If the context has an argument, you can specify its value as a keyword argument:: >>> with ureg.context('one', n=1): ... pass Multiple contexts can be entered in single call: >>> with ureg.context('one', 'two', n=1): ... pass or nested allowing you to give different values to the same keyword argument:: >>> with ureg.context('one', n=1): ... with ureg.context('two', n=2): ... pass A nested context inherits the defaults from the containing context:: >>> with ureg.context('one', n=1): ... with ureg.context('two'): # Here n takes the value of the upper context ... pass """ # Enable the contexts. self.enable_contexts(*names, **kwargs) try: # After adding the context and rebuilding the graph, the registry # is ready to use. yield self finally: # Upon leaving the with statement, # the added contexts are removed from the active one. self.disable_contexts(len(names)) def with_context(self, name, **kw): """Decorator to wrap a function call in a Pint context. Use it to ensure that a certain context is active when calling a function:: >>> @ureg.with_context('sp') ... def my_cool_fun(wavelenght): ... print('This wavelength is equivalent to: %s', wavelength.to('terahertz')) :param names: name of the context. :param kwargs: keyword arguments for the contexts. :return: the wrapped function. """ def decorator(func): assigned = tuple(attr for attr in functools.WRAPPER_ASSIGNMENTS if hasattr(func, attr)) updated = tuple(attr for attr in functools.WRAPPER_UPDATES if hasattr(func, attr)) @functools.wraps(func, assigned=assigned, updated=updated) def wrapper(*values, **kwargs): with self.context(name, **kw): return func(*values, **kwargs) return wrapper return decorator def _convert(self, value, src, dst, inplace=False): """Convert value from some source to destination units. In addition to what is done by the BaseRegistry, converts between units with different dimensions by following transformation rules defined in the context. :param value: value :param src: source units. :type src: UnitsContainer :param dst: destination units. :type dst: UnitsContainer :return: converted value """ # If there is an active context, we look for a path connecting source and # destination dimensionality. If it exists, we transform the source value # by applying sequentially each transformation of the path. if self._active_ctx: src_dim = self._get_dimensionality(src) dst_dim = self._get_dimensionality(dst) path = find_shortest_path(self._active_ctx.graph, src_dim, dst_dim) if path: src = self.Quantity(value, src) for a, b in zip(path[:-1], path[1:]): src = self._active_ctx.transform(a, b, self, src) value, src = src._magnitude, src._units return super(ContextRegistry, self)._convert(value, src, dst, inplace) def _get_compatible_units(self, input_units, group_or_system): """ """ src_dim = self._get_dimensionality(input_units) ret = super(ContextRegistry, self)._get_compatible_units(input_units, group_or_system) if self._active_ctx: nodes = find_connected_nodes(self._active_ctx.graph, src_dim) ret = set() if nodes: for node in nodes: ret |= self._dimensional_equivalents[node] return ret class SystemRegistry(BaseRegistry): """Handle of Systems and Groups. Conversion between units with different dimenstions according to previously established relations (contexts). (e.g. in the spectroscopy, conversion between frequency and energy is possible) Capabilities: - Register systems and groups. - List systems - Get or get the default system. - Parse @system and @group directive. """ def __init__(self, system=None, **kwargs): super(SystemRegistry, self).__init__(**kwargs) #: Map system name to system. #: :type: dict[ str | System] self._systems = {} #: Maps dimensionality (UnitsContainer) to Dimensionality (UnitsContainer) self._base_units_cache = dict() #: 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 _after_init(self): super(SystemRegistry, self)._after_init() #: Copy units in root group to the default group if 'group' in self._defaults: grp = self.get_group(self._defaults['group'], True) grp.add_units(*self.get_group('root', False).non_inherited_unit_names) #: System name to be used by default. self._default_system = self._default_system or self._defaults.get('system', None) def _register_parsers(self): super(SystemRegistry, self)._register_parsers() self._register_parser('@group', self._parse_group) self._register_parser('@system', self._parse_system) def _parse_group(self, ifile): self.Group.from_lines(ifile.block_iter(), self.define) def _parse_system(self, ifile): self.System.from_lines(ifile.block_iter(), self.get_root_units) def get_group(self, name, create_if_needed=True): """Return a Group. :param name: Name of the group to be :param create_if_needed: Create a group if not Found. If False, raise an Exception. :return: Group """ if name in self._groups: return self._groups[name] if not create_if_needed: raise ValueError('Unkown group %s' % name) return self.Group(name) @property def sys(self): return systems.Lister(self._systems) @property def default_system(self): return self._default_system @default_system.setter def default_system(self, name): if name: if name not in self._systems: raise ValueError('Unknown system %s' % name) self._base_units_cache = {} self._default_system = name def get_system(self, name, create_if_needed=True): """Return a Group. :param name: Name of the group to be :param create_if_needed: Create a group if not Found. If False, raise an Exception. :return: System """ if name in self._systems: return self._systems[name] if not create_if_needed: raise ValueError('Unkown system %s' % name) return self.System(name) def _define(self, definition): # In addition to the what is done by the BaseRegistry, # this adds all units to the `root` group. definition, d, di = super(SystemRegistry, self)._define(definition) if isinstance(definition, UnitDefinition): # We add all units to the root group self.get_group('root').add_units(definition.name) return definition, d, di def get_base_units(self, input_units, check_nonmult=True, system=None): """Convert unit or dict of units to the base units. If any unit is non multiplicative and check_converter is True, then None is returned as the multiplicative factor. Unlike BaseRegistry, in this registry root_units might be different from base_units :param input_units: units :type input_units: UnitsContainer or str :param check_nonmult: if True, None will be returned as the multiplicative factor if a non-multiplicative units is found in the final Units. :return: multiplicative factor, base units """ input_units = to_units_container(input_units) f, units = self._get_base_units(input_units, check_nonmult, system) return f, self.Unit(units) def _get_base_units(self, input_units, check_nonmult=True, system=None): if system is None: system = self._default_system # The cache is only done for check_nonmult=True and the current system. if check_nonmult and system == self._default_system and input_units in self._base_units_cache: return self._base_units_cache[input_units] factor, units = self.get_root_units(input_units, check_nonmult) if not system: return factor, units # This will not be necessary after integration with the registry # as it has a UnitsContainer intermediate units = to_units_container(units, self) destination_units = UnitsContainer() bu = self.get_system(system, False).base_units for unit, value in units.items(): if unit in bu: new_unit = bu[unit] new_unit = to_units_container(new_unit, self) destination_units *= new_unit ** value else: destination_units *= UnitsContainer({unit: value}) base_factor = self.convert(factor, units, destination_units) if check_nonmult: self._base_units_cache[input_units] = base_factor, destination_units return base_factor, destination_units def _get_compatible_units(self, input_units, group_or_system): """ """ if group_or_system is None: group_or_system = self._default_system ret = super(SystemRegistry, self)._get_compatible_units(input_units, group_or_system) if group_or_system: if group_or_system in self._systems: members = self._systems[group_or_system].members elif group_or_system in self._groups: members = self._groups[group_or_system].members else: raise ValueError("Unknown Group o System with name '%s'" % group_or_system) return frozenset(ret.intersection(members)) return ret class UnitRegistry(SystemRegistry, ContextRegistry, NonMultiplicativeRegistry): """The unit registry stores the definitions and relationships between units. :param filename: path of the units definition file to load. Empty to load the default definition file. None to leave the UnitRegistry empty. :param force_ndarray: convert any input, scalar or not to a numpy.ndarray. :param default_as_delta: In the context of a multiplication of units, interpret non-multiplicative units as their *delta* counterparts. :param autoconvert_offset_to_baseunit: If True converts offset units in quantites are converted to their base units in multiplicative context. If False no conversion happens. :param on_redefinition: action to take in case a unit is redefined. 'warn', 'raise', 'ignore' :type on_redefinition: str :param auto_reduce_dimensions: If True, reduce dimensionality on appropriate operations. """ def __init__(self, filename='', force_ndarray=False, default_as_delta=True, autoconvert_offset_to_baseunit=False, on_redefinition='warn', system=None, auto_reduce_dimensions=False): super(UnitRegistry, self).__init__(filename=filename, force_ndarray=force_ndarray, on_redefinition=on_redefinition, default_as_delta=default_as_delta, autoconvert_offset_to_baseunit=autoconvert_offset_to_baseunit, system=system, auto_reduce_dimensions=auto_reduce_dimensions) def pi_theorem(self, quantities): """Builds dimensionless quantities using the Buckingham π theorem :param quantities: mapping between variable name and units :type quantities: dict :return: a list of dimensionless quantities expressed as dicts """ return pi_theorem(quantities, self) wraps = registry_helpers.wraps check = registry_helpers.check class LazyRegistry(object): def __init__(self, args=None, kwargs=None): self.__dict__['params'] = args or (), kwargs or {} def __init(self): args, kwargs = self.__dict__['params'] kwargs['on_redefinition'] = 'raise' self.__class__ = UnitRegistry self.__init__(*args, **kwargs) self._after_init() def __getattr__(self, item): if item == '_on_redefinition': return 'raise' self.__init() return getattr(self, item) def __setattr__(self, key, value): if key == '__class__': super(LazyRegistry, self).__setattr__(key, value) else: self.__init() setattr(self, key, value) def __getitem__(self, item): self.__init() return self[item] def __call__(self, *args, **kwargs): self.__init() return self(*args, **kwargs) pint-0.8.1/pint/registry_helpers.py000066400000000000000000000176431311537545300174320ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.registry_helpers ~~~~~~~~~~~~~~~~~~~~~ Miscellaneous methods of the registry writen as separate functions. :copyright: 2016 by Pint Authors, see AUTHORS for more details.. :license: BSD, see LICENSE for more details. """ import functools from .compat import string_types, zip_longest from .errors import DimensionalityError from .util import to_units_container, UnitsContainer def _replace_units(original_units, values_by_name): """Convert a unit compatible type to a UnitsContainer. :param original_units: a UnitsContainer instance. :param values_by_name: a map between original names and the new values. """ q = 1 for arg_name, exponent in original_units.items(): q = q * values_by_name[arg_name] ** exponent return getattr(q, "_units", UnitsContainer({})) def _to_units_container(a, registry=None): """Convert a unit compatible type to a UnitsContainer, checking if it is string field prefixed with an equal (which is considered a reference) Return a tuple with the unit container and a boolean indicating if it was a reference. """ if isinstance(a, string_types) and '=' in a: return to_units_container(a.split('=', 1)[1]), True return to_units_container(a, registry), False def _parse_wrap_args(args, registry=None): # Arguments which contain definitions # (i.e. names that appear alone and for the first time) defs_args = set() defs_args_ndx = set() # Arguments which depend on others dependent_args_ndx = set() # Arguments which have units. unit_args_ndx = set() # _to_units_container args_as_uc = [_to_units_container(arg, registry) for arg in args] # Check for references in args, remove None values for ndx, (arg, is_ref) in enumerate(args_as_uc): if arg is None: continue elif is_ref: if len(arg) == 1: [(key, value)] = arg.items() if value == 1 and key not in defs_args: # This is the first time that # a variable is used => it is a definition. defs_args.add(key) defs_args_ndx.add(ndx) args_as_uc[ndx] = (key, True) else: # The variable was already found elsewhere, # we consider it a dependent variable. dependent_args_ndx.add(ndx) else: dependent_args_ndx.add(ndx) else: unit_args_ndx.add(ndx) # Check that all valid dependent variables for ndx in dependent_args_ndx: arg, is_ref = args_as_uc[ndx] if not isinstance(arg, dict): continue if not set(arg.keys()) <= defs_args: raise ValueError('Found a missing token while wrapping a function: ' 'Not all variable referenced in %s are defined using !' % args[ndx]) def _converter(ureg, values, strict): new_values = list(value for value in values) values_by_name = {} # first pass: Grab named values for ndx in defs_args_ndx: value = values[ndx] values_by_name[args_as_uc[ndx][0]] = value new_values[ndx] = getattr(value, "_magnitude", value) # second pass: calculate derived values based on named values for ndx in dependent_args_ndx: value = values[ndx] assert _replace_units(args_as_uc[ndx][0], values_by_name) is not None new_values[ndx] = ureg._convert(getattr(value, "_magnitude", value), getattr(value, "_units", UnitsContainer({})), _replace_units(args_as_uc[ndx][0], values_by_name)) # third pass: convert other arguments for ndx in unit_args_ndx: if isinstance(values[ndx], ureg.Quantity): new_values[ndx] = ureg._convert(values[ndx]._magnitude, values[ndx]._units, args_as_uc[ndx][0]) else: if strict: raise ValueError('A wrapped function using strict=True requires ' 'quantity for all arguments with not None units. ' '(error found for {0}, {1})'.format(args_as_uc[ndx][0], new_values[ndx])) return new_values, values_by_name return _converter def wraps(ureg, ret, args, strict=True): """Wraps a function to become pint-aware. Use it when a function requires a numerical value but in some specific units. The wrapper function will take a pint quantity, convert to the units specified in `args` and then call the wrapped function with the resulting magnitude. The value returned by the wrapped function will be converted to the units specified in `ret`. Use None to skip argument conversion. Set strict to False, to accept also numerical values. :param ureg: a UnitRegistry instance. :param ret: output units. :param args: iterable of input units. :param strict: boolean to indicate that only quantities are accepted. :return: the wrapped function. :raises: :class:`ValueError` if strict and one of the arguments is not a Quantity. """ if not isinstance(args, (list, tuple)): args = (args, ) converter = _parse_wrap_args(args) if isinstance(ret, (list, tuple)): container, ret = True, ret.__class__([_to_units_container(arg, ureg) for arg in ret]) else: container, ret = False, _to_units_container(ret, ureg) def decorator(func): assigned = tuple(attr for attr in functools.WRAPPER_ASSIGNMENTS if hasattr(func, attr)) updated = tuple(attr for attr in functools.WRAPPER_UPDATES if hasattr(func, attr)) @functools.wraps(func, assigned=assigned, updated=updated) def wrapper(*values, **kw): # In principle, the values are used as is # When then extract the magnitudes when needed. new_values, values_by_name = converter(ureg, values, strict) result = func(*new_values, **kw) if container: out_units = (_replace_units(r, values_by_name) if is_ref else r for (r, is_ref) in ret) return ret.__class__(res if unit is None else ureg.Quantity(res, unit) for unit, res in zip_longest(out_units, result)) if ret[0] is None: return result return ureg.Quantity(result, _replace_units(ret[0], values_by_name) if ret[1] else ret[0]) return wrapper return decorator def check(ureg, *args): """Decorator to for quantity type checking for function inputs. Use it to ensure that the decorated function input parameters match the expected type of pint quantity. Use None to skip argument checking. :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 """ dimensions = [ureg.get_dimensionality(dim) for dim in args] def decorator(func): assigned = tuple(attr for attr in functools.WRAPPER_ASSIGNMENTS if hasattr(func, attr)) updated = tuple(attr for attr in functools.WRAPPER_UPDATES if hasattr(func, attr)) @functools.wraps(func, assigned=assigned, updated=updated) def wrapper(*values, **kwargs): for dim, value in zip_longest(dimensions, values): if dim and value.dimensionality != dim: raise DimensionalityError(value, 'a quantity of', value.dimensionality, dim) return func(*values, **kwargs) return wrapper return decorator pint-0.8.1/pint/systems.py000066400000000000000000000325731311537545300155460ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.systems ~~~~~~~~~~~~ Functions and classes related to system definitions and conversions. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import division, unicode_literals, print_function, absolute_import import re from .definitions import Definition, UnitDefinition from .errors import DefinitionSyntaxError, RedefinitionError from .util import to_units_container, SharedRegistryObject, SourceIterator, logger from .babel_names import _babel_systems from pint.compat import Loc class _Group(SharedRegistryObject): """A group is a set of units. Units can be added directly or by including other groups. Members are computed dynamically, that is if a unit is added to a group X all groups that include X are affected. The group belongs to one Registry. It can be specified in the definition file as: @group [using , ..., ] ... @end """ #: Regex to match the header parts of a definition. _header_re = re.compile('@group\s+(?P\w+)\s*(using\s(?P.*))*') def __init__(self, name): """ :param name: Name of the group. If not given, a root Group will be created. :type name: str :param groups: dictionary like object groups and system. The newly created group will be added after creation. :type groups: dict[str | Group] """ # The name of the group. #: type: str self.name = name #: Names of the units in this group. #: :type: set[str] self._unit_names = set() #: Names of the groups in this group. #: :type: set[str] self._used_groups = set() #: Names of the groups in which this group is contained. #: :type: set[str] self._used_by = set() # Add this group to the group dictionary self._REGISTRY._groups[self.name] = self if name != 'root': # All groups are added to root group self._REGISTRY._groups['root'].add_groups(name) #: A cache of the included units. #: None indicates that the cache has been invalidated. #: :type: frozenset[str] | None self._computed_members = None @property def members(self): """Names of the units that are members of the group. Calculated to include to all units in all included _used_groups. :rtype: frozenset[str] """ if self._computed_members is None: self._computed_members = set(self._unit_names) for _, group in self.iter_used_groups(): self._computed_members |= group.members self._computed_members = frozenset(self._computed_members) return self._computed_members def invalidate_members(self): """Invalidate computed members in this Group and all parent nodes. """ self._computed_members = None d = self._REGISTRY._groups for name in self._used_by: d[name].invalidate_members() def iter_used_groups(self): pending = set(self._used_groups) d = self._REGISTRY._groups while pending: name = pending.pop() group = d[name] pending |= group._used_groups yield name, d[name] def is_used_group(self, group_name): for name, _ in self.iter_used_groups(): if name == group_name: return True return False def add_units(self, *unit_names): """Add units to group. :type unit_names: str """ for unit_name in unit_names: self._unit_names.add(unit_name) self.invalidate_members() @property def non_inherited_unit_names(self): return frozenset(self._unit_names) def remove_units(self, *unit_names): """Remove units from group. :type unit_names: str """ for unit_name in unit_names: self._unit_names.remove(unit_name) self.invalidate_members() def add_groups(self, *group_names): """Add groups to group. :type group_names: str """ d = self._REGISTRY._groups for group_name in group_names: grp = d[group_name] if grp.is_used_group(self.name): raise ValueError('Cyclic relationship found between %s and %s' % (self.name, group_name)) self._used_groups.add(group_name) grp._used_by.add(self.name) self.invalidate_members() def remove_groups(self, *group_names): """Remove groups from group. :type group_names: str """ d = self._REGISTRY._groups for group_name in group_names: grp = d[group_name] self._used_groups.remove(group_name) grp._used_by.remove(self.name) self.invalidate_members() @classmethod def from_lines(cls, lines, define_func): """Return a Group object parsing an iterable of lines. :param lines: iterable :type lines: list[str] :param define_func: Function to define a unit in the registry. :type define_func: str -> None """ lines = SourceIterator(lines) lineno, header = next(lines) r = cls._header_re.search(header) if r is None: raise ValueError("Invalid Group header syntax: '%s'" % header) name = r.groupdict()['name'].strip() groups = r.groupdict()['used_groups'] if groups: group_names = tuple(a.strip() for a in groups.split(',')) else: group_names = () unit_names = [] for lineno, line in lines: if '=' in line: # Is a definition definition = Definition.from_string(line) if not isinstance(definition, UnitDefinition): raise DefinitionSyntaxError('Only UnitDefinition are valid inside _used_groups, ' 'not %s' % type(definition), lineno=lineno) try: define_func(definition) except (RedefinitionError, DefinitionSyntaxError) as ex: if ex.lineno is None: ex.lineno = lineno raise ex unit_names.append(definition.name) else: unit_names.append(line.strip()) grp = cls(name) grp.add_units(*unit_names) if group_names: grp.add_groups(*group_names) return grp def __getattr__(self, item): return self._REGISTRY 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 all groups that include X are affected. The System belongs to one Registry. It can be specified in the definition file as: @system [using , ..., ] ... @end The syntax for the rule is: new_unit_name : old_unit_name where: - old_unit_name: a root unit part which is going to be removed from the system. - new_unit_name: a non root unit which is going to replace the old_unit. If the new_unit_name and the old_unit_name, the later and the colon can be ommited. """ #: Regex to match the header parts of a context. _header_re = re.compile('@system\s+(?P\w+)\s*(using\s(?P.*))*') def __init__(self, name): """ :param name: Name of the group :type name: str """ #: Name of the system #: :type: str self.name = name #: Maps root unit names to a dict indicating the new unit and its exponent. #: :type: dict[str, dict[str, number]]] self.base_units = {} #: Derived unit names. #: :type: set(str) self.derived_units = set() #: Names of the _used_groups in used by this system. #: :type: set(str) self._used_groups = set() #: :type: frozenset | None self._computed_members = None # Add this system to the system dictionary self._REGISTRY._systems[self.name] = self def __dir__(self): return list(self.members) def __getattr__(self, item): u = getattr(self._REGISTRY, self.name + '_' + item, None) if u is not None: return u return getattr(self._REGISTRY, item) @property def members(self): d = self._REGISTRY._groups if self._computed_members is None: self._computed_members = set() for group_name in self._used_groups: try: self._computed_members |= d[group_name].members except KeyError: logger.warning('Could not resolve {0} in System {1}'.format(group_name, self.name)) self._computed_members = frozenset(self._computed_members) return self._computed_members def invalidate_members(self): """Invalidate computed members in this Group and all parent nodes. """ self._computed_members = None def add_groups(self, *group_names): """Add groups to group. :type group_names: str """ self._used_groups |= set(group_names) self.invalidate_members() def remove_groups(self, *group_names): """Remove groups from group. :type group_names: str """ self._used_groups -= set(group_names) self.invalidate_members() def format_babel(self, locale): """translate the name of the system :type locale: Locale """ if locale and self.name in _babel_systems: name = _babel_systems[self.name] locale = Loc.parse(locale) return locale.measurement_systems[name] return self.name @classmethod def from_lines(cls, lines, get_root_func): lines = SourceIterator(lines) lineno, header = next(lines) r = cls._header_re.search(header) if r is None: raise ValueError("Invalid System header syntax '%s'" % header) name = r.groupdict()['name'].strip() groups = r.groupdict()['used_groups'] # If the systems has no group, it automatically uses the root group. if groups: group_names = tuple(a.strip() for a in groups.split(',')) else: group_names = ('root', ) base_unit_names = {} derived_unit_names = [] for lineno, line in lines: line = line.strip() # We would identify a # - old_unit: a root unit part which is going to be removed from the system. # - new_unit: a non root unit which is going to replace the old_unit. if ':' in line: # The syntax is new_unit:old_unit new_unit, old_unit = line.split(':') new_unit, old_unit = new_unit.strip(), old_unit.strip() # The old unit MUST be a root unit, if not raise an error. if old_unit != str(get_root_func(old_unit)[1]): raise ValueError('In `%s`, the unit at the right of the `:` must be a root unit.' % line) # Here we find new_unit expanded in terms of root_units new_unit_expanded = to_units_container(get_root_func(new_unit)[1]) # We require that the old unit is present in the new_unit expanded if old_unit not in new_unit_expanded: raise ValueError('Old unit must be a component of new unit') # Here we invert the equation, in other words # we write old units in terms new unit and expansion new_unit_dict = dict((new_unit, -1./value) for new_unit, value in new_unit_expanded.items() if new_unit != old_unit) new_unit_dict[new_unit] = 1 / new_unit_expanded[old_unit] base_unit_names[old_unit] = new_unit_dict else: # The syntax is new_unit # old_unit is inferred as the root unit with the same dimensionality. new_unit = line old_unit_dict = to_units_container(get_root_func(line)[1]) if len(old_unit_dict) != 1: raise ValueError('The new base must be a root dimension if not discarded unit is specified.') old_unit, value = dict(old_unit_dict).popitem() base_unit_names[old_unit] = {new_unit: 1./value} system = cls(name) system.add_groups(*group_names) system.base_units.update(**base_unit_names) system.derived_units |= set(derived_unit_names) return system class Lister(object): def __init__(self, d): self.d = d def __dir__(self): return list(self.d.keys()) def __getattr__(self, item): return self.d[item] def build_group_class(registry): class Group(_Group): pass Group._REGISTRY = registry return Group def build_system_class(registry): class System(_System): pass System._REGISTRY = registry return System pint-0.8.1/pint/testsuite/000077500000000000000000000000001311537545300155045ustar00rootroot00000000000000pint-0.8.1/pint/testsuite/__init__.py000066400000000000000000000135061311537545300176220ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import doctest import logging import os import sys from contextlib import contextmanager from pint.compat import ndarray, np from pint import logger, UnitRegistry from pint.quantity import _Quantity from pint.testsuite.compat import unittest from pint.testsuite.helpers import PintOutputChecker from logging.handlers import BufferingHandler class TestHandler(BufferingHandler): def __init__(self, only_warnings=False): # BufferingHandler takes a "capacity" argument # so as to know when to flush. As we're overriding # shouldFlush anyway, we can set a capacity of zero. # You can call flush() manually to clear out the # buffer. self.only_warnings = only_warnings BufferingHandler.__init__(self, 0) def shouldFlush(self): return False def emit(self, record): if self.only_warnings and record.level != logging.WARNING: return self.buffer.append(record.__dict__) class BaseTestCase(unittest.TestCase): CHECK_NO_WARNING = True @contextmanager def capture_log(self, level=logging.DEBUG): th = TestHandler() th.setLevel(level) logger.addHandler(th) if self._test_handler is not None: l = len(self._test_handler.buffer) yield th.buffer if self._test_handler is not None: self._test_handler.buffer = self._test_handler.buffer[:l] def setUp(self): self._test_handler = None if self.CHECK_NO_WARNING: self._test_handler = th = TestHandler() th.setLevel(logging.WARNING) logger.addHandler(th) def tearDown(self): if self._test_handler is not None: buf = self._test_handler.buffer l = len(buf) msg = '\n'.join(record.get('msg', str(record)) for record in buf) self.assertEqual(l, 0, msg='%d warnings raised.\n%s' % (l, msg)) class QuantityTestCase(BaseTestCase): FORCE_NDARRAY = False @classmethod def setUpClass(cls): cls.ureg = UnitRegistry(force_ndarray=cls.FORCE_NDARRAY) cls.Q_ = cls.ureg.Quantity cls.U_ = cls.ureg.Unit def _get_comparable_magnitudes(self, first, second, msg): 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): self.assertTrue(first.dimensionless, msg=msg + ' The first is not dimensionless.') first = first.to('') m1, m2 = first.magnitude, second elif isinstance(second, _Quantity): self.assertTrue(second.dimensionless, msg=msg + ' The second is not dimensionless.') second = second.to('') m1, m2 = first, second.magnitude else: m1, m2 = first, second return m1, m2 def assertQuantityEqual(self, first, second, msg=None): if msg is None: msg = 'Comparing %r and %r. ' % (first, second) m1, m2 = self._get_comparable_magnitudes(first, second, msg) if isinstance(m1, ndarray) or isinstance(m2, ndarray): np.testing.assert_array_equal(m1, m2, err_msg=msg) else: self.assertEqual(m1, m2, msg) def assertQuantityAlmostEqual(self, first, second, rtol=1e-07, atol=0, msg=None): if msg is None: msg = 'Comparing %r and %r. ' % (first, second) m1, m2 = self._get_comparable_magnitudes(first, second, msg) if isinstance(m1, ndarray) or isinstance(m2, ndarray): np.testing.assert_allclose(m1, m2, rtol=rtol, atol=atol, err_msg=msg) else: self.assertLessEqual(abs(m1 - m2), atol + rtol * abs(m2), msg=msg) def testsuite(): """A testsuite that has all the pint tests. """ suite = unittest.TestLoader().discover(os.path.dirname(__file__)) from pint.compat import HAS_NUMPY, HAS_UNCERTAINTIES # TESTING THE DOCUMENTATION requires pyyaml, serialize, numpy and uncertainties if HAS_NUMPY and HAS_UNCERTAINTIES: try: import yaml, serialize add_docs(suite) except ImportError: pass return suite def main(): """Runs the testsuite as command line application. """ try: unittest.main() except Exception as e: print('Error: %s' % e) def run(): """Run all tests. :return: a :class:`unittest.TestResult` object """ test_runner = unittest.TextTestRunner() return test_runner.run(testsuite()) import math _GLOBS = { 'wrapping.rst': { 'pendulum_period': lambda length: 2*math.pi*math.sqrt(length/9.806650), 'pendulum_period2': lambda length, swing_amplitude: 1., 'pendulum_period_maxspeed': lambda length, swing_amplitude: (1., 2.), 'pendulum_period_error': lambda length: (1., False), } } def add_docs(suite): """Add docs to suite :type suite: unittest.TestSuite """ docpath = os.path.join(os.path.dirname(__file__), '..', '..', 'docs') docpath = os.path.abspath(docpath) if os.path.exists(docpath): checker = PintOutputChecker() for name in (name for name in os.listdir(docpath) if name.endswith('.rst')): file = os.path.join(docpath, name) suite.addTest(doctest.DocFileSuite(file, module_relative=False, checker=checker, globs=_GLOBS.get(name, None))) def test_docs(): suite = unittest.TestSuite() add_docs(suite) runner = unittest.TextTestRunner() return runner.run(suite) pint-0.8.1/pint/testsuite/compat/000077500000000000000000000000001311537545300167675ustar00rootroot00000000000000pint-0.8.1/pint/testsuite/compat/__init__.py000066400000000000000000000003441311537545300211010ustar00rootroot00000000000000import sys if sys.version_info < (2, 7): try: import unittest2 as unittest except ImportError: raise Exception("Testing Pint in Python 2.6 requires package 'unittest2'") else: import unittestpint-0.8.1/pint/testsuite/helpers.py000066400000000000000000000063101311537545300175200ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import doctest from distutils.version import StrictVersion import re from pint.compat import HAS_NUMPY, HAS_PROPER_BABEL, HAS_UNCERTAINTIES, NUMPY_VER, PYTHON3 from pint.testsuite.compat import unittest def requires_numpy18(): if not HAS_NUMPY: return unittest.skip('Requires NumPy') return unittest.skipUnless(StrictVersion(NUMPY_VER) >= StrictVersion('1.8'), 'Requires NumPy >= 1.8') def requires_numpy_previous_than(version): if not HAS_NUMPY: return unittest.skip('Requires NumPy') return unittest.skipUnless(StrictVersion(NUMPY_VER) < StrictVersion(version), 'Requires NumPy < %s' % version) def requires_numpy(): return unittest.skipUnless(HAS_NUMPY, 'Requires NumPy') def requires_not_numpy(): return unittest.skipIf(HAS_NUMPY, 'Requires NumPy is not installed.') def requires_proper_babel(): return unittest.skipUnless(HAS_PROPER_BABEL, 'Requires Babel with units support') def requires_uncertainties(): return unittest.skipUnless(HAS_UNCERTAINTIES, 'Requires Uncertainties') def requires_not_uncertainties(): return unittest.skipIf(HAS_UNCERTAINTIES, 'Requires Uncertainties is not installed.') def requires_python2(): return unittest.skipIf(PYTHON3, 'Requires Python 2.X.') def requires_python3(): return unittest.skipUnless(PYTHON3, 'Requires Python 3.X.') _number_re = '([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)' _q_re = re.compile('%s)' % _number_re + '\s*,\s*' + "'(?P.*)'" + '\s*' + '\)>') _sq_re = re.compile('\s*' + '(?P%s)' % _number_re + '\s' + "(?P.*)") _unit_re = re.compile('') class PintOutputChecker(doctest.OutputChecker): def check_output(self, want, got, optionflags): check = super(PintOutputChecker, self).check_output(want, got, optionflags) if check: return check try: if eval(want) == eval(got): return True except: pass for regex in (_q_re, _sq_re): try: parsed_got = regex.match(got.replace(r'\\', '')).groupdict() parsed_want = regex.match(want.replace(r'\\', '')).groupdict() v1 = float(parsed_got['magnitude']) v2 = float(parsed_want['magnitude']) if abs(v1 - v2) > abs(v1) / 1000: return False if parsed_got['unit'] != parsed_want['unit']: return False return True except: pass cnt = 0 for regex in (_unit_re, ): try: parsed_got, tmp = regex.subn('\1', got) cnt += tmp parsed_want, temp = regex.subn('\1', want) cnt += tmp if parsed_got == parsed_want: return True except: pass if cnt: # If there was any replacement, we try again the previous methods. return self.check_output(parsed_want, parsed_got, optionflags) return False pint-0.8.1/pint/testsuite/parameterized.py000066400000000000000000000117021311537545300207130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Adds Parameterized tests for Python's unittest module # # Code from: parameterizedtestcase, version: 0.1.0 # Homepage: https://github.com/msabramo/python_unittest_parameterized_test_case # Author: Marc Abramowitz, email: marc@marc-abramowitz.com # License: MIT # # Fixed for to work in Python 2 & 3 with "add_metaclass" decorator from six # https://pypi.python.org/pypi/six # Author: Benjamin Peterson # License: MIT # # Use like this: # # from parameterizedtestcase import ParameterizedTestCase # # class MyTests(ParameterizedTestCase): # @ParameterizedTestCase.parameterize( # ("input", "expected_output"), # [ # ("2+4", 6), # ("3+5", 8), # ("6*9", 54), # ] # ) # def test_eval(self, input, expected_output): # self.assertEqual(eval(input), expected_output) from pint.testsuite.compat import unittest from functools import wraps import collections def add_metaclass(metaclass): """Class decorator for creating a class with a metaclass.""" def wrapper(cls): orig_vars = cls.__dict__.copy() orig_vars.pop('__dict__', None) orig_vars.pop('__weakref__', None) slots = orig_vars.get('__slots__') if slots is not None: if isinstance(slots, str): slots = [slots] for slots_var in slots: orig_vars.pop(slots_var) return metaclass(cls.__name__, cls.__bases__, orig_vars) return wrapper def augment_method_docstring(method, new_class_dict, classname, param_names, param_values, new_method): param_assignments_str = '; '.join( ['%s = %s' % (k, v) for (k, v) in zip(param_names, param_values)]) extra_doc = "%s (%s.%s) [with %s] " % ( method.__name__, new_class_dict.get('__module__', ''), classname, param_assignments_str) try: new_method.__doc__ = extra_doc + new_method.__doc__ except TypeError: # Catches when new_method.__doc__ is None new_method.__doc__ = extra_doc class ParameterizedTestCaseMetaClass(type): method_counter = {} def __new__(meta, classname, bases, class_dict): 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'): # print("Processing attr_name = %r; attr_value = %r" % ( # attr_name, attr_value)) method = attr_value param_names = attr_value.param_names data = attr_value.data func_name_format = attr_value.func_name_format meta.process_method( classname, method, param_names, data, new_class_dict, func_name_format) else: new_class_dict[attr_name] = attr_value return type.__new__(meta, classname, bases, new_class_dict) @classmethod def process_method( cls, classname, method, param_names, data, new_class_dict, func_name_format): method_counter = cls.method_counter for param_values in data: new_method = cls.new_method(method, param_values) method_counter[method.__name__] = \ method_counter.get(method.__name__, 0) + 1 case_data = dict(list(zip(param_names, param_values))) case_data['func_name'] = method.__name__ case_data['case_num'] = method_counter[method.__name__] new_method.__name__ = func_name_format.format(**case_data) augment_method_docstring( method, new_class_dict, classname, param_names, param_values, new_method) new_class_dict[new_method.__name__] = new_method @classmethod def new_method(cls, method, param_values): @wraps(method) def new_method(self): return method(self, *param_values) return new_method @add_metaclass(ParameterizedTestCaseMetaClass) class ParameterizedTestMixin(object): @classmethod def parameterize(cls, param_names, data, func_name_format='{func_name}_{case_num:05d}'): """Decorator for parameterizing a test method - example: @ParameterizedTestCase.parameterize( ("isbn", "expected_title"), [ ("0262033844", "Introduction to Algorithms"), ("0321558146", "Campbell Essential Biology")]) """ def decorator(func): @wraps(func) def newfunc(*arg, **kwargs): return func(*arg, **kwargs) newfunc.param_names = param_names newfunc.data = data newfunc.func_name_format = func_name_format return newfunc return decorator @add_metaclass(ParameterizedTestCaseMetaClass) class ParameterizedTestCase(unittest.TestCase, ParameterizedTestMixin): pass pint-0.8.1/pint/testsuite/test_babel.py000066400000000000000000000023011311537545300201560ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import from pint.testsuite import helpers, BaseTestCase from pint import UnitRegistry import os class TestBabel(BaseTestCase): @helpers.requires_proper_babel() def test_babel(self): ureg = UnitRegistry() dirname = os.path.dirname(__file__) ureg.load_definitions(os.path.join(dirname, '../xtranslated.txt')) distance = 24.0 * ureg.meter self.assertEqual( distance.format_babel(locale='fr_FR', length='long'), "24.0 mètres" ) time = 8.0 * ureg.second self.assertEqual( time.format_babel(locale='fr_FR', length='long'), "8.0 secondes" ) self.assertEqual( time.format_babel(locale='ro', length='short'), "8.0 s" ) acceleration = distance / time ** 2 self.assertEqual( acceleration.format_babel(locale='fr_FR', length='long'), "0.375 mètre par seconde²" ) mks = ureg.get_system('mks') self.assertEqual( mks.format_babel(locale='fr_FR'), "métrique" ) pint-0.8.1/pint/testsuite/test_contexts.py000066400000000000000000000513351311537545300207730ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import itertools from collections import defaultdict from pint import UnitRegistry, errors from pint.context import Context from pint.util import UnitsContainer from pint.testsuite import QuantityTestCase def add_ctxs(ureg): a, b = UnitsContainer({'[length]': 1}), UnitsContainer({'[time]': -1}) d = Context('lc') d.add_transformation(a, b, lambda ureg, x: ureg.speed_of_light / x) d.add_transformation(b, a, lambda ureg, x: ureg.speed_of_light / x) ureg.add_context(d) a, b = UnitsContainer({'[length]': 1}), UnitsContainer({'[current]': -1}) d = Context('ab') d.add_transformation(a, b, lambda ureg, x: 1 / x) d.add_transformation(b, a, lambda ureg, x: 1 / x) ureg.add_context(d) def add_arg_ctxs(ureg): a, b = UnitsContainer({'[length]': 1}), UnitsContainer({'[time]': -1}) d = Context('lc') d.add_transformation(a, b, lambda ureg, x, n: ureg.speed_of_light / x / n) d.add_transformation(b, a, lambda ureg, x, n: ureg.speed_of_light / x / n) ureg.add_context(d) a, b = UnitsContainer({'[length]': 1}), UnitsContainer({'[current]': -1}) d = Context('ab') d.add_transformation(a, b, lambda ureg, x: 1 / x) d.add_transformation(b, a, lambda ureg, x: 1 / x) ureg.add_context(d) def add_argdef_ctxs(ureg): a, b = UnitsContainer({'[length]': 1}), UnitsContainer({'[time]': -1}) d = Context('lc', defaults=dict(n=1)) assert d.defaults == dict(n=1) d.add_transformation(a, b, lambda ureg, x, n: ureg.speed_of_light / x / n) d.add_transformation(b, a, lambda ureg, x, n: ureg.speed_of_light / x / n) ureg.add_context(d) a, b = UnitsContainer({'[length]': 1}), UnitsContainer({'[current]': -1}) d = Context('ab') d.add_transformation(a, b, lambda ureg, x: 1 / x) d.add_transformation(b, a, lambda ureg, x: 1 / x) ureg.add_context(d) def add_sharedargdef_ctxs(ureg): a, b = UnitsContainer({'[length]': 1}), UnitsContainer({'[time]': -1}) d = Context('lc', defaults=dict(n=1)) assert d.defaults == dict(n=1) d.add_transformation(a, b, lambda ureg, x, n: ureg.speed_of_light / x / n) d.add_transformation(b, a, lambda ureg, x, n: ureg.speed_of_light / x / n) ureg.add_context(d) a, b = UnitsContainer({'[length]': 1}), UnitsContainer({'[current]': 1}) d = Context('ab', defaults=dict(n=0)) d.add_transformation(a, b, lambda ureg, x, n: ureg.ampere * ureg.meter * n / x) d.add_transformation(b, a, lambda ureg, x, n: ureg.ampere * ureg.meter * n / x) ureg.add_context(d) class TestContexts(QuantityTestCase): def test_known_context(self): ureg = UnitRegistry() add_ctxs(ureg) with ureg.context('lc'): self.assertTrue(ureg._active_ctx) self.assertTrue(ureg._active_ctx.graph) self.assertFalse(ureg._active_ctx) self.assertFalse(ureg._active_ctx.graph) with ureg.context('lc', n=1): self.assertTrue(ureg._active_ctx) self.assertTrue(ureg._active_ctx.graph) self.assertFalse(ureg._active_ctx) self.assertFalse(ureg._active_ctx.graph) def test_known_context_enable(self): ureg = UnitRegistry() add_ctxs(ureg) ureg.enable_contexts('lc') self.assertTrue(ureg._active_ctx) self.assertTrue(ureg._active_ctx.graph) ureg.disable_contexts(1) self.assertFalse(ureg._active_ctx) self.assertFalse(ureg._active_ctx.graph) ureg.enable_contexts('lc', n=1) self.assertTrue(ureg._active_ctx) self.assertTrue(ureg._active_ctx.graph) ureg.disable_contexts(1) self.assertFalse(ureg._active_ctx) self.assertFalse(ureg._active_ctx.graph) def test_graph(self): ureg = UnitRegistry() add_ctxs(ureg) l = UnitsContainer({'[length]': 1.}) t = UnitsContainer({'[time]': -1.}) c = UnitsContainer({'[current]': -1.}) g_sp = defaultdict(set) g_sp.update({l: set((t,)), t: set((l,))}) g_ab = defaultdict(set) g_ab.update({l: set((c,)), c: set((l,))}) g = defaultdict(set) g.update({l: set((t, c)), t: set((l,)), c: set((l,))}) with ureg.context('lc'): self.assertEqual(ureg._active_ctx.graph, g_sp) with ureg.context('lc', n=1): self.assertEqual(ureg._active_ctx.graph, g_sp) with ureg.context('ab'): self.assertEqual(ureg._active_ctx.graph, g_ab) with ureg.context('lc'): with ureg.context('ab'): self.assertEqual(ureg._active_ctx.graph, g) with ureg.context('ab'): with ureg.context('lc'): self.assertEqual(ureg._active_ctx.graph, g) with ureg.context('lc', 'ab'): self.assertEqual(ureg._active_ctx.graph, g) with ureg.context('ab', 'lc'): self.assertEqual(ureg._active_ctx.graph, g) def test_graph_enable(self): ureg = UnitRegistry() add_ctxs(ureg) l = UnitsContainer({'[length]': 1.}) t = UnitsContainer({'[time]': -1.}) c = UnitsContainer({'[current]': -1.}) g_sp = defaultdict(set) g_sp.update({l: set((t,)), t: set((l,))}) g_ab = defaultdict(set) g_ab.update({l: set((c,)), c: set((l,))}) g = defaultdict(set) g.update({l: set((t, c)), t: set((l,)), c: set((l,))}) ureg.enable_contexts('lc') self.assertEqual(ureg._active_ctx.graph, g_sp) ureg.disable_contexts(1) ureg.enable_contexts('lc', n=1) self.assertEqual(ureg._active_ctx.graph, g_sp) ureg.disable_contexts(1) ureg.enable_contexts('ab') self.assertEqual(ureg._active_ctx.graph, g_ab) ureg.disable_contexts(1) ureg.enable_contexts('lc') ureg.enable_contexts('ab') self.assertEqual(ureg._active_ctx.graph, g) ureg.disable_contexts(2) ureg.enable_contexts('ab') ureg.enable_contexts('lc') self.assertEqual(ureg._active_ctx.graph, g) ureg.disable_contexts(2) ureg.enable_contexts('lc', 'ab') self.assertEqual(ureg._active_ctx.graph, g) ureg.disable_contexts(2) ureg.enable_contexts('ab', 'lc') self.assertEqual(ureg._active_ctx.graph, g) ureg.disable_contexts(2) def test_known_nested_context(self): ureg = UnitRegistry() add_ctxs(ureg) with ureg.context('lc'): x = dict(ureg._active_ctx) y = dict(ureg._active_ctx.graph) self.assertTrue(ureg._active_ctx) self.assertTrue(ureg._active_ctx.graph) with ureg.context('ab'): self.assertTrue(ureg._active_ctx) self.assertTrue(ureg._active_ctx.graph) self.assertNotEqual(x, ureg._active_ctx) self.assertNotEqual(y, ureg._active_ctx.graph) self.assertEqual(x, ureg._active_ctx) self.assertEqual(y, ureg._active_ctx.graph) self.assertFalse(ureg._active_ctx) self.assertFalse(ureg._active_ctx.graph) def test_unknown_context(self): ureg = UnitRegistry() add_ctxs(ureg) try: with ureg.context('la'): pass except KeyError as e: value = True except Exception as e: value = False self.assertTrue(value) self.assertFalse(ureg._active_ctx) self.assertFalse(ureg._active_ctx.graph) def test_unknown_nested_context(self): ureg = UnitRegistry() add_ctxs(ureg) with ureg.context('lc'): x = dict(ureg._active_ctx) y = dict(ureg._active_ctx.graph) try: with ureg.context('la'): pass except KeyError as e: value = True except Exception as e: value = False self.assertTrue(value) self.assertEqual(x, ureg._active_ctx) self.assertEqual(y, ureg._active_ctx.graph) self.assertFalse(ureg._active_ctx) self.assertFalse(ureg._active_ctx.graph) def test_one_context(self): ureg = UnitRegistry() add_ctxs(ureg) q = 500 * ureg.meter s = (ureg.speed_of_light / q).to('Hz') self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc'): self.assertEqual(q.to('Hz'), s) self.assertRaises(ValueError, q.to, 'Hz') def test_multiple_context(self): ureg = UnitRegistry() add_ctxs(ureg) q = 500 * ureg.meter s = (ureg.speed_of_light / q).to('Hz') self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc', 'ab'): self.assertEqual(q.to('Hz'), s) self.assertRaises(ValueError, q.to, 'Hz') def test_nested_context(self): ureg = UnitRegistry() add_ctxs(ureg) q = 500 * ureg.meter s = (ureg.speed_of_light / q).to('Hz') self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc'): self.assertEqual(q.to('Hz'), s) with ureg.context('ab'): self.assertEqual(q.to('Hz'), s) self.assertEqual(q.to('Hz'), s) with ureg.context('ab'): self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc'): self.assertEqual(q.to('Hz'), s) self.assertRaises(ValueError, q.to, 'Hz') def test_context_with_arg(self): ureg = UnitRegistry() add_arg_ctxs(ureg) q = 500 * ureg.meter s = (ureg.speed_of_light / q).to('Hz') self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc', n=1): self.assertEqual(q.to('Hz'), s) with ureg.context('ab'): self.assertEqual(q.to('Hz'), s) self.assertEqual(q.to('Hz'), s) with ureg.context('ab'): self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc', n=1): self.assertEqual(q.to('Hz'), s) self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc'): self.assertRaises(TypeError, q.to, 'Hz') def test_enable_context_with_arg(self): ureg = UnitRegistry() add_arg_ctxs(ureg) q = 500 * ureg.meter s = (ureg.speed_of_light / q).to('Hz') self.assertRaises(ValueError, q.to, 'Hz') ureg.enable_contexts('lc', n=1) self.assertEqual(q.to('Hz'), s) ureg.enable_contexts('ab') self.assertEqual(q.to('Hz'), s) self.assertEqual(q.to('Hz'), s) ureg.disable_contexts(1) ureg.disable_contexts(1) ureg.enable_contexts('ab') self.assertRaises(ValueError, q.to, 'Hz') ureg.enable_contexts('lc', n=1) self.assertEqual(q.to('Hz'), s) ureg.disable_contexts(1) self.assertRaises(ValueError, q.to, 'Hz') ureg.disable_contexts(1) ureg.enable_contexts('lc') self.assertRaises(TypeError, q.to, 'Hz') ureg.disable_contexts(1) def test_context_with_arg_def(self): ureg = UnitRegistry() add_argdef_ctxs(ureg) q = 500 * ureg.meter s = (ureg.speed_of_light / q).to('Hz') self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc'): self.assertEqual(q.to('Hz'), s) with ureg.context('ab'): self.assertEqual(q.to('Hz'), s) self.assertEqual(q.to('Hz'), s) with ureg.context('ab'): self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc'): self.assertEqual(q.to('Hz'), s) self.assertRaises(ValueError, q.to, 'Hz') self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc', n=2): self.assertEqual(q.to('Hz'), s / 2) with ureg.context('ab'): self.assertEqual(q.to('Hz'), s / 2) self.assertEqual(q.to('Hz'), s / 2) with ureg.context('ab'): self.assertRaises(ValueError, q.to, 'Hz') with ureg.context('lc', n=2): self.assertEqual(q.to('Hz'), s / 2) self.assertRaises(ValueError, q.to, 'Hz') def test_context_with_sharedarg_def(self): ureg = UnitRegistry() add_sharedargdef_ctxs(ureg) q = 500 * ureg.meter s = (ureg.speed_of_light / q).to('Hz') u = (1 / 500) * ureg.ampere with ureg.context('lc'): self.assertEqual(q.to('Hz'), s) with ureg.context('ab'): self.assertEqual(q.to('ampere'), u) with ureg.context('ab'): self.assertEqual(q.to('ampere'), 0 * u) with ureg.context('lc'): self.assertRaises(ZeroDivisionError, ureg.Quantity.to, q, 'Hz') with ureg.context('lc', n=2): self.assertEqual(q.to('Hz'), s / 2) with ureg.context('ab'): self.assertEqual(q.to('ampere'), 2 * u) with ureg.context('ab', n=3): self.assertEqual(q.to('ampere'), 3 * u) with ureg.context('lc'): self.assertEqual(q.to('Hz'), s / 3) with ureg.context('lc', n=2): self.assertEqual(q.to('Hz'), s / 2) with ureg.context('ab', n=4): self.assertEqual(q.to('ampere'), 4 * u) with ureg.context('ab', n=3): self.assertEqual(q.to('ampere'), 3 * u) with ureg.context('lc', n=6): self.assertEqual(q.to('Hz'), s / 6) def _test_ctx(self, ctx): ureg = UnitRegistry() q = 500 * ureg.meter s = (ureg.speed_of_light / q).to('Hz') nctx = len(ureg._contexts) self.assertNotIn(ctx.name, ureg._contexts) ureg.add_context(ctx) self.assertIn(ctx.name, ureg._contexts) self.assertEqual(len(ureg._contexts), nctx + 1 + len(ctx.aliases)) with ureg.context(ctx.name): self.assertEqual(q.to('Hz'), s) self.assertEqual(s.to('meter'), q) ureg.remove_context(ctx.name) self.assertNotIn(ctx.name, ureg._contexts) self.assertEqual(len(ureg._contexts), nctx) def test_parse_invalid(self): s = ['@context longcontextname', '[length] = 1 / [time]: c / value', '1 / [time] = [length]: c / value'] self.assertRaises(ValueError, Context.from_lines, s) def test_parse_simple(self): a = Context.__keytransform__(UnitsContainer({'[time]': -1}), UnitsContainer({'[length]': 1})) b = Context.__keytransform__(UnitsContainer({'[length]': 1}), UnitsContainer({'[time]': -1})) s = ['@context longcontextname', '[length] -> 1 / [time]: c / value', '1 / [time] -> [length]: c / value'] c = Context.from_lines(s) self.assertEqual(c.name, 'longcontextname') self.assertEqual(c.aliases, ()) self.assertEqual(c.defaults, {}) self.assertEqual(set(c.funcs.keys()), set((a, b))) self._test_ctx(c) s = ['@context longcontextname = lc', '[length] <-> 1 / [time]: c / value'] c = Context.from_lines(s) self.assertEqual(c.name, 'longcontextname') self.assertEqual(c.aliases, ('lc', )) self.assertEqual(c.defaults, {}) self.assertEqual(set(c.funcs.keys()), set((a, b))) self._test_ctx(c) s = ['@context longcontextname = lc = lcn', '[length] <-> 1 / [time]: c / value'] c = Context.from_lines(s) self.assertEqual(c.name, 'longcontextname') self.assertEqual(c.aliases, ('lc', 'lcn', )) self.assertEqual(c.defaults, {}) self.assertEqual(set(c.funcs.keys()), set((a, b))) self._test_ctx(c) def test_parse_auto_inverse(self): a = Context.__keytransform__(UnitsContainer({'[time]': -1.}), UnitsContainer({'[length]': 1.})) b = Context.__keytransform__(UnitsContainer({'[length]': 1.}), UnitsContainer({'[time]': -1.})) s = ['@context longcontextname', '[length] <-> 1 / [time]: c / value'] c = Context.from_lines(s) self.assertEqual(c.defaults, {}) self.assertEqual(set(c.funcs.keys()), set((a, b))) self._test_ctx(c) def test_parse_define(self): a = Context.__keytransform__(UnitsContainer({'[time]': -1}), UnitsContainer({'[length]': 1.})) b = Context.__keytransform__(UnitsContainer({'[length]': 1}), UnitsContainer({'[time]': -1.})) s = ['@context longcontextname', '[length] <-> 1 / [time]: c / value'] c = Context.from_lines(s) self.assertEqual(c.defaults, {}) self.assertEqual(set(c.funcs.keys()), set((a, b))) self._test_ctx(c) def test_parse_parameterized(self): a = Context.__keytransform__(UnitsContainer({'[time]': -1.}), UnitsContainer({'[length]': 1.})) b = Context.__keytransform__(UnitsContainer({'[length]': 1.}), UnitsContainer({'[time]': -1.})) s = ['@context(n=1) longcontextname', '[length] <-> 1 / [time]: n * c / value'] c = Context.from_lines(s) self.assertEqual(c.defaults, {'n': 1}) self.assertEqual(set(c.funcs.keys()), set((a, b))) self._test_ctx(c) s = ['@context(n=1, bla=2) longcontextname', '[length] <-> 1 / [time]: n * c / value / bla'] c = Context.from_lines(s) self.assertEqual(c.defaults, {'n': 1, 'bla': 2}) self.assertEqual(set(c.funcs.keys()), set((a, b))) # If the variable is not present in the definition, then raise an error s = ['@context(n=1) longcontextname', '[length] <-> 1 / [time]: c / value'] self.assertRaises(ValueError, Context.from_lines, s) def test_warnings(self): ureg = UnitRegistry() with self.capture_log() as buffer: add_ctxs(ureg) d = Context('ab') ureg.add_context(d) self.assertEqual(len(buffer), 1) self.assertIn("ab", str(buffer[-1])) d = Context('ab1', aliases=('ab',)) ureg.add_context(d) self.assertEqual(len(buffer), 2) self.assertIn("ab", str(buffer[-1])) class TestDefinedContexts(QuantityTestCase): FORCE_NDARRAY = False def test_defined(self): ureg = self.ureg with ureg.context('sp'): pass a = Context.__keytransform__(UnitsContainer({'[time]': -1.}), UnitsContainer({'[length]': 1.})) b = Context.__keytransform__(UnitsContainer({'[length]': 1.}), UnitsContainer({'[time]': -1.})) self.assertIn(a, ureg._contexts['sp'].funcs) self.assertIn(b, ureg._contexts['sp'].funcs) with ureg.context('sp'): self.assertIn(a, ureg._active_ctx) self.assertIn(b, ureg._active_ctx) def test_spectroscopy(self): ureg = self.ureg eq = (532. * ureg.nm, 563.5 * ureg.terahertz, 2.33053 * ureg.eV) with ureg.context('sp'): from pint.util import find_shortest_path for a, b in itertools.product(eq, eq): for x in range(2): if x == 1: a = a.to_base_units() b = b.to_base_units() da, db = Context.__keytransform__(a.dimensionality, b.dimensionality) p = find_shortest_path(ureg._active_ctx.graph, da, db) self.assertTrue(p) msg = '{0} <-> {1}'.format(a, b) # 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) def test_decorator(self): ureg = self.ureg a = 532. * ureg.nm with ureg.context('sp'): b = a.to('terahertz') def f(wl): return wl.to('terahertz') self.assertRaises(errors.DimensionalityError, f, a) @ureg.with_context('sp') def g(wl): return wl.to('terahertz') self.assertEqual(b, g(a)) def test_decorator_composition(self): ureg = self.ureg a = 532. * ureg.nm with ureg.context('sp'): b = a.to('terahertz') @ureg.with_context('sp') @ureg.check('[length]') def f(wl): return wl.to('terahertz') @ureg.with_context('sp') @ureg.check('[length]') def g(wl): return wl.to('terahertz') self.assertEqual(b, f(a)) self.assertEqual(b, g(a))pint-0.8.1/pint/testsuite/test_converters.py000066400000000000000000000030541311537545300213110ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import itertools from pint.compat import np from pint.converters import (ScaleConverter, OffsetConverter, Converter) from pint.testsuite import helpers, BaseTestCase class TestConverter(BaseTestCase): def test_converter(self): c = Converter() self.assertTrue(c.is_multiplicative) self.assertTrue(c.to_reference(8)) self.assertTrue(c.from_reference(8)) def test_multiplicative_converter(self): c = ScaleConverter(20.) self.assertEqual(c.from_reference(c.to_reference(100)), 100) self.assertEqual(c.to_reference(c.from_reference(100)), 100) def test_offset_converter(self): c = OffsetConverter(20., 2) self.assertEqual(c.from_reference(c.to_reference(100)), 100) self.assertEqual(c.to_reference(c.from_reference(100)), 100) @helpers.requires_numpy() def test_converter_inplace(self): for c in (ScaleConverter(20.), OffsetConverter(20., 2)): fun1 = lambda x, y: c.from_reference(c.to_reference(x, y), y) fun2 = lambda x, y: c.to_reference(c.from_reference(x, y), y) for fun, (inplace, comp) in itertools.product((fun1, fun2), ((True, self.assertIs), (False, self.assertIsNot))): a = np.ones((1, 10)) ac = np.ones((1, 10)) r = fun(a, inplace) np.testing.assert_allclose(r, ac) comp(a, r) pint-0.8.1/pint/testsuite/test_definitions.py000066400000000000000000000067401311537545300214370ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import from pint.util import (UnitsContainer) from pint.converters import (ScaleConverter, OffsetConverter) from pint.definitions import (Definition, PrefixDefinition, UnitDefinition, DimensionDefinition) from pint.testsuite import BaseTestCase class TestDefinition(BaseTestCase): def test_invalid(self): self.assertRaises(ValueError, Definition.from_string, 'x = [time] * meter') self.assertRaises(ValueError, Definition.from_string, '[x] = [time] * meter') def test_prefix_definition(self): for definition in ('m- = 1e-3', 'm- = 10**-3', 'm- = 0.001'): x = Definition.from_string(definition) self.assertIsInstance(x, PrefixDefinition) self.assertEqual(x.name, 'm') self.assertEqual(x.aliases, ()) self.assertEqual(x.converter.to_reference(1000), 1) self.assertEqual(x.converter.from_reference(0.001), 1) self.assertEqual(str(x), 'm') x = Definition.from_string('kilo- = 1e-3 = k-') self.assertIsInstance(x, PrefixDefinition) self.assertEqual(x.name, 'kilo') self.assertEqual(x.aliases, ()) self.assertEqual(x.symbol, 'k') self.assertEqual(x.converter.to_reference(1000), 1) self.assertEqual(x.converter.from_reference(.001), 1) x = Definition.from_string('kilo- = 1e-3 = k- = anotherk-') self.assertIsInstance(x, PrefixDefinition) self.assertEqual(x.name, 'kilo') self.assertEqual(x.aliases, ('anotherk', )) self.assertEqual(x.symbol, 'k') self.assertEqual(x.converter.to_reference(1000), 1) self.assertEqual(x.converter.from_reference(.001), 1) def test_baseunit_definition(self): x = Definition.from_string('meter = [length]') self.assertIsInstance(x, UnitDefinition) self.assertTrue(x.is_base) self.assertEqual(x.reference, UnitsContainer({'[length]': 1})) def test_unit_definition(self): x = Definition.from_string('coulomb = ampere * second') self.assertIsInstance(x, UnitDefinition) self.assertFalse(x.is_base) self.assertIsInstance(x.converter, ScaleConverter) self.assertEqual(x.converter.scale, 1) self.assertEqual(x.reference, UnitsContainer(ampere=1, second=1)) x = Definition.from_string('faraday = 96485.3399 * coulomb') self.assertIsInstance(x, UnitDefinition) self.assertFalse(x.is_base) self.assertIsInstance(x.converter, ScaleConverter) self.assertEqual(x.converter.scale, 96485.3399) self.assertEqual(x.reference, UnitsContainer(coulomb=1)) x = Definition.from_string('degF = 9 / 5 * kelvin; offset: 255.372222') self.assertIsInstance(x, UnitDefinition) self.assertFalse(x.is_base) self.assertIsInstance(x.converter, OffsetConverter) self.assertEqual(x.converter.scale, 9/5) self.assertEqual(x.converter.offset, 255.372222) self.assertEqual(x.reference, UnitsContainer(kelvin=1)) def test_dimension_definition(self): x = DimensionDefinition('[time]', '', (), converter='') self.assertTrue(x.is_base) self.assertEqual(x.name, '[time]') x = Definition.from_string('[speed] = [length]/[time]') self.assertIsInstance(x, DimensionDefinition) self.assertEqual(x.reference, UnitsContainer({'[length]': 1, '[time]': -1})) pint-0.8.1/pint/testsuite/test_errors.py000066400000000000000000000012751311537545300204360ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import from pint.errors import DimensionalityError, UndefinedUnitError from pint.testsuite import BaseTestCase class TestErrors(BaseTestCase): def test_errors(self): x = ('meter', ) msg = "'meter' is not defined in the unit registry" self.assertEqual(str(UndefinedUnitError(x)), msg) self.assertEqual(str(UndefinedUnitError(list(x))), msg) self.assertEqual(str(UndefinedUnitError(set(x))), msg) msg = "Cannot convert from 'a' (c) to 'b' (d)msg" ex = DimensionalityError('a', 'b', 'c', 'd', 'msg') self.assertEqual(str(ex), msg) pint-0.8.1/pint/testsuite/test_formatter.py000066400000000000000000000036251311537545300211260ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import from pint import formatting as fmt from pint.testsuite import QuantityTestCase class TestFormatter(QuantityTestCase): def test_join(self): for empty in (tuple(), []): self.assertEqual(fmt._join('s', empty), '') self.assertEqual(fmt._join('*', '1 2 3'.split()), '1*2*3') self.assertEqual(fmt._join('{0}*{1}', '1 2 3'.split()), '1*2*3') def test_formatter(self): self.assertEqual(fmt.formatter(dict().items()), '') self.assertEqual(fmt.formatter(dict(meter=1).items()), 'meter') self.assertEqual(fmt.formatter(dict(meter=-1).items()), '1 / meter') self.assertEqual(fmt.formatter(dict(meter=-1).items(), as_ratio=False), 'meter ** -1') self.assertEqual(fmt.formatter(dict(meter=-1, second=-1).items(), as_ratio=False), 'meter ** -1 * second ** -1') self.assertEqual(fmt.formatter(dict(meter=-1, second=-1).items()), '1 / meter / second') self.assertEqual(fmt.formatter(dict(meter=-1, second=-1).items(), single_denominator=True), '1 / (meter * second)') self.assertEqual(fmt.formatter(dict(meter=-1, second=-2).items()), '1 / meter / second ** 2') self.assertEqual(fmt.formatter(dict(meter=-1, second=-2).items(), single_denominator=True), '1 / (meter * second ** 2)') def test_parse_spec(self): self.assertEqual(fmt._parse_spec(''), '') self.assertEqual(fmt._parse_spec(''), '') self.assertRaises(ValueError, fmt._parse_spec, 'W') self.assertRaises(ValueError, fmt._parse_spec, 'PL') def test_format_unit(self): self.assertEqual(fmt.format_unit('', 'C'), 'dimensionless') self.assertRaises(ValueError, fmt.format_unit, 'm', 'W') pint-0.8.1/pint/testsuite/test_infer_base_unit.py000066400000000000000000000022731311537545300222550ustar00rootroot00000000000000from pint import UnitRegistry, set_application_registry 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): from pint.util import infer_base_unit self.assertEqual(infer_base_unit(Q(1, 'millimeter * nanometer')), Q(1, 'meter**2').units) def test_units_adding_to_zero(self): self.assertEqual(infer_base_unit(Q(1, 'm * mm / m / um * s')), Q(1, 's').units) def test_to_compact(self): r = Q(1000000000, 'm') * Q(1, 'mm') / Q(1, 's') / Q(1, 'ms') compact_r = r.to_compact() expected = Q(1000., 'kilometer**2 / second**2') self.assertQuantityAlmostEqual(compact_r, expected) r = (Q(1, 'm') * Q(1, 'mm') / Q(1, 'm') / Q(2, 'um') * Q(2, 's')).to_compact() self.assertQuantityAlmostEqual(r, Q(1000, 's')) def test_volts(self): from pint.util import infer_base_unit 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'))pint-0.8.1/pint/testsuite/test_issues.py000066400000000000000000000467021311537545300204410ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import math import copy from pint import 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.testsuite import QuantityTestCase, helpers from pint.testsuite.compat import unittest class TestIssues(QuantityTestCase): FORCE_NDARRAY = False def setup(self): self.ureg.autoconvert_offset_to_baseunit = False @unittest.expectedFailure def test_issue25(self): x = ParserHelper.from_string('10 %') self.assertEqual(x, ParserHelper(10, {'%': 1})) x = ParserHelper.from_string('10 ‰') self.assertEqual(x, ParserHelper(10, {'‰': 1})) ureg = UnitRegistry() ureg.define('percent = [fraction]; offset: 0 = %') ureg.define('permille = percent / 10 = ‰') x = ureg.parse_expression('10 %') self.assertEqual(x, ureg.Quantity(10, {'%': 1})) y = ureg.parse_expression('10 ‰') self.assertEqual(y, ureg.Quantity(10, {'‰': 1})) self.assertEqual(x.to('‰'), ureg.Quantity(1, {'‰': 1})) def test_issue29(self): ureg = UnitRegistry() ureg.define('molar = mole / liter = M') t = 4 * ureg('mM') self.assertEqual(t.magnitude, 4) self.assertEqual(t._units, UnitsContainer(millimolar=1)) self.assertEqual(t.to('mole / liter'), 4e-3 * ureg('M')) def test_issue52(self): u1 = UnitRegistry() u2 = UnitRegistry() q1 = 1*u1.meter q2 = 1*u2.meter import operator as op for fun in (op.add, op.iadd, op.sub, op.isub, op.mul, op.imul, op.floordiv, op.ifloordiv, op.truediv, op.itruediv): self.assertRaises(ValueError, fun, q1, q2) def test_issue54(self): ureg = UnitRegistry() self.assertEqual((1*ureg.km/ureg.m + 1).magnitude, 1001) def test_issue54_related(self): ureg = UnitRegistry() self.assertEqual(ureg.km/ureg.m, 1000) self.assertEqual(1000, ureg.km/ureg.m) self.assertLess(900, ureg.km/ureg.m) self.assertGreater(1100, ureg.km/ureg.m) def test_issue61(self): ureg = UnitRegistry() Q_ = ureg.Quantity for value in ({}, {'a': 3}, None): self.assertRaises(TypeError, Q_, value) self.assertRaises(TypeError, Q_, value, 'meter') self.assertRaises(ValueError, Q_, '', 'meter') self.assertRaises(ValueError, Q_, '') @helpers.requires_not_numpy() def test_issue61_notNP(self): ureg = UnitRegistry() Q_ = ureg.Quantity for value in ([1, 2, 3], (1, 2, 3)): self.assertRaises(TypeError, Q_, value) self.assertRaises(TypeError, Q_, value, 'meter') def test_issue66(self): ureg = UnitRegistry() self.assertEqual(ureg.get_dimensionality(UnitsContainer({'[temperature]': 1})), UnitsContainer({'[temperature]': 1})) self.assertEqual(ureg.get_dimensionality(ureg.kelvin), UnitsContainer({'[temperature]': 1})) self.assertEqual(ureg.get_dimensionality(ureg.degC), UnitsContainer({'[temperature]': 1})) def test_issue66b(self): ureg = UnitRegistry() self.assertEqual(ureg.get_base_units(ureg.kelvin), (1.0, ureg.Unit(UnitsContainer({'kelvin': 1})))) self.assertEqual(ureg.get_base_units(ureg.degC), (1.0, ureg.Unit(UnitsContainer({'kelvin': 1})))) def test_issue69(self): ureg = UnitRegistry() q = ureg('m').to(ureg('in')) self.assertEqual(q, ureg('m').to('in')) @helpers.requires_uncertainties() def test_issue77(self): ureg = UnitRegistry() acc = (5.0 * ureg('m/s/s')).plus_minus(0.25) tim = (37.0 * ureg('s')).plus_minus(0.16) dis = acc * tim ** 2 / 2 self.assertEqual(dis.value, acc.value * tim.value ** 2 / 2) def test_issue85(self): ureg = UnitRegistry() T = 4. * ureg.kelvin m = 1. * ureg.amu va = 2. * ureg.k * T / m try: va.to_base_units() except: self.assertTrue(False, 'Error while trying to get base units for {}'.format(va)) boltmk = 1.3806488e-23*ureg.J/ureg.K vb = 2. * boltmk * T / m self.assertQuantityAlmostEqual(va.to_base_units(), vb.to_base_units()) def test_issue86(self): ureg = self.ureg ureg.autoconvert_offset_to_baseunit = True def parts(q): return q.magnitude, q.units q1 = 10. * ureg.degC q2 = 10. * ureg.kelvin k1 = q1.to_base_units() q3 = 3. * ureg.meter q1m, q1u = parts(q1) q2m, q2u = parts(q2) q3m, q3u = parts(q3) k1m, k1u = parts(k1) self.assertEqual(parts(q2 * q3), (q2m * q3m, q2u * q3u)) self.assertEqual(parts(q2 / q3), (q2m / q3m, q2u / q3u)) self.assertEqual(parts(q3 * q2), (q3m * q2m, q3u * q2u)) self.assertEqual(parts(q3 / q2), (q3m / q2m, q3u / q2u)) self.assertEqual(parts(q2 ** 1), (q2m ** 1, q2u ** 1)) self.assertEqual(parts(q2 ** -1), (q2m ** -1, q2u ** -1)) self.assertEqual(parts(q2 ** 2), (q2m ** 2, q2u ** 2)) self.assertEqual(parts(q2 ** -2), (q2m ** -2, q2u ** -2)) self.assertEqual(parts(q1 * q3), (k1m * q3m, k1u * q3u)) self.assertEqual(parts(q1 / q3), (k1m / q3m, k1u / q3u)) self.assertEqual(parts(q3 * q1), (q3m * k1m, q3u * k1u)) self.assertEqual(parts(q3 / q1), (q3m / k1m, q3u / k1u)) self.assertEqual(parts(q1 ** -1), (k1m ** -1, k1u ** -1)) self.assertEqual(parts(q1 ** 2), (k1m ** 2, k1u ** 2)) self.assertEqual(parts(q1 ** -2), (k1m ** -2, k1u ** -2)) def test_issues86b(self): ureg = self.ureg T1 = 200. * ureg.degC T2 = T1.to(ureg.kelvin) m = 132.9054519 * ureg.amu v1 = 2 * ureg.k * T1 / m v2 = 2 * ureg.k * T2 / m self.assertQuantityAlmostEqual(v1, v2) self.assertQuantityAlmostEqual(v1, v2.to_base_units()) self.assertQuantityAlmostEqual(v1.to_base_units(), v2) self.assertQuantityAlmostEqual(v1.to_base_units(), v2.to_base_units()) @unittest.expectedFailure def test_issue86c(self): ureg = self.ureg ureg.autoconvert_offset_to_baseunit = True T = ureg.degC T = 100. * T self.assertQuantityAlmostEqual(ureg.k*2*T, ureg.k*(2*T)) 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) 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_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)) 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) def test_issue105(self): ureg = UnitRegistry() func = ureg.parse_unit_name val = list(func('meter')) self.assertEqual(list(func('METER')), []) self.assertEqual(val, list(func('METER', False))) for func in (ureg.get_name, ureg.parse_expression): val = func('meter') self.assertRaises(AttributeError, func, 'METER') self.assertEqual(val, func('METER', False)) def test_issue104(self): ureg = UnitRegistry() 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_issue170(self): Q_ = UnitRegistry().Quantity q = Q_('1 kHz')/Q_('100 Hz') iq = int(q) self.assertEqual(iq, 10) self.assertIsInstance(iq, int) @helpers.requires_python2() def test_issue170b(self): Q_ = UnitRegistry().Quantity q = Q_('1 kHz')/Q_('100 Hz') iq = long(q) self.assertEqual(iq, long(10)) self.assertIsInstance(iq, long) def test_angstrom_creation(self): ureg = UnitRegistry() try: ureg.Quantity(2, 'Å') except SyntaxError: self.fail('Quantity with Å could not be created.') def test_alternative_angstrom_definition(self): ureg = UnitRegistry() try: ureg.Quantity(2, '\u212B') except UndefinedUnitError: self.fail('Quantity with Å could not be created.') def test_micro_creation(self): ureg = UnitRegistry() try: ureg.Quantity(2, 'µm') except SyntaxError: self.fail('Quantity with µ prefix could not be created.') @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)) def test_issue171_real_imag(self): qr = [1., 2., 3., 4.] * self.ureg.meter qi = [4., 3., 2., 1.] * self.ureg.meter q = qr + 1j * qi self.assertQuantityEqual(q.real, qr) self.assertQuantityEqual(q.imag, qi) 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) def test_issue250(self): a = self.ureg.V b = self.ureg.mV self.assertEqual(np.float16(a/b), 1000.) self.assertEqual(np.float32(a/b), 1000.) self.assertEqual(np.float64(a/b), 1000.) if "float128" in dir(np): self.assertEqual(np.float128(a/b), 1000.) def test_issue252(self): ur = UnitRegistry() q = ur("3 F") t = copy.deepcopy(q) u = t.to(ur.mF) self.assertQuantityEqual(q.to(ur.mF), u) def test_issue323(self): from fractions import Fraction as F self.assertEqual((self.Q_(F(2,3), 's')).to('ms'), self.Q_(F(2000,3), 'ms')) self.assertEqual((self.Q_(F(2,3), 'm')).to('km'), self.Q_(F(1,1500), 'km')) def test_issue339(self): q1 = self.ureg('') self.assertEqual(q1.magnitude, 1) self.assertEqual(q1.units, self.ureg.dimensionless) q2 = self.ureg('1 dimensionless') self.assertEqual(q1, q2) def test_issue354_356_370(self): q = 1 * self.ureg.second / self.ureg.millisecond self.assertEqual('{0:~}'.format(1 * self.ureg.second / self.ureg.millisecond), '1.0 s / ms') self.assertEqual("{0:~}".format(1 * self.ureg.count), '1 count') 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() @ureg.wraps(('kg'), 'meter') def f(x): return x x = ureg.Quantity(1., 'meter') y = f(x) z = x * y self.assertEquals(z, ureg.Quantity(1., 'meter * kilogram')) def test_issue483(self): ureg = self.ureg a = np.asarray([1, 2, 3]) q = [1, 2, 3] * ureg.dimensionless p = (q ** q).m np.testing.assert_array_equal(p, a ** a)pint-0.8.1/pint/testsuite/test_measurement.py000066400000000000000000000172351311537545300214520ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import from pint.testsuite import QuantityTestCase, helpers @helpers.requires_not_uncertainties() class TestNotMeasurement(QuantityTestCase): FORCE_NDARRAY = False def test_instantiate(self): M_ = self.ureg.Measurement self.assertRaises(RuntimeError, M_, 4.0, 0.1, 's') @helpers.requires_uncertainties() class TestMeasurement(QuantityTestCase): FORCE_NDARRAY = False def test_simple(self): M_ = self.ureg.Measurement M_(4.0, 0.1, 's') def test_build(self): M_ = self.ureg.Measurement v, u = self.Q_(4.0, 's'), self.Q_(.1, 's') M_(v.magnitude, u.magnitude, 's') ms = (M_(v.magnitude, u.magnitude, 's'), M_(v, u.magnitude), M_(v, u), v.plus_minus(.1), v.plus_minus(0.025, True), v.plus_minus(u),) for m in ms: self.assertEqual(m.value, v) self.assertEqual(m.error, u) self.assertEqual(m.rel, m.error / abs(m.value)) def test_format(self): v, u = self.Q_(4.0, 's ** 2'), self.Q_(.1, 's ** 2') m = self.ureg.Measurement(v, u) self.assertEqual(str(m), '(4.00 +/- 0.10) second ** 2') self.assertEqual(repr(m), '') #self.assertEqual('{:!s}'.format(m), '(4.00 +/- 0.10) second ** 2') #self.assertEqual('{:!r}'.format(m), '') self.assertEqual('{0:P}'.format(m), '(4.00 ± 0.10) second²') self.assertEqual('{0:L}'.format(m), r'\left(4.00 \pm 0.10\right)\ \mathrm{second}^{2}') self.assertEqual('{0:H}'.format(m), '(4.00 ± 0.10) second2') self.assertEqual('{0:C}'.format(m), '(4.00+/-0.10) second**2') self.assertEqual('{0:Lx}'.format(m), r'\SI[separate-uncertainty=true]{4.00(10)}{\second\squared}') self.assertEqual('{0:.1f}'.format(m), '(4.0 +/- 0.1) second ** 2') self.assertEqual('{0:.1fP}'.format(m), '(4.0 ± 0.1) second²') self.assertEqual('{0:.1fL}'.format(m), r'\left(4.0 \pm 0.1\right)\ \mathrm{second}^{2}') self.assertEqual('{0:.1fH}'.format(m), '(4.0 ± 0.1) second2') self.assertEqual('{0:.1fC}'.format(m), '(4.0+/-0.1) second**2') self.assertEqual('{0:.1fLx}'.format(m), '\SI[separate-uncertainty=true]{4.0(1)}{\second\squared}') def test_format_paru(self): v, u = self.Q_(0.20, 's ** 2'), self.Q_(0.01, 's ** 2') m = self.ureg.Measurement(v, u) self.assertEqual('{0:uS}'.format(m), '0.200(10) second ** 2') self.assertEqual('{0:.3uS}'.format(m), '0.2000(100) second ** 2') self.assertEqual('{0:.3uSP}'.format(m), '0.2000(100) second²') self.assertEqual('{0:.3uSL}'.format(m), r'0.2000\left(100\right)\ \mathrm{second}^{2}') self.assertEqual('{0:.3uSH}'.format(m), '0.2000(100) second2') self.assertEqual('{0:.3uSC}'.format(m), '0.2000(100) second**2') def test_format_u(self): v, u = self.Q_(0.20, 's ** 2'), self.Q_(0.01, 's ** 2') m = self.ureg.Measurement(v, u) self.assertEqual('{0:.3u}'.format(m), '(0.2000 +/- 0.0100) second ** 2') self.assertEqual('{0:.3uP}'.format(m), '(0.2000 ± 0.0100) second²') self.assertEqual('{0:.3uL}'.format(m), r'\left(0.2000 \pm 0.0100\right)\ \mathrm{second}^{2}') self.assertEqual('{0:.3uH}'.format(m), '(0.2000 ± 0.0100) second2') self.assertEqual('{0:.3uC}'.format(m), '(0.2000+/-0.0100) second**2') self.assertEqual('{0:.3uLx}'.format(m), '\SI[separate-uncertainty=true]{0.2000(100)}{\second\squared}') self.assertEqual('{0:.1uLx}'.format(m), '\SI[separate-uncertainty=true]{0.20(1)}{\second\squared}') def test_format_percu(self): self.test_format_perce() v, u = self.Q_(0.20, 's ** 2'), self.Q_(0.01, 's ** 2') m = self.ureg.Measurement(v, u) self.assertEqual('{0:.1u%}'.format(m), '(20 +/- 1)% second ** 2') self.assertEqual('{0:.1u%P}'.format(m), '(20 ± 1)% second²') self.assertEqual('{0:.1u%L}'.format(m), r'\left(20 \pm 1\right) \%\ \mathrm{second}^{2}') self.assertEqual('{0:.1u%H}'.format(m), '(20 ± 1)% second2') self.assertEqual('{0:.1u%C}'.format(m), '(20+/-1)% second**2') def test_format_perce(self): v, u = self.Q_(0.20, 's ** 2'), self.Q_(0.01, 's ** 2') m = self.ureg.Measurement(v, u) self.assertEqual('{0:.1ue}'.format(m), '(2.0 +/- 0.1)e-01 second ** 2') self.assertEqual('{0:.1ueP}'.format(m), '(2.0 ± 0.1)×10⁻¹ second²') self.assertEqual('{0:.1ueL}'.format(m), r'\left(2.0 \pm 0.1\right) \times 10^{-1}\ \mathrm{second}^{2}') self.assertEqual('{0:.1ueH}'.format(m), '(2.0 ± 0.1)e-01 second2') self.assertEqual('{0:.1ueC}'.format(m), '(2.0+/-0.1)e-01 second**2') def test_raise_build(self): v, u = self.Q_(1.0, 's'), self.Q_(.1, 's') o = self.Q_(.1, 'm') M_ = self.ureg.Measurement self.assertRaises(ValueError, M_, v, o) self.assertRaises(ValueError, v.plus_minus, o) self.assertRaises(ValueError, v.plus_minus, u, True) def test_propagate_linear(self): v1, u1 = self.Q_(8.0, 's'), self.Q_(.7, 's') v2, u2 = self.Q_(5.0, 's'), self.Q_(.6, 's') v2, u3 = self.Q_(-5.0, 's'), self.Q_(.6, 's') m1 = v1.plus_minus(u1) m2 = v2.plus_minus(u2) m3 = v2.plus_minus(u3) for factor, m in zip((3, -3, 3, -3), (m1, m3, m1, m3)): r = factor * m self.assertAlmostEqual(r.value.magnitude, factor * m.value.magnitude) self.assertAlmostEqual(r.error.magnitude, abs(factor * m.error.magnitude)) self.assertEqual(r.value.units, m.value.units) for ml, mr in zip((m1, m1, m1, m3), (m1, m2, m3, m3)): r = ml + mr self.assertAlmostEqual(r.value.magnitude, ml.value.magnitude + mr.value.magnitude) self.assertAlmostEqual(r.error.magnitude, ml.error.magnitude + mr.error.magnitude if ml is mr else (ml.error.magnitude ** 2 + mr.error.magnitude ** 2) ** .5) self.assertEqual(r.value.units, ml.value.units) for ml, mr in zip((m1, m1, m1, m3), (m1, m2, m3, m3)): r = ml - mr self.assertAlmostEqual(r.value.magnitude, ml.value.magnitude - mr.value.magnitude) self.assertAlmostEqual(r.error.magnitude, 0 if ml is mr else (ml.error.magnitude ** 2 + mr.error.magnitude ** 2) ** .5) self.assertEqual(r.value.units, ml.value.units) def test_propagate_product(self): v1, u1 = self.Q_(8.0, 's'), self.Q_(.7, 's') v2, u2 = self.Q_(5.0, 's'), self.Q_(.6, 's') v2, u3 = self.Q_(-5.0, 's'), self.Q_(.6, 's') m1 = v1.plus_minus(u1) m2 = v2.plus_minus(u2) m3 = v2.plus_minus(u3) m4 = (2.3 * self.ureg.meter).plus_minus(0.1) m5 = (1.4 * self.ureg.meter).plus_minus(0.2) for ml, mr in zip((m1, m1, m1, m3, m4), (m1, m2, m3, m3, m5)): r = ml * mr self.assertAlmostEqual(r.value.magnitude, ml.value.magnitude * mr.value.magnitude) self.assertEqual(r.value.units, ml.value.units * mr.value.units) for ml, mr in zip((m1, m1, m1, m3, m4), (m1, m2, m3, m3, m5)): r = ml / mr self.assertAlmostEqual(r.value.magnitude, ml.value.magnitude / mr.value.magnitude) self.assertEqual(r.value.units, ml.value.units / mr.value.units) pint-0.8.1/pint/testsuite/test_numpy.py000066400000000000000000000371611311537545300202750ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import copy import operator as op from pint import DimensionalityError, set_application_registry from pint.compat import np from pint.testsuite import QuantityTestCase, helpers from pint.testsuite.compat import unittest from pint.testsuite.test_umath import TestUFuncs @helpers.requires_numpy() class TestNumpyMethods(QuantityTestCase): FORCE_NDARRAY = True @classmethod def setUpClass(cls): from pint import _DEFAULT_REGISTRY cls.ureg = _DEFAULT_REGISTRY cls.Q_ = cls.ureg.Quantity @property def q(self): return [[1,2],[3,4]] * self.ureg.m def test_tolist(self): self.assertEqual(self.q.tolist(), [[1*self.ureg.m, 2*self.ureg.m], [3*self.ureg.m, 4*self.ureg.m]]) def test_sum(self): self.assertEqual(self.q.sum(), 10*self.ureg.m) self.assertQuantityEqual(self.q.sum(0), [4, 6]*self.ureg.m) self.assertQuantityEqual(self.q.sum(1), [3, 7]*self.ureg.m) def test_fill(self): tmp = self.q tmp.fill(6 * self.ureg.ft) self.assertQuantityEqual(tmp, [[6, 6], [6, 6]] * self.ureg.ft) tmp.fill(5 * self.ureg.m) self.assertQuantityEqual(tmp, [[5, 5], [5, 5]] * self.ureg.m) def test_reshape(self): self.assertQuantityEqual(self.q.reshape([1,4]), [[1, 2, 3, 4]] * self.ureg.m) def test_transpose(self): self.assertQuantityEqual(self.q.transpose(), [[1, 3], [2, 4]] * self.ureg.m) def test_flatten(self): self.assertQuantityEqual(self.q.flatten(), [1, 2, 3, 4] * self.ureg.m) def test_flat(self): for q, v in zip(self.q.flat, [1, 2, 3, 4]): self.assertEqual(q, v * self.ureg.m) def test_ravel(self): self.assertQuantityEqual(self.q.ravel(), [1, 2, 3, 4] * self.ureg.m) def test_squeeze(self): self.assertQuantityEqual( self.q.reshape([1,4]).squeeze(), [1, 2, 3, 4] * self.ureg.m ) def test_take(self): self.assertQuantityEqual(self.q.take([0,1,2,3]), self.q.flatten()) def test_put(self): q = [1., 2., 3., 4.] * self.ureg.m q.put([0, 2], [10.,20.]*self.ureg.m) self.assertQuantityEqual(q, [10., 2., 20., 4.]*self.ureg.m) q = [1., 2., 3., 4.] * self.ureg.m q.put([0, 2], [1., 2.]*self.ureg.mm) self.assertQuantityEqual(q, [0.001, 2., 0.002, 4.]*self.ureg.m) q = [1., 2., 3., 4.] * self.ureg.m / self.ureg.mm q.put([0, 2], [1., 2.]) self.assertQuantityEqual(q, [0.001, 2., 0.002, 4.]*self.ureg.m/self.ureg.mm) q = [1., 2., 3., 4.] * self.ureg.m self.assertRaises(ValueError, q.put, [0, 2], [4., 6.] * self.ureg.J) self.assertRaises(ValueError, q.put, [0, 2], [4., 6.]) def test_repeat(self): self.assertQuantityEqual(self.q.repeat(2), [1,1,2,2,3,3,4,4]*self.ureg.m) def test_sort(self): q = [4, 5, 2, 3, 1, 6] * self.ureg.m q.sort() self.assertQuantityEqual(q, [1, 2, 3, 4, 5, 6] * self.ureg.m) def test_argsort(self): q = [1, 4, 5, 6, 2, 9] * self.ureg.MeV np.testing.assert_array_equal(q.argsort(), [0, 4, 1, 2, 3, 5]) def test_diagonal(self): q = [[1, 2, 3], [1, 2, 3], [1, 2, 3]] * self.ureg.m self.assertQuantityEqual(q.diagonal(offset=1), [2, 3] * self.ureg.m) def test_compress(self): self.assertQuantityEqual(self.q.compress([False, True], axis=0), [[3, 4]] * self.ureg.m) self.assertQuantityEqual(self.q.compress([False, True], axis=1), [[2], [4]] * self.ureg.m) def test_searchsorted(self): q = self.q.flatten() np.testing.assert_array_equal(q.searchsorted([1.5, 2.5] * self.ureg.m), [1, 2]) q = self.q.flatten() self.assertRaises(ValueError, q.searchsorted, [1.5, 2.5]) def test_nonzero(self): q = [1, 0, 5, 6, 0, 9] * self.ureg.m np.testing.assert_array_equal(q.nonzero()[0], [0, 2, 3, 5]) def test_max(self): self.assertEqual(self.q.max(), 4*self.ureg.m) def test_argmax(self): self.assertEqual(self.q.argmax(), 3) def test_min(self): self.assertEqual(self.q.min(), 1 * self.ureg.m) def test_argmin(self): self.assertEqual(self.q.argmin(), 0) def test_ptp(self): self.assertEqual(self.q.ptp(), 3 * self.ureg.m) def test_clip(self): self.assertQuantityEqual( self.q.clip(max=2*self.ureg.m), [[1, 2], [2, 2]] * self.ureg.m ) self.assertQuantityEqual( self.q.clip(min=3*self.ureg.m), [[3, 3], [3, 4]] * self.ureg.m ) self.assertQuantityEqual( self.q.clip(min=2*self.ureg.m, max=3*self.ureg.m), [[2, 2], [3, 3]] * self.ureg.m ) self.assertRaises(ValueError, self.q.clip, self.ureg.J) self.assertRaises(ValueError, self.q.clip, 1) def test_round(self): q = [1, 1.33, 5.67, 22] * self.ureg.m self.assertQuantityEqual(q.round(0), [1, 1, 6, 22] * self.ureg.m) self.assertQuantityEqual(q.round(-1), [0, 0, 10, 20] * self.ureg.m) self.assertQuantityEqual(q.round(1), [1, 1.3, 5.7, 22] * self.ureg.m) def test_trace(self): self.assertEqual(self.q.trace(), (1+4) * self.ureg.m) def test_cumsum(self): self.assertQuantityEqual(self.q.cumsum(), [1, 3, 6, 10] * self.ureg.m) def test_mean(self): self.assertEqual(self.q.mean(), 2.5 * self.ureg.m) def test_var(self): self.assertEqual(self.q.var(), 1.25*self.ureg.m**2) def test_std(self): self.assertQuantityAlmostEqual(self.q.std(), 1.11803*self.ureg.m, rtol=1e-5) def test_prod(self): self.assertEqual(self.q.prod(), 24 * self.ureg.m**4) def test_cumprod(self): self.assertRaises(ValueError, self.q.cumprod) self.assertQuantityEqual((self.q / self.ureg.m).cumprod(), [1, 2, 6, 24]) @helpers.requires_numpy_previous_than('1.10') def test_integer_div(self): a = [1] * self.ureg.m b = [2] * self.ureg.m c = a/b # Should be float division self.assertEqual(c.magnitude[0], 0.5) a /= b # Should be integer division self.assertEqual(a.magnitude[0], 0) def test_conj(self): self.assertQuantityEqual((self.q*(1+1j)).conj(), self.q*(1-1j)) self.assertQuantityEqual((self.q*(1+1j)).conjugate(), self.q*(1-1j)) def test_getitem(self): self.assertRaises(IndexError, self.q.__getitem__, (0,10)) self.assertQuantityEqual(self.q[0], [1,2]*self.ureg.m) self.assertEqual(self.q[1,1], 4*self.ureg.m) def test_setitem(self): self.assertRaises(ValueError, self.q.__setitem__, (0,0), 1) self.assertRaises(ValueError, self.q.__setitem__, (0,0), 1*self.ureg.J) self.assertRaises(ValueError, self.q.__setitem__, 0, 1) self.assertRaises(ValueError, self.q.__setitem__, 0, np.ndarray([1, 2])) self.assertRaises(ValueError, self.q.__setitem__, 0, 1*self.ureg.J) q = self.q.copy() q[0] = 1*self.ureg.m self.assertQuantityEqual(q, [[1,1],[3,4]]*self.ureg.m) q = self.q.copy() q.__setitem__(Ellipsis, 1*self.ureg.m) self.assertQuantityEqual(q, [[1,1],[1,1]]*self.ureg.m) q = self.q.copy() q[:] = 1*self.ureg.m self.assertQuantityEqual(q, [[1,1],[1,1]]*self.ureg.m) # check and see that dimensionless num bers work correctly q = [0,1,2,3]*self.ureg.dimensionless q[0] = 1 self.assertQuantityEqual(q, np.asarray([1,1,2,3])) q[0] = self.ureg.m/self.ureg.mm self.assertQuantityEqual(q, np.asarray([1000, 1,2,3])) q = [0.,1.,2.,3.] * self.ureg.m / self.ureg.mm q[0] = 1. self.assertQuantityEqual(q, [0.001,1,2,3]*self.ureg.m / self.ureg.mm) def test_iterator(self): for q, v in zip(self.q.flatten(), [1, 2, 3, 4]): self.assertEqual(q, v * self.ureg.m) def test_reversible_op(self): """ """ x = self.q.magnitude u = self.Q_(np.ones(x.shape)) self.assertQuantityEqual(x / self.q, u * x / self.q) self.assertQuantityEqual(x * self.q, u * x * self.q) self.assertQuantityEqual(x + u, u + x) self.assertQuantityEqual(x - u, -(u - x)) 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) def test_equal(self): x = self.q.magnitude u = self.Q_(np.ones(x.shape)) self.assertQuantityEqual(u, u) self.assertQuantityEqual(u == u, u.magnitude == u.magnitude) self.assertQuantityEqual(u == 1, u.magnitude == 1) def test_shape(self): u = self.Q_(np.arange(12)) u.shape = 4, 3 self.assertEqual(u.magnitude.shape, (4, 3)) @helpers.requires_numpy() class TestNumpyNeedsSubclassing(TestUFuncs): FORCE_NDARRAY = True @property def q(self): return [1. ,2., 3., 4.] * self.ureg.J @unittest.expectedFailure def test_unwrap(self): """unwrap depends on diff """ self.assertQuantityEqual(np.unwrap([0,3*np.pi]*self.ureg.radians), [0,np.pi]) self.assertQuantityEqual(np.unwrap([0,540]*self.ureg.deg), [0,180]*self.ureg.deg) @unittest.expectedFailure def test_trapz(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray """ self.assertQuantityEqual(np.trapz(self.q, dx=1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m) @unittest.expectedFailure def test_diff(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray """ self.assertQuantityEqual(np.diff(self.q, 1), [1, 1, 1] * self.ureg.J) @unittest.expectedFailure def test_ediff1d(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray """ self.assertQuantityEqual(np.ediff1d(self.q, 1 * self.ureg.J), [1, 1, 1] * self.ureg.J) @unittest.expectedFailure def test_fix(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray """ self.assertQuantityEqual(np.fix(3.14 * self.ureg.m), 3.0 * self.ureg.m) self.assertQuantityEqual(np.fix(3.0 * self.ureg.m), 3.0 * self.ureg.m) self.assertQuantityEqual( np.fix([2.1, 2.9, -2.1, -2.9] * self.ureg.m), [2., 2., -2., -2.] * self.ureg.m ) @unittest.expectedFailure def test_gradient(self): """shape is a property not a function """ l = np.gradient([[1,1],[3,4]] * self.ureg.J, 1 * self.ureg.m) self.assertQuantityEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.J / self.ureg.m) self.assertQuantityEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.J / self.ureg.m) @unittest.expectedFailure def test_cross(self): """Units are erased by asarray, Quantity does not inherit from NDArray """ a = [[3,-3, 1]] * self.ureg.kPa b = [[4, 9, 2]] * self.ureg.m**2 self.assertQuantityEqual(np.cross(a, b), [-15, -2, 39] * self.ureg.kPa * self.ureg.m**2) @unittest.expectedFailure def test_power(self): """This is not supported as different elements might end up with different units eg. ([1, 1] * m) ** [2, 3] Must force exponent to single value """ self._test2(np.power, self.q1, (self.qless, np.asarray([1., 2, 3, 4])), (self.q2, ),) @unittest.expectedFailure def test_ones_like(self): """Units are erased by emptyarra, Quantity does not inherit from NDArray """ self._test1(np.ones_like, (self.q2, self.qs, self.qless, self.qi), (), 2) @unittest.skip class TestBitTwiddlingUfuncs(TestUFuncs): """Universal functions (ufuncs) > Bittwiddling functions http://docs.scipy.org/doc/numpy/reference/ufuncs.html#bittwiddlingfunctions bitwise_and(x1, x2[, out]) Compute the bitwise AND of two arrays elementwise. bitwise_or(x1, x2[, out]) Compute the bitwise OR of two arrays elementwise. bitwise_xor(x1, x2[, out]) Compute the bitwise XOR of two arrays elementwise. invert(x[, out]) Compute bitwise inversion, or bitwise NOT, elementwise. left_shift(x1, x2[, out]) Shift the bits of an integer to the left. right_shift(x1, x2[, out]) Shift the bits of an integer to the right. """ @property def qless(self): return np.asarray([1, 2, 3, 4], dtype=np.uint8) * self.ureg.dimensionless @property def qs(self): return 8 * self.ureg.J @property def q1(self): return np.asarray([1, 2, 3, 4], dtype=np.uint8) * self.ureg.J @property def q2(self): return 2 * self.q1 @property def qm(self): return np.asarray([1, 2, 3, 4], dtype=np.uint8) * self.ureg.m def test_bitwise_and(self): self._test2(np.bitwise_and, self.q1, (self.q2, self.qs,), (self.qm, ), 'same') def test_bitwise_or(self): self._test2(np.bitwise_or, self.q1, (self.q1, self.q2, self.qs, ), (self.qm,), 'same') def test_bitwise_xor(self): self._test2(np.bitwise_xor, self.q1, (self.q1, self.q2, self.qs, ), (self.qm, ), 'same') def test_invert(self): self._test1(np.invert, (self.q1, self.q2, self.qs, ), (), 'same') def test_left_shift(self): self._test2(np.left_shift, self.q1, (self.qless, 2), (self.q1, self.q2, self.qs, ), 'same') def test_right_shift(self): self._test2(np.right_shift, self.q1, (self.qless, 2), (self.q1, self.q2, self.qs, ), 'same') class TestNDArrayQunatityMath(QuantityTestCase): @helpers.requires_numpy() def test_exponentiation_array_exp(self): arr = np.array(range(3), dtype=np.float) q = self.Q_(arr, 'meter') for op_ in [op.pow, op.ipow]: q_cp = copy.copy(q) self.assertRaises(DimensionalityError, op_, 2., q_cp) arr_cp = copy.copy(arr) arr_cp = copy.copy(arr) q_cp = copy.copy(q) self.assertRaises(DimensionalityError, op_, q_cp, arr_cp) q_cp = copy.copy(q) q2_cp = copy.copy(q) self.assertRaises(DimensionalityError, op_, q_cp, q2_cp) @unittest.expectedFailure @helpers.requires_numpy() def test_exponentiation_array_exp_2(self): arr = np.array(range(3), dtype=np.float) #q = self.Q_(copy.copy(arr), None) q = self.Q_(copy.copy(arr), 'meter') arr_cp = copy.copy(arr) q_cp = copy.copy(q) # this fails as expected since numpy 1.8.0 but... 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 arr_cp = copy.copy(arr) q_cp = copy.copy(q) self.assertRaises(DimensionalityError, op.ipow, arr_cp, q_cp) pint-0.8.1/pint/testsuite/test_pint_eval.py000066400000000000000000000060551311537545300211040ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import from pint.compat import tokenizer from pint.pint_eval import build_eval_tree from pint.testsuite.compat import unittest class TestPintEval(unittest.TestCase): def _test_one(self, input_text, parsed): self.assertEqual(build_eval_tree(tokenizer(input_text)).to_string(), parsed) def test_build_eval_tree(self): self._test_one('3', '3') self._test_one('1 + 2', '(1 + 2)') # order of operations self._test_one('2 * 3 + 4', '((2 * 3) + 4)') # parentheses self._test_one('2 * (3 + 4)', '(2 * (3 + 4))') # more order of operations self._test_one('1 + 2 * 3 ** (4 + 3 / 5)', '(1 + (2 * (3 ** (4 + (3 / 5)))))') # nested parentheses at beginning self._test_one('1 * ((3 + 4) * 5)', '(1 * ((3 + 4) * 5))') # nested parentheses at end self._test_one('1 * (5 * (3 + 4))', '(1 * (5 * (3 + 4)))') # nested parentheses in middle self._test_one('1 * (5 * (3 + 4) / 6)', '(1 * ((5 * (3 + 4)) / 6))') # unary self._test_one('-1', '(- 1)') # unary self._test_one('3 * -1', '(3 * (- 1))') # double unary self._test_one('3 * --1', '(3 * (- (- 1)))') # parenthetical unary self._test_one('3 * -(2 + 4)', '(3 * (- (2 + 4)))') # parenthetical unary self._test_one('3 * -((2 + 4))', '(3 * (- (2 + 4)))') # implicit op self._test_one('3 4', '(3 4)') # implicit op, then parentheses self._test_one('3 (2 + 4)', '(3 (2 + 4))') # parentheses, then implicit self._test_one('(3 ** 4 ) 5', '((3 ** 4) 5)') # implicit op, then exponentiation self._test_one('3 4 ** 5', '(3 (4 ** 5))') # implicit op, then addition self._test_one('3 4 + 5', '((3 4) + 5)') # power followed by implicit self._test_one('3 ** 4 5', '((3 ** 4) 5)') # implicit with parentheses self._test_one('3 (4 ** 5)', '(3 (4 ** 5))') # exponent with e self._test_one('3e-1', '3e-1') # multiple units with exponents self._test_one('kg ** 1 * s ** 2', '((kg ** 1) * (s ** 2))') # multiple units with neg exponents self._test_one('kg ** -1 * s ** -2', '((kg ** (- 1)) * (s ** (- 2)))') # multiple units with neg exponents self._test_one('kg^-1 * s^-2', '((kg ^ (- 1)) * (s ^ (- 2)))') # multiple units with neg exponents, implicit op self._test_one('kg^-1 s^-2', '((kg ^ (- 1)) (s ^ (- 2)))') # nested power self._test_one('2 ^ 3 ^ 2', '(2 ^ (3 ^ 2))') # nested power self._test_one('gram * second / meter ** 2', '((gram * second) / (meter ** 2))') # nested power self._test_one('gram / meter ** 2 / second', '((gram / (meter ** 2)) / second)') # units should behave like numbers, so we don't need a bunch of extra tests for them # implicit op, then addition self._test_one('3 kg + 5', '((3 kg) + 5)') pint-0.8.1/pint/testsuite/test_pitheorem.py000066400000000000000000000024011311537545300211060ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import itertools from pint import pi_theorem from pint.testsuite import QuantityTestCase class TestPiTheorem(QuantityTestCase): FORCE_NDARRAY = False def test_simple(self): # simple movement with self.capture_log() as buffer: self.assertEqual(pi_theorem({'V': 'm/s', 'T': 's', 'L': 'm'}), [{'V': 1, 'T': 1, 'L': -1}]) # pendulum self.assertEqual(pi_theorem({'T': 's', 'M': 'grams', 'L': 'm', 'g': 'm/s**2'}), [{'g': 1, 'T': 2, 'L': -1}]) self.assertEqual(len(buffer), 7) def test_inputs(self): V = 'km/hour' T = 'ms' L = 'cm' f1 = lambda x: x f2 = lambda x: self.Q_(1, x) f3 = lambda x: self.Q_(1, x).units f4 = lambda x: self.Q_(1, x).dimensionality fs = f1, f2, f3, f4 for fv, ft, fl in itertools.product(fs, fs, fs): qv = fv(V) qt = ft(T) ql = ft(L) self.assertEqual(self.ureg.pi_theorem({'V': qv, 'T': qt, 'L': ql}), [{'V': 1.0, 'T': 1.0, 'L': -1.0}]) pint-0.8.1/pint/testsuite/test_quantity.py000066400000000000000000001613041311537545300210000ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import copy import datetime import math import operator as op import warnings from pint import DimensionalityError, OffsetUnitCalculusError, UnitRegistry from pint.unit import UnitsContainer from pint.compat import string_types, PYTHON3, np from pint.testsuite import QuantityTestCase, helpers from pint.testsuite.parameterized import ParameterizedTestCase class TestQuantity(QuantityTestCase): FORCE_NDARRAY = False def test_quantity_creation(self): for args in ((4.2, 'meter'), (4.2, UnitsContainer(meter=1)), (4.2, self.ureg.meter), ('4.2*meter', ), ('4.2/meter**(-1)', ), (self.Q_(4.2, 'meter'),)): x = self.Q_(*args) self.assertEqual(x.magnitude, 4.2) self.assertEqual(x.units, UnitsContainer(meter=1)) x = self.Q_(4.2, UnitsContainer(length=1)) y = self.Q_(x) self.assertEqual(x.magnitude, y.magnitude) self.assertEqual(x.units, y.units) self.assertIsNot(x, y) x = self.Q_(4.2, None) self.assertEqual(x.magnitude, 4.2) self.assertEqual(x.units, UnitsContainer()) with self.capture_log() as buffer: self.assertEqual(4.2 * self.ureg.meter, self.Q_(4.2, 2 * self.ureg.meter)) self.assertEqual(len(buffer), 1) def test_quantity_bool(self): self.assertTrue(self.Q_(1, None)) self.assertTrue(self.Q_(1, 'meter')) self.assertFalse(self.Q_(0, None)) self.assertFalse(self.Q_(0, 'meter')) def test_quantity_comparison(self): x = self.Q_(4.2, 'meter') y = self.Q_(4.2, 'meter') z = self.Q_(5, 'meter') j = self.Q_(5, 'meter*meter') # identity for single object self.assertTrue(x == x) self.assertFalse(x != x) # identity for multiple objects with same value self.assertTrue(x == y) self.assertFalse(x != y) self.assertTrue(x <= y) self.assertTrue(x >= y) self.assertFalse(x < y) self.assertFalse(x > y) self.assertFalse(x == z) self.assertTrue(x != z) self.assertTrue(x < z) self.assertTrue(z != j) self.assertNotEqual(z, j) self.assertEqual(self.Q_(0, 'meter'), self.Q_(0, 'centimeter')) self.assertNotEqual(self.Q_(0, 'meter'), self.Q_(0, 'second')) self.assertLess(self.Q_(10, 'meter'), self.Q_(5, 'kilometer')) def test_quantity_comparison_convert(self): self.assertEqual(self.Q_(1000, 'millimeter'), self.Q_(1, 'meter')) self.assertEqual(self.Q_(1000, 'millimeter/min'), self.Q_(1000/60, 'millimeter/s')) def test_quantity_repr(self): x = self.Q_(4.2, UnitsContainer(meter=1)) self.assertEqual(str(x), '4.2 meter') self.assertEqual(repr(x), "") def test_quantity_format(self): x = self.Q_(4.12345678, UnitsContainer(meter=2, kilogram=1, second=-1)) for spec, result in (('{0}', str(x)), ('{0!s}', str(x)), ('{0!r}', repr(x)), ('{0.magnitude}', str(x.magnitude)), ('{0.units}', str(x.units)), ('{0.magnitude!s}', str(x.magnitude)), ('{0.units!s}', str(x.units)), ('{0.magnitude!r}', repr(x.magnitude)), ('{0.units!r}', repr(x.units)), ('{0:.4f}', '{0:.4f} {1!s}'.format(x.magnitude, x.units)), ('{0:L}', r'4.12345678\ \frac{\mathrm{kilogram} \cdot \mathrm{meter}^{2}}{\mathrm{second}}'), ('{0:P}', '4.12345678 kilogram·meter²/second'), ('{0:H}', '4.12345678 kilogram meter2/second'), ('{0:C}', '4.12345678 kilogram*meter**2/second'), ('{0:~}', '4.12345678 kg * m ** 2 / s'), ('{0:L~}', r'4.12345678\ \frac{\mathrm{kg} \cdot \mathrm{m}^{2}}{\mathrm{s}}'), ('{0:P~}', '4.12345678 kg·m²/s'), ('{0:H~}', '4.12345678 kg m2/s'), ('{0:C~}', '4.12345678 kg*m**2/s'), ('{0:Lx}', r'\SI[]{4.12345678}{\kilo\gram\meter\squared\per\second}'), ): self.assertEqual(spec.format(x), result) # Check the special case that prevents e.g. '3 1 / second' x = self.Q_(3, UnitsContainer(second=-1)) self.assertEqual('{0}'.format(x), '3 / second') def test_format_compact(self): q1 = (200e-9 * self.ureg.s).to_compact() q1b = self.Q_(200., 'nanosecond') self.assertAlmostEqual(q1.magnitude, q1b.magnitude) self.assertEqual(q1.units, q1b.units) q2 = (1e-2 * self.ureg('kg m/s^2')).to_compact('N') q2b = self.Q_(10., 'millinewton') self.assertEqual(q2.magnitude, q2b.magnitude) self.assertEqual(q2.units, q2b.units) q3 = (-1000.0 * self.ureg('meters')).to_compact() q3b = self.Q_(-1., 'kilometer') self.assertEqual(q3.magnitude, q3b.magnitude) self.assertEqual(q3.units, q3b.units) self.assertEqual('{0:#.1f}'.format(q1), '{0}'.format(q1b)) self.assertEqual('{0:#.1f}'.format(q2), '{0}'.format(q2b)) self.assertEqual('{0:#.1f}'.format(q3), '{0}'.format(q3b)) def test_default_formatting(self): ureg = UnitRegistry() x = ureg.Quantity(4.12345678, UnitsContainer(meter=2, kilogram=1, second=-1)) for spec, result in (('L', r'4.12345678\ \frac{\mathrm{kilogram} \cdot \mathrm{meter}^{2}}{\mathrm{second}}'), ('P', '4.12345678 kilogram·meter²/second'), ('H', '4.12345678 kilogram meter2/second'), ('C', '4.12345678 kilogram*meter**2/second'), ('~', '4.12345678 kg * m ** 2 / s'), ('L~', r'4.12345678\ \frac{\mathrm{kg} \cdot \mathrm{m}^{2}}{\mathrm{s}}'), ('P~', '4.12345678 kg·m²/s'), ('H~', '4.12345678 kg m2/s'), ('C~', '4.12345678 kg*m**2/s'), ): ureg.default_format = spec self.assertEqual('{0}'.format(x), result) def test_to_base_units(self): x = self.Q_('1*inch') self.assertQuantityAlmostEqual(x.to_base_units(), self.Q_(0.0254, 'meter')) x = self.Q_('1*inch*inch') self.assertQuantityAlmostEqual(x.to_base_units(), self.Q_(0.0254 ** 2.0, 'meter*meter')) x = self.Q_('1*inch/minute') self.assertQuantityAlmostEqual(x.to_base_units(), self.Q_(0.0254 / 60., 'meter/second')) def test_convert(self): x = self.Q_('2*inch') self.assertQuantityAlmostEqual(x.to('meter'), self.Q_(2. * 0.0254, 'meter')) x = self.Q_('2*meter') self.assertQuantityAlmostEqual(x.to('inch'), self.Q_(2. / 0.0254, 'inch')) x = self.Q_('2*sidereal_second') self.assertQuantityAlmostEqual(x.to('second'), self.Q_(1.994539133 , 'second')) x = self.Q_('2.54*centimeter/second') self.assertQuantityAlmostEqual(x.to('inch/second'), self.Q_(1, 'inch/second')) x = self.Q_('2.54*centimeter') self.assertQuantityAlmostEqual(x.to('inch').magnitude, 1) self.assertQuantityAlmostEqual(self.Q_(2, 'second').to('millisecond').magnitude, 2000) @helpers.requires_numpy() def test_convert(self): # Conversions with single units take a different codepath than # Conversions with more than one unit. src_dst1 = UnitsContainer(meter=1), UnitsContainer(inch=1) src_dst2 = UnitsContainer(meter=1, second=-1), UnitsContainer(inch=1, minute=-1) for src, dst in (src_dst1, src_dst2): a = np.ones((3, 1)) ac = np.ones((3, 1)) q = self.Q_(a, src) qac = self.Q_(ac, src).to(dst) r = q.to(dst) self.assertQuantityAlmostEqual(qac, r) self.assertIsNot(r, q) self.assertIsNot(r._magnitude, a) @helpers.requires_numpy() def test_retain_unit(self): # Test that methods correctly retain units and do not degrade into # ordinary ndarrays. List contained in __copy_units. a = np.ones((3, 2)) q = self.Q_(a, "km") self.assertEqual(q.u, q.reshape(2, 3).u) self.assertEqual(q.u, q.swapaxes(0, 1).u) self.assertEqual(q.u, q.mean().u) self.assertEqual(q.u, np.compress((q==q[0,0]).any(0), q).u) def test_context_attr(self): self.assertEqual(self.ureg.meter, self.Q_(1, 'meter')) def test_both_symbol(self): self.assertEqual(self.Q_(2, 'ms'), self.Q_(2, 'millisecond')) self.assertEqual(self.Q_(2, 'cm'), self.Q_(2, 'centimeter')) def test_dimensionless_units(self): self.assertAlmostEqual(self.Q_(360, 'degree').to('radian').magnitude, 2 * math.pi) self.assertAlmostEqual(self.Q_(2 * math.pi, 'radian'), self.Q_(360, 'degree')) self.assertEqual(self.Q_(1, 'radian').dimensionality, UnitsContainer()) self.assertTrue(self.Q_(1, 'radian').dimensionless) self.assertFalse(self.Q_(1, 'radian').unitless) self.assertEqual(self.Q_(1, 'meter')/self.Q_(1, 'meter'), 1) self.assertEqual((self.Q_(1, 'meter')/self.Q_(1, 'mm')).to(''), 1000) self.assertEqual(self.Q_(10) // self.Q_(360, 'degree'), 1) self.assertEqual(self.Q_(400, 'degree') // self.Q_(2 * math.pi), 1) self.assertEqual(self.Q_(400, 'degree') // (2 * math.pi), 1) self.assertEqual(7 // self.Q_(360, 'degree'), 1) def test_offset(self): self.assertQuantityAlmostEqual(self.Q_(0, 'kelvin').to('kelvin'), self.Q_(0, 'kelvin')) self.assertQuantityAlmostEqual(self.Q_(0, 'degC').to('kelvin'), self.Q_(273.15, 'kelvin')) self.assertQuantityAlmostEqual(self.Q_(0, 'degF').to('kelvin'), self.Q_(255.372222, 'kelvin'), rtol=0.01) self.assertQuantityAlmostEqual(self.Q_(100, 'kelvin').to('kelvin'), self.Q_(100, 'kelvin')) self.assertQuantityAlmostEqual(self.Q_(100, 'degC').to('kelvin'), self.Q_(373.15, 'kelvin')) self.assertQuantityAlmostEqual(self.Q_(100, 'degF').to('kelvin'), self.Q_(310.92777777, 'kelvin'), rtol=0.01) self.assertQuantityAlmostEqual(self.Q_(0, 'kelvin').to('degC'), self.Q_(-273.15, 'degC')) self.assertQuantityAlmostEqual(self.Q_(100, 'kelvin').to('degC'), self.Q_(-173.15, 'degC')) self.assertQuantityAlmostEqual(self.Q_(0, 'kelvin').to('degF'), self.Q_(-459.67, 'degF'), rtol=0.01) self.assertQuantityAlmostEqual(self.Q_(100, 'kelvin').to('degF'), self.Q_(-279.67, 'degF'), rtol=0.01) self.assertQuantityAlmostEqual(self.Q_(32, 'degF').to('degC'), self.Q_(0, 'degC'), atol=0.01) self.assertQuantityAlmostEqual(self.Q_(100, 'degC').to('degF'), self.Q_(212, 'degF'), atol=0.01) self.assertQuantityAlmostEqual(self.Q_(54, 'degF').to('degC'), self.Q_(12.2222, 'degC'), atol=0.01) self.assertQuantityAlmostEqual(self.Q_(12, 'degC').to('degF'), self.Q_(53.6, 'degF'), atol=0.01) self.assertQuantityAlmostEqual(self.Q_(12, 'kelvin').to('degC'), self.Q_(-261.15, 'degC'), atol=0.01) self.assertQuantityAlmostEqual(self.Q_(12, 'degC').to('kelvin'), self.Q_(285.15, 'kelvin'), atol=0.01) self.assertQuantityAlmostEqual(self.Q_(12, 'kelvin').to('degR'), self.Q_(21.6, 'degR'), atol=0.01) self.assertQuantityAlmostEqual(self.Q_(12, 'degR').to('kelvin'), self.Q_(6.66666667, 'kelvin'), atol=0.01) self.assertQuantityAlmostEqual(self.Q_(12, 'degC').to('degR'), self.Q_(513.27, 'degR'), atol=0.01) self.assertQuantityAlmostEqual(self.Q_(12, 'degR').to('degC'), self.Q_(-266.483333, 'degC'), atol=0.01) def test_offset_delta(self): self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degC').to('kelvin'), self.Q_(0, 'kelvin')) self.assertQuantityAlmostEqual(self.Q_(0, 'delta_degF').to('kelvin'), self.Q_(0, 'kelvin'), rtol=0.01) self.assertQuantityAlmostEqual(self.Q_(100, 'kelvin').to('delta_degC'), self.Q_(100, 'delta_degC')) self.assertQuantityAlmostEqual(self.Q_(100, 'kelvin').to('delta_degF'), self.Q_(180, 'delta_degF'), rtol=0.01) self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degF').to('kelvin'), self.Q_(55.55555556, 'kelvin'), rtol=0.01) self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degC').to('delta_degF'), self.Q_(180, 'delta_degF'), rtol=0.01) self.assertQuantityAlmostEqual(self.Q_(100, 'delta_degF').to('delta_degC'), self.Q_(55.55555556, 'delta_degC'), rtol=0.01) self.assertQuantityAlmostEqual(self.Q_(12.3, 'delta_degC').to('delta_degF'), self.Q_(22.14, 'delta_degF'), rtol=0.01) def test_pickle(self): import pickle def pickle_test(q): self.assertEqual(q, pickle.loads(pickle.dumps(q))) pickle_test(self.Q_(32, '')) pickle_test(self.Q_(2.4, '')) pickle_test(self.Q_(32, 'm/s')) pickle_test(self.Q_(2.4, 'm/s')) class TestQuantityToCompact(QuantityTestCase): def assertQuantityAlmostIdentical(self, q1, q2): self.assertEqual(q1.units, q2.units) self.assertAlmostEqual(q1.magnitude, q2.magnitude) def compareQuantity_compact(self, q, expected_compact, unit=None): self.assertQuantityAlmostIdentical(q.to_compact(unit=unit), expected_compact) def test_dimensionally_simple_units(self): ureg = self.ureg self.compareQuantity_compact(1*ureg.m, 1*ureg.m) self.compareQuantity_compact(1e-9*ureg.m, 1*ureg.nm) def test_power_units(self): ureg = self.ureg self.compareQuantity_compact(900*ureg.m**2, 900*ureg.m**2) self.compareQuantity_compact(1e7*ureg.m**2, 10*ureg.km**2) def test_inverse_units(self): ureg = self.ureg self.compareQuantity_compact(1/ureg.m, 1/ureg.m) self.compareQuantity_compact(100e9/ureg.m, 100/ureg.nm) def test_inverse_square_units(self): ureg = self.ureg self.compareQuantity_compact(1/ureg.m**2, 1/ureg.m**2) self.compareQuantity_compact(1e11/ureg.m**2, 1e5/ureg.mm**2) def test_fractional_exponent_units(self): ureg = self.ureg self.compareQuantity_compact(1*ureg.m**0.5, 1*ureg.m**0.5) self.compareQuantity_compact(1e-2*ureg.m**0.5, 10*ureg.um**0.5) def test_derived_units(self): ureg = self.ureg self.compareQuantity_compact(0.5*ureg.megabyte, 500*ureg.kilobyte) self.compareQuantity_compact(1e-11*ureg.N, 10*ureg.pN) def test_unit_parameter(self): ureg = self.ureg self.compareQuantity_compact(self.Q_(100e-9, 'kg m / s^2'), 100*ureg.nN, ureg.N) self.compareQuantity_compact(self.Q_(101.3e3, 'kg/m/s^2'), 101.3*ureg.kPa, ureg.Pa) def test_limits_magnitudes(self): ureg = self.ureg self.compareQuantity_compact(0*ureg.m, 0*ureg.m) self.compareQuantity_compact(float('inf')*ureg.m, float('inf')*ureg.m) def test_nonnumeric_magnitudes(self): ureg = self.ureg x = "some string"*ureg.m self.assertRaises(RuntimeError, self.compareQuantity_compact(x,x)) class TestQuantityBasicMath(QuantityTestCase): FORCE_NDARRAY = False def _test_inplace(self, operator, value1, value2, expected_result, unit=None): if isinstance(value1, string_types): value1 = self.Q_(value1) if isinstance(value2, string_types): value2 = self.Q_(value2) if isinstance(expected_result, string_types): expected_result = self.Q_(expected_result) if not unit is None: value1 = value1 * unit value2 = value2 * unit expected_result = expected_result * unit value1 = copy.copy(value1) value2 = copy.copy(value2) id1 = id(value1) id2 = id(value2) value1 = operator(value1, value2) value2_cpy = copy.copy(value2) self.assertQuantityAlmostEqual(value1, expected_result) self.assertEqual(id1, id(value1)) self.assertQuantityAlmostEqual(value2, value2_cpy) self.assertEqual(id2, id(value2)) def _test_not_inplace(self, operator, value1, value2, expected_result, unit=None): if isinstance(value1, string_types): value1 = self.Q_(value1) if isinstance(value2, string_types): value2 = self.Q_(value2) if isinstance(expected_result, string_types): expected_result = self.Q_(expected_result) if not unit is None: value1 = value1 * unit value2 = value2 * unit expected_result = expected_result * unit id1 = id(value1) id2 = id(value2) value1_cpy = copy.copy(value1) value2_cpy = copy.copy(value2) result = operator(value1, value2) self.assertQuantityAlmostEqual(expected_result, result) self.assertQuantityAlmostEqual(value1, value1_cpy) self.assertQuantityAlmostEqual(value2, value2_cpy) self.assertNotEqual(id(result), id1) self.assertNotEqual(id(result), id2) def _test_quantity_add_sub(self, unit, func): x = self.Q_(unit, 'centimeter') y = self.Q_(unit, 'inch') z = self.Q_(unit, 'second') a = self.Q_(unit, None) func(op.add, x, x, self.Q_(unit + unit, 'centimeter')) func(op.add, x, y, self.Q_(unit + 2.54 * unit, 'centimeter')) func(op.add, y, x, self.Q_(unit + unit / (2.54 * unit), 'inch')) func(op.add, a, unit, self.Q_(unit + unit, None)) self.assertRaises(DimensionalityError, op.add, 10, x) self.assertRaises(DimensionalityError, op.add, x, 10) self.assertRaises(DimensionalityError, op.add, x, z) func(op.sub, x, x, self.Q_(unit - unit, 'centimeter')) func(op.sub, x, y, self.Q_(unit - 2.54 * unit, 'centimeter')) func(op.sub, y, x, self.Q_(unit - unit / (2.54 * unit), 'inch')) func(op.sub, a, unit, self.Q_(unit - unit, None)) self.assertRaises(DimensionalityError, op.sub, 10, x) self.assertRaises(DimensionalityError, op.sub, x, 10) self.assertRaises(DimensionalityError, op.sub, x, z) def _test_quantity_iadd_isub(self, unit, func): x = self.Q_(unit, 'centimeter') y = self.Q_(unit, 'inch') z = self.Q_(unit, 'second') a = self.Q_(unit, None) func(op.iadd, x, x, self.Q_(unit + unit, 'centimeter')) func(op.iadd, x, y, self.Q_(unit + 2.54 * unit, 'centimeter')) func(op.iadd, y, x, self.Q_(unit + unit / 2.54, 'inch')) func(op.iadd, a, unit, self.Q_(unit + unit, None)) self.assertRaises(DimensionalityError, op.iadd, 10, x) self.assertRaises(DimensionalityError, op.iadd, x, 10) self.assertRaises(DimensionalityError, op.iadd, x, z) func(op.isub, x, x, self.Q_(unit - unit, 'centimeter')) func(op.isub, x, y, self.Q_(unit - 2.54, 'centimeter')) func(op.isub, y, x, self.Q_(unit - unit / 2.54, 'inch')) func(op.isub, a, unit, self.Q_(unit - unit, None)) self.assertRaises(DimensionalityError, op.sub, 10, x) self.assertRaises(DimensionalityError, op.sub, x, 10) self.assertRaises(DimensionalityError, op.sub, x, z) def _test_quantity_mul_div(self, unit, func): func(op.mul, unit * 10.0, '4.2*meter', '42*meter', unit) func(op.mul, '4.2*meter', unit * 10.0, '42*meter', unit) func(op.mul, '4.2*meter', '10*inch', '42*meter*inch', unit) func(op.truediv, unit * 42, '4.2*meter', '10/meter', unit) func(op.truediv, '4.2*meter', unit * 10.0, '0.42*meter', unit) func(op.truediv, '4.2*meter', '10*inch', '0.42*meter/inch', unit) def _test_quantity_imul_idiv(self, unit, func): #func(op.imul, 10.0, '4.2*meter', '42*meter') func(op.imul, '4.2*meter', 10.0, '42*meter', unit) func(op.imul, '4.2*meter', '10*inch', '42*meter*inch', unit) #func(op.truediv, 42, '4.2*meter', '10/meter') func(op.itruediv, '4.2*meter', unit * 10.0, '0.42*meter', unit) func(op.itruediv, '4.2*meter', '10*inch', '0.42*meter/inch', unit) def _test_quantity_floordiv(self, unit, func): a = self.Q_('10*meter') b = self.Q_('3*second') self.assertRaises(DimensionalityError, op.floordiv, a, b) self.assertRaises(DimensionalityError, op.floordiv, 3, b) self.assertRaises(DimensionalityError, op.floordiv, a, 3) self.assertRaises(DimensionalityError, op.ifloordiv, a, b) self.assertRaises(DimensionalityError, op.ifloordiv, 3, b) self.assertRaises(DimensionalityError, op.ifloordiv, a, 3) func(op.floordiv, unit * 10.0, '4.2*meter/meter', 2, unit) func(op.floordiv, '10*meter', '4.2*inch', 93, unit) def _test_quantity_mod(self, unit, func): a = self.Q_('10*meter') b = self.Q_('3*second') self.assertRaises(DimensionalityError, op.mod, a, b) self.assertRaises(DimensionalityError, op.mod, 3, b) self.assertRaises(DimensionalityError, op.mod, a, 3) self.assertRaises(DimensionalityError, op.imod, a, b) self.assertRaises(DimensionalityError, op.imod, 3, b) self.assertRaises(DimensionalityError, op.imod, a, 3) func(op.mod, unit * 10.0, '4.2*meter/meter', 1.6, unit) def _test_quantity_ifloordiv(self, unit, func): func(op.ifloordiv, 10.0, '4.2*meter/meter', 2, unit) func(op.ifloordiv, '10*meter', '4.2*inch', 93, unit) def _test_quantity_divmod_one(self, a, b): if isinstance(a, string_types): a = self.Q_(a) if isinstance(b, string_types): b = self.Q_(b) q, r = divmod(a, b) self.assertEqual(q, a // b) self.assertEqual(r, a % b) self.assertEqual(a, (q * b) + r) self.assertEqual(q, math.floor(q)) if b > (0 * b): self.assertTrue((0 * b) <= r < b) else: self.assertTrue((0 * b) >= r > b) if isinstance(a, self.Q_): self.assertEqual(r.units, a.units) else: self.assertTrue(r.unitless) self.assertTrue(q.unitless) copy_a = copy.copy(a) a %= b self.assertEqual(a, r) copy_a //= b self.assertEqual(copy_a, q) def _test_quantity_divmod(self): self._test_quantity_divmod_one('10*meter', '4.2*inch') self._test_quantity_divmod_one('-10*meter', '4.2*inch') self._test_quantity_divmod_one('-10*meter', '-4.2*inch') self._test_quantity_divmod_one('10*meter', '-4.2*inch') self._test_quantity_divmod_one('400*degree', '3') self._test_quantity_divmod_one('4', '180 degree') self._test_quantity_divmod_one(4, '180 degree') self._test_quantity_divmod_one('20', 4) self._test_quantity_divmod_one('300*degree', '100 degree') a = self.Q_('10*meter') b = self.Q_('3*second') self.assertRaises(DimensionalityError, divmod, a, b) self.assertRaises(DimensionalityError, divmod, 3, b) self.assertRaises(DimensionalityError, divmod, a, 3) def _test_numeric(self, unit, ifunc): self._test_quantity_add_sub(unit, self._test_not_inplace) self._test_quantity_iadd_isub(unit, ifunc) self._test_quantity_mul_div(unit, self._test_not_inplace) self._test_quantity_imul_idiv(unit, ifunc) self._test_quantity_floordiv(unit, self._test_not_inplace) self._test_quantity_mod(unit, self._test_not_inplace) self._test_quantity_divmod() #self._test_quantity_ifloordiv(unit, ifunc) def test_float(self): self._test_numeric(1., self._test_not_inplace) def test_fraction(self): import fractions self._test_numeric(fractions.Fraction(1, 1), self._test_not_inplace) @helpers.requires_numpy() def test_nparray(self): self._test_numeric(np.ones((1, 3)), self._test_inplace) def test_quantity_abs_round(self): x = self.Q_(-4.2, 'meter') y = self.Q_(4.2, 'meter') # In Python 3+ round of x is delegated to x.__round__, instead of round(x.__float__) # and therefore it can be properly implemented by Pint for fun in (abs, op.pos, op.neg) + (round, ) if PYTHON3 else (): zx = self.Q_(fun(x.magnitude), 'meter') zy = self.Q_(fun(y.magnitude), 'meter') rx = fun(x) ry = fun(y) self.assertEqual(rx, zx, 'while testing {0}'.format(fun)) self.assertEqual(ry, zy, 'while testing {0}'.format(fun)) self.assertIsNot(rx, zx, 'while testing {0}'.format(fun)) self.assertIsNot(ry, zy, 'while testing {0}'.format(fun)) def test_quantity_float_complex(self): x = self.Q_(-4.2, None) y = self.Q_(4.2, None) z = self.Q_(1, 'meter') for fun in (float, complex): self.assertEqual(fun(x), fun(x.magnitude)) self.assertEqual(fun(y), fun(y.magnitude)) self.assertRaises(DimensionalityError, fun, z) class TestDimensions(QuantityTestCase): FORCE_NDARRAY = False def test_get_dimensionality(self): get = self.ureg.get_dimensionality self.assertEqual(get('[time]'), UnitsContainer({'[time]': 1})) self.assertEqual(get(UnitsContainer({'[time]': 1})), UnitsContainer({'[time]': 1})) self.assertEqual(get('seconds'), UnitsContainer({'[time]': 1})) self.assertEqual(get(UnitsContainer({'seconds': 1})), UnitsContainer({'[time]': 1})) self.assertEqual(get('[speed]'), UnitsContainer({'[length]': 1, '[time]': -1})) self.assertEqual(get('[acceleration]'), UnitsContainer({'[length]': 1, '[time]': -2})) def test_dimensionality(self): x = self.Q_(42, 'centimeter') x.to_base_units() x = self.Q_(42, 'meter*second') self.assertEqual(x.dimensionality, UnitsContainer({'[length]': 1., '[time]': 1.})) x = self.Q_(42, 'meter*second*second') self.assertEqual(x.dimensionality, UnitsContainer({'[length]': 1., '[time]': 2.})) x = self.Q_(42, 'inch*second*second') self.assertEqual(x.dimensionality, UnitsContainer({'[length]': 1., '[time]': 2.})) self.assertTrue(self.Q_(42, None).dimensionless) self.assertFalse(self.Q_(42, 'meter').dimensionless) self.assertTrue((self.Q_(42, 'meter') / self.Q_(1, 'meter')).dimensionless) self.assertFalse((self.Q_(42, 'meter') / self.Q_(1, 'second')).dimensionless) self.assertTrue((self.Q_(42, 'meter') / self.Q_(1, 'inch')).dimensionless) def test_inclusion(self): dim = self.Q_(42, 'meter').dimensionality self.assertTrue('[length]' in dim) self.assertFalse('[time]' in dim) dim = (self.Q_(42, 'meter') / self.Q_(11, 'second')).dimensionality self.assertTrue('[length]' in dim) self.assertTrue('[time]' in dim) dim = self.Q_(20.785, 'J/(mol)').dimensionality for dimension in ('[length]', '[mass]', '[substance]', '[time]'): self.assertTrue(dimension in dim) self.assertFalse('[angle]' in dim) class TestQuantityWithDefaultRegistry(TestDimensions): @classmethod def setUpClass(cls): from pint import _DEFAULT_REGISTRY cls.ureg = _DEFAULT_REGISTRY cls.Q_ = cls.ureg.Quantity class TestDimensionsWithDefaultRegistry(TestDimensions): @classmethod def setUpClass(cls): from pint import _DEFAULT_REGISTRY cls.ureg = _DEFAULT_REGISTRY cls.Q_ = cls.ureg.Quantity class TestOffsetUnitMath(QuantityTestCase, ParameterizedTestCase): def setup(self): self.ureg.autoconvert_offset_to_baseunit = False self.ureg.default_as_delta = True additions = [ # --- input tuple -------------------- | -- expected result -- (((100, 'kelvin'), (10, 'kelvin')), (110, 'kelvin')), (((100, 'kelvin'), (10, 'degC')), 'error'), (((100, 'kelvin'), (10, 'degF')), 'error'), (((100, 'kelvin'), (10, 'degR')), (105.56, 'kelvin')), (((100, 'kelvin'), (10, 'delta_degC')), (110, 'kelvin')), (((100, 'kelvin'), (10, 'delta_degF')), (105.56, 'kelvin')), (((100, 'degC'), (10, 'kelvin')), 'error'), (((100, 'degC'), (10, 'degC')), 'error'), (((100, 'degC'), (10, 'degF')), 'error'), (((100, 'degC'), (10, 'degR')), 'error'), (((100, 'degC'), (10, 'delta_degC')), (110, 'degC')), (((100, 'degC'), (10, 'delta_degF')), (105.56, 'degC')), (((100, 'degF'), (10, 'kelvin')), 'error'), (((100, 'degF'), (10, 'degC')), 'error'), (((100, 'degF'), (10, 'degF')), 'error'), (((100, 'degF'), (10, 'degR')), 'error'), (((100, 'degF'), (10, 'delta_degC')), (118, 'degF')), (((100, 'degF'), (10, 'delta_degF')), (110, 'degF')), (((100, 'degR'), (10, 'kelvin')), (118, 'degR')), (((100, 'degR'), (10, 'degC')), 'error'), (((100, 'degR'), (10, 'degF')), 'error'), (((100, 'degR'), (10, 'degR')), (110, 'degR')), (((100, 'degR'), (10, 'delta_degC')), (118, 'degR')), (((100, 'degR'), (10, 'delta_degF')), (110, 'degR')), (((100, 'delta_degC'), (10, 'kelvin')), (110, 'kelvin')), (((100, 'delta_degC'), (10, 'degC')), (110, 'degC')), (((100, 'delta_degC'), (10, 'degF')), (190, 'degF')), (((100, 'delta_degC'), (10, 'degR')), (190, 'degR')), (((100, 'delta_degC'), (10, 'delta_degC')), (110, 'delta_degC')), (((100, 'delta_degC'), (10, 'delta_degF')), (105.56, 'delta_degC')), (((100, 'delta_degF'), (10, 'kelvin')), (65.56, 'kelvin')), (((100, 'delta_degF'), (10, 'degC')), (65.56, 'degC')), (((100, 'delta_degF'), (10, 'degF')), (110, 'degF')), (((100, 'delta_degF'), (10, 'degR')), (110, 'degR')), (((100, 'delta_degF'), (10, 'delta_degC')), (118, 'delta_degF')), (((100, 'delta_degF'), (10, 'delta_degF')), (110, 'delta_degF')), ] @ParameterizedTestCase.parameterize(("input", "expected_output"), additions) def test_addition(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = False qin1, qin2 = input_tuple q1, q2 = self.Q_(*qin1), self.Q_(*qin2) # update input tuple with new values to have correct values on failure input_tuple = q1, q2 if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.add, q1, q2) else: expected = self.Q_(*expected) self.assertEqual(op.add(q1, q2).units, expected.units) self.assertQuantityAlmostEqual(op.add(q1, q2), expected, atol=0.01) @helpers.requires_numpy() @ParameterizedTestCase.parameterize(("input", "expected_output"), additions) def test_inplace_addition(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = False (q1v, q1u), (q2v, q2u) = input_tuple # update input tuple with new values to have correct values on failure input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u), (np.array([q2v]*2, dtype=np.float), q2u)) Q_ = self.Q_ qin1, qin2 = input_tuple q1, q2 = Q_(*qin1), Q_(*qin2) q1_cp = copy.copy(q1) if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.iadd, q1_cp, q2) else: expected = np.array([expected[0]]*2, dtype=np.float), expected[1] self.assertEqual(op.iadd(q1_cp, q2).units, Q_(*expected).units) q1_cp = copy.copy(q1) self.assertQuantityAlmostEqual(op.iadd(q1_cp, q2), Q_(*expected), atol=0.01) subtractions = [ (((100, 'kelvin'), (10, 'kelvin')), (90, 'kelvin')), (((100, 'kelvin'), (10, 'degC')), (-183.15, 'kelvin')), (((100, 'kelvin'), (10, 'degF')), (-160.93, 'kelvin')), (((100, 'kelvin'), (10, 'degR')), (94.44, 'kelvin')), (((100, 'kelvin'), (10, 'delta_degC')), (90, 'kelvin')), (((100, 'kelvin'), (10, 'delta_degF')), (94.44, 'kelvin')), (((100, 'degC'), (10, 'kelvin')), (363.15, 'delta_degC')), (((100, 'degC'), (10, 'degC')), (90, 'delta_degC')), (((100, 'degC'), (10, 'degF')), (112.22, 'delta_degC')), (((100, 'degC'), (10, 'degR')), (367.59, 'delta_degC')), (((100, 'degC'), (10, 'delta_degC')), (90, 'degC')), (((100, 'degC'), (10, 'delta_degF')), (94.44, 'degC')), (((100, 'degF'), (10, 'kelvin')), (541.67, 'delta_degF')), (((100, 'degF'), (10, 'degC')), (50, 'delta_degF')), (((100, 'degF'), (10, 'degF')), (90, 'delta_degF')), (((100, 'degF'), (10, 'degR')), (549.67, 'delta_degF')), (((100, 'degF'), (10, 'delta_degC')), (82, 'degF')), (((100, 'degF'), (10, 'delta_degF')), (90, 'degF')), (((100, 'degR'), (10, 'kelvin')), (82, 'degR')), (((100, 'degR'), (10, 'degC')), (-409.67, 'degR')), (((100, 'degR'), (10, 'degF')), (-369.67, 'degR')), (((100, 'degR'), (10, 'degR')), (90, 'degR')), (((100, 'degR'), (10, 'delta_degC')), (82, 'degR')), (((100, 'degR'), (10, 'delta_degF')), (90, 'degR')), (((100, 'delta_degC'), (10, 'kelvin')), (90, 'kelvin')), (((100, 'delta_degC'), (10, 'degC')), (90, 'degC')), (((100, 'delta_degC'), (10, 'degF')), (170, 'degF')), (((100, 'delta_degC'), (10, 'degR')), (170, 'degR')), (((100, 'delta_degC'), (10, 'delta_degC')), (90, 'delta_degC')), (((100, 'delta_degC'), (10, 'delta_degF')), (94.44, 'delta_degC')), (((100, 'delta_degF'), (10, 'kelvin')), (45.56, 'kelvin')), (((100, 'delta_degF'), (10, 'degC')), (45.56, 'degC')), (((100, 'delta_degF'), (10, 'degF')), (90, 'degF')), (((100, 'delta_degF'), (10, 'degR')), (90, 'degR')), (((100, 'delta_degF'), (10, 'delta_degC')), (82, 'delta_degF')), (((100, 'delta_degF'), (10, 'delta_degF')), (90, 'delta_degF')), ] @ParameterizedTestCase.parameterize(("input", "expected_output"), subtractions) def test_subtraction(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = False qin1, qin2 = input_tuple q1, q2 = self.Q_(*qin1), self.Q_(*qin2) input_tuple = q1, q2 if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.sub, q1, q2) else: expected = self.Q_(*expected) self.assertEqual(op.sub(q1, q2).units, expected.units) self.assertQuantityAlmostEqual(op.sub(q1, q2), expected, atol=0.01) # @unittest.expectedFailure @helpers.requires_numpy() @ParameterizedTestCase.parameterize(("input", "expected_output"), subtractions) def test_inplace_subtraction(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = False (q1v, q1u), (q2v, q2u) = input_tuple # update input tuple with new values to have correct values on failure input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u), (np.array([q2v]*2, dtype=np.float), q2u)) Q_ = self.Q_ qin1, qin2 = input_tuple q1, q2 = Q_(*qin1), Q_(*qin2) q1_cp = copy.copy(q1) if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.isub, q1_cp, q2) else: expected = np.array([expected[0]]*2, dtype=np.float), expected[1] self.assertEqual(op.isub(q1_cp, q2).units, Q_(*expected).units) q1_cp = copy.copy(q1) self.assertQuantityAlmostEqual(op.isub(q1_cp, q2), Q_(*expected), atol=0.01) multiplications = [ (((100, 'kelvin'), (10, 'kelvin')), (1000, 'kelvin**2')), (((100, 'kelvin'), (10, 'degC')), 'error'), (((100, 'kelvin'), (10, 'degF')), 'error'), (((100, 'kelvin'), (10, 'degR')), (1000, 'kelvin*degR')), (((100, 'kelvin'), (10, 'delta_degC')), (1000, 'kelvin*delta_degC')), (((100, 'kelvin'), (10, 'delta_degF')), (1000, 'kelvin*delta_degF')), (((100, 'degC'), (10, 'kelvin')), 'error'), (((100, 'degC'), (10, 'degC')), 'error'), (((100, 'degC'), (10, 'degF')), 'error'), (((100, 'degC'), (10, 'degR')), 'error'), (((100, 'degC'), (10, 'delta_degC')), 'error'), (((100, 'degC'), (10, 'delta_degF')), 'error'), (((100, 'degF'), (10, 'kelvin')), 'error'), (((100, 'degF'), (10, 'degC')), 'error'), (((100, 'degF'), (10, 'degF')), 'error'), (((100, 'degF'), (10, 'degR')), 'error'), (((100, 'degF'), (10, 'delta_degC')), 'error'), (((100, 'degF'), (10, 'delta_degF')), 'error'), (((100, 'degR'), (10, 'kelvin')), (1000, 'degR*kelvin')), (((100, 'degR'), (10, 'degC')), 'error'), (((100, 'degR'), (10, 'degF')), 'error'), (((100, 'degR'), (10, 'degR')), (1000, 'degR**2')), (((100, 'degR'), (10, 'delta_degC')), (1000, 'degR*delta_degC')), (((100, 'degR'), (10, 'delta_degF')), (1000, 'degR*delta_degF')), (((100, 'delta_degC'), (10, 'kelvin')), (1000, 'delta_degC*kelvin')), (((100, 'delta_degC'), (10, 'degC')), 'error'), (((100, 'delta_degC'), (10, 'degF')), 'error'), (((100, 'delta_degC'), (10, 'degR')), (1000, 'delta_degC*degR')), (((100, 'delta_degC'), (10, 'delta_degC')), (1000, 'delta_degC**2')), (((100, 'delta_degC'), (10, 'delta_degF')), (1000, 'delta_degC*delta_degF')), (((100, 'delta_degF'), (10, 'kelvin')), (1000, 'delta_degF*kelvin')), (((100, 'delta_degF'), (10, 'degC')), 'error'), (((100, 'delta_degF'), (10, 'degF')), 'error'), (((100, 'delta_degF'), (10, 'degR')), (1000, 'delta_degF*degR')), (((100, 'delta_degF'), (10, 'delta_degC')), (1000, 'delta_degF*delta_degC')), (((100, 'delta_degF'), (10, 'delta_degF')), (1000, 'delta_degF**2')), ] @ParameterizedTestCase.parameterize(("input", "expected_output"), multiplications) def test_multiplication(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = False qin1, qin2 = input_tuple q1, q2 = self.Q_(*qin1), self.Q_(*qin2) input_tuple = q1, q2 if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.mul, q1, q2) else: expected = self.Q_(*expected) self.assertEqual(op.mul(q1, q2).units, expected.units) self.assertQuantityAlmostEqual(op.mul(q1, q2), expected, atol=0.01) @helpers.requires_numpy() @ParameterizedTestCase.parameterize(("input", "expected_output"), multiplications) def test_inplace_multiplication(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = False (q1v, q1u), (q2v, q2u) = input_tuple # update input tuple with new values to have correct values on failure input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u), (np.array([q2v]*2, dtype=np.float), q2u)) Q_ = self.Q_ qin1, qin2 = input_tuple q1, q2 = Q_(*qin1), Q_(*qin2) q1_cp = copy.copy(q1) if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.imul, q1_cp, q2) else: expected = np.array([expected[0]]*2, dtype=np.float), expected[1] self.assertEqual(op.imul(q1_cp, q2).units, Q_(*expected).units) q1_cp = copy.copy(q1) self.assertQuantityAlmostEqual(op.imul(q1_cp, q2), Q_(*expected), atol=0.01) divisions = [ (((100, 'kelvin'), (10, 'kelvin')), (10, '')), (((100, 'kelvin'), (10, 'degC')), 'error'), (((100, 'kelvin'), (10, 'degF')), 'error'), (((100, 'kelvin'), (10, 'degR')), (10, 'kelvin/degR')), (((100, 'kelvin'), (10, 'delta_degC')), (10, 'kelvin/delta_degC')), (((100, 'kelvin'), (10, 'delta_degF')), (10, 'kelvin/delta_degF')), (((100, 'degC'), (10, 'kelvin')), 'error'), (((100, 'degC'), (10, 'degC')), 'error'), (((100, 'degC'), (10, 'degF')), 'error'), (((100, 'degC'), (10, 'degR')), 'error'), (((100, 'degC'), (10, 'delta_degC')), 'error'), (((100, 'degC'), (10, 'delta_degF')), 'error'), (((100, 'degF'), (10, 'kelvin')), 'error'), (((100, 'degF'), (10, 'degC')), 'error'), (((100, 'degF'), (10, 'degF')), 'error'), (((100, 'degF'), (10, 'degR')), 'error'), (((100, 'degF'), (10, 'delta_degC')), 'error'), (((100, 'degF'), (10, 'delta_degF')), 'error'), (((100, 'degR'), (10, 'kelvin')), (10, 'degR/kelvin')), (((100, 'degR'), (10, 'degC')), 'error'), (((100, 'degR'), (10, 'degF')), 'error'), (((100, 'degR'), (10, 'degR')), (10, '')), (((100, 'degR'), (10, 'delta_degC')), (10, 'degR/delta_degC')), (((100, 'degR'), (10, 'delta_degF')), (10, 'degR/delta_degF')), (((100, 'delta_degC'), (10, 'kelvin')), (10, 'delta_degC/kelvin')), (((100, 'delta_degC'), (10, 'degC')), 'error'), (((100, 'delta_degC'), (10, 'degF')), 'error'), (((100, 'delta_degC'), (10, 'degR')), (10, 'delta_degC/degR')), (((100, 'delta_degC'), (10, 'delta_degC')), (10, '')), (((100, 'delta_degC'), (10, 'delta_degF')), (10, 'delta_degC/delta_degF')), (((100, 'delta_degF'), (10, 'kelvin')), (10, 'delta_degF/kelvin')), (((100, 'delta_degF'), (10, 'degC')), 'error'), (((100, 'delta_degF'), (10, 'degF')), 'error'), (((100, 'delta_degF'), (10, 'degR')), (10, 'delta_degF/degR')), (((100, 'delta_degF'), (10, 'delta_degC')), (10, 'delta_degF/delta_degC')), (((100, 'delta_degF'), (10, 'delta_degF')), (10, '')), ] @ParameterizedTestCase.parameterize(("input", "expected_output"), divisions) def test_truedivision(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = False qin1, qin2 = input_tuple q1, q2 = self.Q_(*qin1), self.Q_(*qin2) input_tuple = q1, q2 if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.truediv, q1, q2) else: expected = self.Q_(*expected) self.assertEqual(op.truediv(q1, q2).units, expected.units) self.assertQuantityAlmostEqual(op.truediv(q1, q2), expected, atol=0.01) @helpers.requires_numpy() @ParameterizedTestCase.parameterize(("input", "expected_output"), divisions) def test_inplace_truedivision(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = False (q1v, q1u), (q2v, q2u) = input_tuple # update input tuple with new values to have correct values on failure input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u), (np.array([q2v]*2, dtype=np.float), q2u)) Q_ = self.Q_ qin1, qin2 = input_tuple q1, q2 = Q_(*qin1), Q_(*qin2) q1_cp = copy.copy(q1) if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.itruediv, q1_cp, q2) else: expected = np.array([expected[0]]*2, dtype=np.float), expected[1] self.assertEqual(op.itruediv(q1_cp, q2).units, Q_(*expected).units) q1_cp = copy.copy(q1) self.assertQuantityAlmostEqual(op.itruediv(q1_cp, q2), Q_(*expected), atol=0.01) multiplications_with_autoconvert_to_baseunit = [ (((100, 'kelvin'), (10, 'degC')), (28315., 'kelvin**2')), (((100, 'kelvin'), (10, 'degF')), (26092.78, 'kelvin**2')), (((100, 'degC'), (10, 'kelvin')), (3731.5, 'kelvin**2')), (((100, 'degC'), (10, 'degC')), (105657.42, 'kelvin**2')), (((100, 'degC'), (10, 'degF')), (97365.20, 'kelvin**2')), (((100, 'degC'), (10, 'degR')), (3731.5, 'kelvin*degR')), (((100, 'degC'), (10, 'delta_degC')), (3731.5, 'kelvin*delta_degC')), (((100, 'degC'), (10, 'delta_degF')), (3731.5, 'kelvin*delta_degF')), (((100, 'degF'), (10, 'kelvin')), (3109.28, 'kelvin**2')), (((100, 'degF'), (10, 'degC')), (88039.20, 'kelvin**2')), (((100, 'degF'), (10, 'degF')), (81129.69, 'kelvin**2')), (((100, 'degF'), (10, 'degR')), (3109.28, 'kelvin*degR')), (((100, 'degF'), (10, 'delta_degC')), (3109.28, 'kelvin*delta_degC')), (((100, 'degF'), (10, 'delta_degF')), (3109.28, 'kelvin*delta_degF')), (((100, 'degR'), (10, 'degC')), (28315., 'degR*kelvin')), (((100, 'degR'), (10, 'degF')), (26092.78, 'degR*kelvin')), (((100, 'delta_degC'), (10, 'degC')), (28315., 'delta_degC*kelvin')), (((100, 'delta_degC'), (10, 'degF')), (26092.78, 'delta_degC*kelvin')), (((100, 'delta_degF'), (10, 'degC')), (28315., 'delta_degF*kelvin')), (((100, 'delta_degF'), (10, 'degF')), (26092.78, 'delta_degF*kelvin')), ] @ParameterizedTestCase.parameterize( ("input", "expected_output"), multiplications_with_autoconvert_to_baseunit) def test_multiplication_with_autoconvert(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = True qin1, qin2 = input_tuple q1, q2 = self.Q_(*qin1), self.Q_(*qin2) input_tuple = q1, q2 if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.mul, q1, q2) else: expected = self.Q_(*expected) self.assertEqual(op.mul(q1, q2).units, expected.units) self.assertQuantityAlmostEqual(op.mul(q1, q2), expected, atol=0.01) @helpers.requires_numpy() @ParameterizedTestCase.parameterize( ("input", "expected_output"), multiplications_with_autoconvert_to_baseunit) def test_inplace_multiplication_with_autoconvert(self, input_tuple, expected): self.ureg.autoconvert_offset_to_baseunit = True (q1v, q1u), (q2v, q2u) = input_tuple # update input tuple with new values to have correct values on failure input_tuple = ((np.array([q1v]*2, dtype=np.float), q1u), (np.array([q2v]*2, dtype=np.float), q2u)) Q_ = self.Q_ qin1, qin2 = input_tuple q1, q2 = Q_(*qin1), Q_(*qin2) q1_cp = copy.copy(q1) if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.imul, q1_cp, q2) else: expected = np.array([expected[0]]*2, dtype=np.float), expected[1] self.assertEqual(op.imul(q1_cp, q2).units, Q_(*expected).units) q1_cp = copy.copy(q1) self.assertQuantityAlmostEqual(op.imul(q1_cp, q2), Q_(*expected), atol=0.01) multiplications_with_scalar = [ (((10, 'kelvin'), 2), (20., 'kelvin')), (((10, 'kelvin**2'), 2), (20., 'kelvin**2')), (((10, 'degC'), 2), (20., 'degC')), (((10, '1/degC'), 2), 'error'), (((10, 'degC**0.5'), 2), 'error'), (((10, 'degC**2'), 2), 'error'), (((10, 'degC**-2'), 2), 'error'), ] @ParameterizedTestCase.parameterize( ("input", "expected_output"), multiplications_with_scalar) def test_multiplication_with_scalar(self, input_tuple, expected): self.ureg.default_as_delta = False in1, in2 = input_tuple if type(in1) is tuple: in1, in2 = self.Q_(*in1), in2 else: in1, in2 = in1, self.Q_(*in2) input_tuple = in1, in2 # update input_tuple for better tracebacks if expected == 'error': self.assertRaises(OffsetUnitCalculusError, op.mul, in1, in2) else: expected = self.Q_(*expected) self.assertEqual(op.mul(in1, in2).units, expected.units) self.assertQuantityAlmostEqual(op.mul(in1, in2), expected, atol=0.01) divisions_with_scalar = [ # without / with autoconvert to base unit (((10, 'kelvin'), 2), [(5., 'kelvin'), (5., 'kelvin')]), (((10, 'kelvin**2'), 2), [(5., 'kelvin**2'), (5., 'kelvin**2')]), (((10, 'degC'), 2), ['error', 'error']), (((10, 'degC**2'), 2), ['error', 'error']), (((10, 'degC**-2'), 2), ['error', 'error']), ((2, (10, 'kelvin')), [(0.2, '1/kelvin'), (0.2, '1/kelvin')]), ((2, (10, 'degC')), ['error', (2/283.15, '1/kelvin')]), ((2, (10, 'degC**2')), ['error', 'error']), ((2, (10, 'degC**-2')), ['error', 'error']), ] @ParameterizedTestCase.parameterize( ("input", "expected_output"), divisions_with_scalar) def test_division_with_scalar(self, input_tuple, expected): self.ureg.default_as_delta = False in1, in2 = input_tuple if type(in1) is tuple: in1, in2 = self.Q_(*in1), in2 else: in1, in2 = in1, self.Q_(*in2) input_tuple = in1, in2 # update input_tuple for better tracebacks expected_copy = expected[:] for i, mode in enumerate([False, True]): self.ureg.autoconvert_offset_to_baseunit = mode if expected_copy[i] == 'error': self.assertRaises(OffsetUnitCalculusError, op.truediv, in1, in2) else: expected = self.Q_(*expected_copy[i]) self.assertEqual(op.truediv(in1, in2).units, expected.units) self.assertQuantityAlmostEqual(op.truediv(in1, in2), expected) exponentiation = [ # resuls without / with autoconvert (((10, 'degC'), 1), [(10, 'degC'), (10, 'degC')]), (((10, 'degC'), 0.5), ['error', (283.15**0.5, 'kelvin**0.5')]), (((10, 'degC'), 0), [(1., ''), (1., '')]), (((10, 'degC'), -1), ['error', (1/(10+273.15), 'kelvin**-1')]), (((10, 'degC'), -2), ['error', (1/(10+273.15)**2., 'kelvin**-2')]), ((( 0, 'degC'), -2), ['error', (1/(273.15)**2, 'kelvin**-2')]), (((10, 'degC'), (2, '')), ['error', ((283.15)**2, 'kelvin**2')]), (((10, 'degC'), (10, 'degK')), ['error', 'error']), (((10, 'kelvin'), (2, '')), [(100., 'kelvin**2'), (100., 'kelvin**2')]), (( 2, (2, 'kelvin')), ['error', 'error']), (( 2, (500., 'millikelvin/kelvin')), [2**0.5, 2**0.5]), (( 2, (0.5, 'kelvin/kelvin')), [2**0.5, 2**0.5]), (((10, 'degC'), (500., 'millikelvin/kelvin')), ['error', (283.15**0.5, 'kelvin**0.5')]), ] @ParameterizedTestCase.parameterize( ("input", "expected_output"), exponentiation) def test_exponentiation(self, input_tuple, expected): self.ureg.default_as_delta = False in1, in2 = input_tuple if type(in1) is tuple and type(in2) is tuple: in1, in2 = self.Q_(*in1), self.Q_(*in2) elif not type(in1) is tuple and type(in2) is tuple: in2 = self.Q_(*in2) else: in1 = self.Q_(*in1) input_tuple = in1, in2 expected_copy = expected[:] for i, mode in enumerate([False, True]): self.ureg.autoconvert_offset_to_baseunit = mode if expected_copy[i] == 'error': self.assertRaises((OffsetUnitCalculusError, DimensionalityError), op.pow, in1, in2) else: if type(expected_copy[i]) is tuple: expected = self.Q_(*expected_copy[i]) self.assertEqual(op.pow(in1, in2).units, expected.units) else: expected = expected_copy[i] self.assertQuantityAlmostEqual(op.pow(in1, in2), expected) @helpers.requires_numpy() @ParameterizedTestCase.parameterize( ("input", "expected_output"), exponentiation) def test_inplace_exponentiation(self, input_tuple, expected): self.ureg.default_as_delta = False in1, in2 = input_tuple if type(in1) is tuple and type(in2) is tuple: (q1v, q1u), (q2v, q2u) = in1, in2 in1 = self.Q_(*(np.array([q1v]*2, dtype=np.float), q1u)) in2 = self.Q_(q2v, q2u) elif not type(in1) is tuple and type(in2) is tuple: in2 = self.Q_(*in2) else: in1 = self.Q_(*in1) input_tuple = in1, in2 expected_copy = expected[:] for i, mode in enumerate([False, True]): self.ureg.autoconvert_offset_to_baseunit = mode in1_cp = copy.copy(in1) if expected_copy[i] == 'error': self.assertRaises((OffsetUnitCalculusError, DimensionalityError), op.ipow, in1_cp, in2) else: if type(expected_copy[i]) is tuple: expected = self.Q_(np.array([expected_copy[i][0]]*2, dtype=np.float), expected_copy[i][1]) self.assertEqual(op.ipow(in1_cp, in2).units, expected.units) else: expected = np.array([expected_copy[i]]*2, dtype=np.float) in1_cp = copy.copy(in1) self.assertQuantityAlmostEqual(op.ipow(in1_cp, in2), expected) class TestDimensionReduction(QuantityTestCase): def _calc_mass(self, ureg): density = 3 * ureg.g / ureg.L volume = 32 * ureg.milliliter return density * volume def _icalc_mass(self, ureg): res = ureg.Quantity(3.0, 'gram/liter') res *= ureg.Quantity(32.0, 'milliliter') return res def test_mul_and_div_reduction(self): ureg = UnitRegistry(auto_reduce_dimensions=True) mass = self._calc_mass(ureg) self.assertEqual(mass.units, ureg.g) ureg = UnitRegistry(auto_reduce_dimensions=False) mass = self._calc_mass(ureg) self.assertEqual(mass.units, ureg.g / ureg.L * ureg.milliliter) @helpers.requires_numpy() def test_imul_and_div_reduction(self): ureg = UnitRegistry(auto_reduce_dimensions=True, force_ndarray=True) mass = self._icalc_mass(ureg) self.assertEqual(mass.units, ureg.g) ureg = UnitRegistry(auto_reduce_dimensions=False, force_ndarray=True) mass = self._icalc_mass(ureg) self.assertEqual(mass.units, ureg.g / ureg.L * ureg.milliliter) def test_reduction_to_dimensionless(self): ureg = UnitRegistry(auto_reduce_dimensions=True) x = (10 * ureg.feet) / (3 * ureg.inches) self.assertEqual(x.units, UnitsContainer({})) ureg = UnitRegistry(auto_reduce_dimensions=False) x = (10 * ureg.feet) / (3 * ureg.inches) self.assertEqual(x.units, ureg.feet / ureg.inches) class TestTimedelta(QuantityTestCase): def test_add_sub(self): d = datetime.datetime(year=1968, month=1, day=10, hour=3, minute=42, second=24) after = d + 3 * self.ureg.second self.assertEqual(d + datetime.timedelta(seconds=3), after) after = 3 * self.ureg.second + d self.assertEqual(d + datetime.timedelta(seconds=3), after) after = d - 3 * self.ureg.second self.assertEqual(d - datetime.timedelta(seconds=3), after) with self.assertRaises(DimensionalityError): 3 * self.ureg.second - d def test_iadd_isub(self): d = datetime.datetime(year=1968, month=1, day=10, hour=3, minute=42, second=24) after = copy.copy(d) after += 3 * self.ureg.second self.assertEqual(d + datetime.timedelta(seconds=3), after) after = 3 * self.ureg.second after += d self.assertEqual(d + datetime.timedelta(seconds=3), after) after = copy.copy(d) after -= 3 * self.ureg.second self.assertEqual(d - datetime.timedelta(seconds=3), after) after = 3 * self.ureg.second with self.assertRaises(DimensionalityError): after -= d pint-0.8.1/pint/testsuite/test_systems.py000066400000000000000000000256641311537545300206410ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import from pint import UnitRegistry from pint.testsuite import QuantityTestCase class TestGroup(QuantityTestCase): def _build_empty_reg_root(self): ureg = UnitRegistry(None) grp = ureg.get_group('root') grp.remove_units('pi') grp.invalidate_members() return ureg, ureg.get_group('root') def test_units_programatically(self): ureg, root = self._build_empty_reg_root() d = ureg._groups self.assertEqual(root._used_groups, set()) self.assertEqual(root._used_by, set()) root.add_units('meter', 'second', 'meter') self.assertEqual(root._unit_names, set(['meter', 'second'])) self.assertEqual(root.members, set(['meter', 'second'])) self.assertEqual(set(d.keys()), set(['root'])) def test_cyclic(self): ureg, root = self._build_empty_reg_root() g2 = ureg.Group('g2') g3 = ureg.Group('g3') g2.add_groups('g3') self.assertRaises(ValueError, g2.add_groups, 'root') self.assertRaises(ValueError, g3.add_groups, 'g2') self.assertRaises(ValueError, g3.add_groups, 'root') def test_groups_programatically(self): ureg, root = self._build_empty_reg_root() d = ureg._groups g2 = ureg.Group('g2') self.assertEqual(set(d.keys()), set(['root', 'g2'])) self.assertEqual(root._used_groups, set(['g2'])) self.assertEqual(root._used_by, set()) self.assertEqual(g2._used_groups, set()) self.assertEqual(g2._used_by, set(['root'])) def test_simple(self): lines = ['@group mygroup', 'meter', 'second', ] ureg, root = self._build_empty_reg_root() d = ureg._groups grp = ureg.Group.from_lines(lines, lambda x: None) self.assertEqual(set(d.keys()), set(['root', 'mygroup'])) self.assertEqual(grp.name, 'mygroup') self.assertEqual(grp._unit_names, set(['meter', 'second'])) self.assertEqual(grp._used_groups, set()) self.assertEqual(grp._used_by, set([root.name])) self.assertEqual(grp.members, frozenset(['meter', 'second'])) def test_using1(self): lines = ['@group mygroup using group1', 'meter', 'second', ] ureg, root = self._build_empty_reg_root() d = ureg._groups g = ureg.Group('group1') grp = ureg.Group.from_lines(lines, lambda x: None) self.assertEqual(grp.name, 'mygroup') self.assertEqual(grp._unit_names, set(['meter', 'second'])) self.assertEqual(grp._used_groups, set(['group1'])) self.assertEqual(grp.members, frozenset(['meter', 'second'])) def test_using2(self): lines = ['@group mygroup using group1,group2', 'meter', 'second', ] ureg, root = self._build_empty_reg_root() d = ureg._groups ureg.Group('group1') ureg.Group('group2') grp = ureg.Group.from_lines(lines, lambda x: None) self.assertEqual(grp.name, 'mygroup') self.assertEqual(grp._unit_names, set(['meter', 'second'])) self.assertEqual(grp._used_groups, set(['group1', 'group2'])) self.assertEqual(grp.members, frozenset(['meter', 'second'])) def test_spaces(self): lines = ['@group mygroup using group1 , group2', ' meter ', ' second ', ] ureg, root = self._build_empty_reg_root() d = ureg._groups ureg.Group('group1') ureg.Group('group2') grp = ureg.Group.from_lines(lines, lambda x: None) self.assertEqual(grp.name, 'mygroup') self.assertEqual(grp._unit_names, set(['meter', 'second'])) self.assertEqual(grp._used_groups, set(['group1', 'group2'])) self.assertEqual(grp.members, frozenset(['meter', 'second'])) def test_invalidate_members(self): lines = ['@group mygroup using group1', 'meter', 'second', ] ureg, root = self._build_empty_reg_root() d = ureg._groups g1 = ureg.Group('group1') grp = ureg.Group.from_lines(lines, lambda x: None) self.assertIs(root._computed_members, None) self.assertIs(grp._computed_members, None) self.assertEqual(grp.members, frozenset(['meter', 'second'])) self.assertIs(root._computed_members, None) self.assertIsNot(grp._computed_members, None) self.assertEqual(root.members, frozenset(['meter', 'second'])) self.assertIsNot(root._computed_members, None) self.assertIsNot(grp._computed_members, None) grp.invalidate_members() self.assertIs(root._computed_members, None) self.assertIs(grp._computed_members, None) def test_with_defintions(self): lines = ['@group imperial', 'inch', 'yard', 'kings_leg = 2 * meter', 'kings_head = 52 * inch' 'pint' ] defs = [] def define(ud): defs.append(ud.name) ureg, root = self._build_empty_reg_root() d = ureg._groups grp = ureg.Group.from_lines(lines, define) self.assertEqual(['kings_leg', 'kings_head'], defs) def test_members_including(self): ureg, root = self._build_empty_reg_root() d = ureg._groups g1 = ureg.Group('group1') g1.add_units('second', 'inch') g2 = ureg.Group('group2') g2.add_units('second', 'newton') g3 = ureg.Group('group3') g3.add_units('meter', 'second') g3.add_groups('group1', 'group2') self.assertEqual(root.members, frozenset(['meter', 'second', 'newton', 'inch'])) self.assertEqual(g1.members, frozenset(['second', 'inch'])) self.assertEqual(g2.members, frozenset(['second', 'newton'])) self.assertEqual(g3.members, frozenset(['meter', 'second', 'newton', 'inch'])) def test_get_compatible_units(self): ureg = UnitRegistry() g = ureg.get_group('test-imperial') g.add_units('inch', 'yard', 'pint') c = ureg.get_compatible_units('meter', 'test-imperial') self.assertEqual(c, frozenset([ureg.inch, ureg.yard])) class TestSystem(QuantityTestCase): def _build_empty_reg_root(self): ureg = UnitRegistry(None) grp = ureg.get_group('root') grp.remove_units('pi') grp.invalidate_members() return ureg, ureg.get_group('root') def test_implicit_root(self): lines = ['@system mks', 'meter', 'kilogram', 'second', ] ureg, root = self._build_empty_reg_root() d = ureg._groups s = ureg.System.from_lines(lines, lambda x: x) s._used_groups = set(['root']) def test_simple_using(self): lines = ['@system mks using g1', 'meter', 'kilogram', 'second', ] ureg, root = self._build_empty_reg_root() d = ureg._groups s = ureg.System.from_lines(lines, lambda x: x) s._used_groups = set(['root', 'g1']) def test_members_group(self): lines = ['@system mk', 'meter', 'kilogram', ] ureg, root = self._build_empty_reg_root() d = ureg._groups root.add_units('second') s = ureg.System.from_lines(lines, lambda x: x) self.assertEqual(s.members, frozenset(['second'])) def test_get_compatible_units(self): sysname = 'mysys1' ureg = UnitRegistry() g = ureg.get_group('test-imperial') g.add_units('inch', 'yard', 'pint') c = ureg.get_compatible_units('meter', 'test-imperial') self.assertEqual(c, frozenset([ureg.inch, ureg.yard])) lines = ['@system %s using test-imperial' % sysname, 'inch', ] s = ureg.System.from_lines(lines, lambda x: x) c = ureg.get_compatible_units('meter', sysname) self.assertEqual(c, frozenset([ureg.inch, ureg.yard])) def test_get_base_units(self): sysname = 'mysys2' ureg = UnitRegistry() g = ureg.get_group('test-imperial') g.add_units('inch', 'yard', 'pint') lines = ['@system %s using test-imperial' % sysname, 'inch', ] s = ureg.System.from_lines(lines, ureg.get_base_units) ureg._systems[s.name] = s # base_factor, destination_units c = ureg.get_base_units('inch', system=sysname) self.assertAlmostEqual(c[0], 1) self.assertEqual(c[1], {'inch': 1}) c = ureg.get_base_units('cm', system=sysname) self.assertAlmostEqual(c[0], 1./2.54) self.assertEqual(c[1], {'inch': 1}) def test_get_base_units_different_exponent(self): sysname = 'mysys3' ureg = UnitRegistry() g = ureg.get_group('test-imperial') g.add_units('inch', 'yard', 'pint') c = ureg.get_compatible_units('meter', 'test-imperial') lines = ['@system %s using test-imperial' % sysname, 'pint:meter', ] s = ureg.System.from_lines(lines, ureg.get_base_units) ureg._systems[s.name] = s # base_factor, destination_units c = ureg.get_base_units('inch', system=sysname) self.assertAlmostEqual(c[0], 0.326, places=3) self.assertEqual(c[1], {'pint': 1./3}) c = ureg.get_base_units('cm', system=sysname) self.assertAlmostEqual(c[0], 0.1283, places=3) self.assertEqual(c[1], {'pint': 1./3}) c = ureg.get_base_units('inch**2', system=sysname) self.assertAlmostEqual(c[0], 0.326**2, places=3) self.assertEqual(c[1], {'pint': 2./3}) c = ureg.get_base_units('cm**2', system=sysname) self.assertAlmostEqual(c[0], 0.1283**2, places=3) self.assertEqual(c[1], {'pint': 2./3}) def test_get_base_units_relation(self): sysname = 'mysys4' ureg = UnitRegistry() g = ureg.get_group('test-imperial') g.add_units('inch', 'yard', 'pint') lines = ['@system %s using test-imperial' % sysname, 'mph:meter', ] s = ureg.System.from_lines(lines, ureg.get_base_units) ureg._systems[s.name] = s # base_factor, destination_units c = ureg.get_base_units('inch', system=sysname) self.assertAlmostEqual(c[0], 0.056, places=2) self.assertEqual(c[1], {'mph': 1, 'second': 1}) c = ureg.get_base_units('kph', system=sysname) self.assertAlmostEqual(c[0], 0.6213, places=3) self.assertEqual(c[1], {'mph': 1}) def test_members_nowarning(self): ureg = self.ureg for name in dir(ureg.sys): s = dir(getattr(ureg.sys, name))pint-0.8.1/pint/testsuite/test_umath.py000066400000000000000000000644301311537545300202420ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import from pint.compat import np from pint.testsuite import QuantityTestCase, helpers # Following http://docs.scipy.org/doc/numpy/reference/ufuncs.html if np: pi = np.pi @helpers.requires_numpy() class TestUFuncs(QuantityTestCase): FORCE_NDARRAY = True @property def qless(self): return np.asarray([1., 2., 3., 4.]) * self.ureg.dimensionless @property def qs(self): return 8 * self.ureg.J @property def q1(self): return np.asarray([1., 2., 3., 4.]) * self.ureg.J @property def q2(self): return 2 * self.q1 @property def qm(self): return np.asarray([1., 2., 3., 4.]) * self.ureg.m @property def qi(self): return np.asarray([1 + 1j, 2 + 2j, 3 + 3j, 4 + 4j]) * self.ureg.m def assertRaisesMsg(self, msg, ExcType, func, *args, **kwargs): try: func(*args, **kwargs) self.assertFalse(True, msg='Exception {0} not raised {1}'.format(ExcType, msg)) except ExcType as e: pass except Exception as e: self.assertFalse(True, msg='{0} not raised but {1}\n{2}'.format(ExcType, e, msg)) def _test1(self, func, ok_with, raise_with=(), output_units='same', results=None, rtol=1e-6): """Test function that takes a single argument and returns Quantity. :param func: function callable. :param ok_with: iterables of values that work fine. :param raise_with: iterables of values that raise exceptions. :param output_units: units to be used when building results. 'same': ok_with[n].units (default). is float: ok_with[n].units ** output_units. None: no output units, the result should be an ndarray. Other value will be parsed as unit. :param results: iterable of results. If None, the result will be obtained by applying func to each ok_with value :param rtol: relative tolerance. """ if results is None: results = [None, ] * len(ok_with) for x1, res in zip(ok_with, results): err_msg = 'At {0} with {1}'.format(func.__name__, x1) if output_units == 'same': ou = x1.units elif isinstance(output_units, (int, float)): ou = x1.units ** output_units else: ou = output_units qm = func(x1) if res is None: res = func(x1.magnitude) if ou is not None: res = self.Q_(res, ou) self.assertQuantityAlmostEqual(qm, res, rtol=rtol, msg=err_msg) for x1 in raise_with: self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1), ValueError, func, x1) def _testn(self, func, ok_with, raise_with=(), results=None): """Test function that takes a single argument and returns and ndarray (not a Quantity) :param func: function callable. :param ok_with: iterables of values that work fine. :param raise_with: iterables of values that raise exceptions. :param results: iterable of results. If None, the result will be obtained by applying func to each ok_with value """ self._test1(func, ok_with, raise_with, output_units=None, results=results) def _test1_2o(self, func, ok_with, raise_with=(), output_units=('same', 'same'), results=None, rtol=1e-6): """Test functions that takes a single argument and return two Quantities. :param func: function callable. :param ok_with: iterables of values that work fine. :param raise_with: iterables of values that raise exceptions. :param output_units: tuple of units to be used when building the result tuple. 'same': ok_with[n].units (default). is float: ok_with[n].units ** output_units. None: no output units, the result should be an ndarray. Other value will be parsed as unit. :param results: iterable of results. If None, the result will be obtained by applying func to each ok_with value :param rtol: relative tolerance. """ if results is None: results = [None, ] * len(ok_with) for x1, res in zip(ok_with, results): err_msg = 'At {0} with {1}'.format(func.__name__, x1) qms = func(x1) if res is None: res = func(x1.magnitude) for ndx, (qm, re, ou) in enumerate(zip(qms, res, output_units)): if ou == 'same': ou = x1.units elif isinstance(ou, (int, float)): ou = x1.units ** ou if ou is not None: re = self.Q_(re, ou) self.assertQuantityAlmostEqual(qm, re, rtol=rtol, msg=err_msg) for x1 in raise_with: self.assertRaisesMsg('At {0} with {1}'.format(func.__name__, x1), ValueError, func, x1) def _test2(self, func, x1, ok_with, raise_with=(), output_units='same', rtol=1e-6, convert2=True): """Test function that takes two arguments and return a Quantity. :param func: function callable. :param x1: first argument of func. :param ok_with: iterables of values that work fine. :param raise_with: iterables of values that raise exceptions. :param output_units: units to be used when building results. 'same': x1.units (default). 'prod': x1.units * ok_with[n].units 'div': x1.units / ok_with[n].units 'second': x1.units * ok_with[n] None: no output units, the result should be an ndarray. Other value will be parsed as unit. :param rtol: relative tolerance. :param convert2: if the ok_with[n] should be converted to x1.units. """ for x2 in ok_with: err_msg = 'At {0} with {1} and {2}'.format(func.__name__, x1, x2) if output_units == 'same': ou = x1.units elif output_units == 'prod': ou = x1.units * x2.units elif output_units == 'div': ou = x1.units / x2.units elif output_units == 'second': ou = x1.units ** x2 else: ou = output_units qm = func(x1, x2) if convert2 and hasattr(x2, 'magnitude'): m2 = x2.to(getattr(x1, 'units', '')).magnitude else: m2 = getattr(x2, 'magnitude', x2) res = func(x1.magnitude, m2) if ou is not None: res = self.Q_(res, ou) self.assertQuantityAlmostEqual(qm, res, rtol=rtol, msg=err_msg) for x2 in raise_with: self.assertRaisesMsg('At {0} with {1} and {2}'.format(func.__name__, x1, x2), ValueError, func, x1, x2) def _testn2(self, func, x1, ok_with, raise_with=()): """Test function that takes two arguments and return a ndarray. :param func: function callable. :param x1: first argument of func. :param ok_with: iterables of values that work fine. :param raise_with: iterables of values that raise exceptions. """ self._test2(func, x1, ok_with, raise_with, output_units=None) @helpers.requires_numpy() class TestMathUfuncs(TestUFuncs): """Universal functions (ufunc) > Math operations http://docs.scipy.org/doc/numpy/reference/ufuncs.html#math-operations add(x1, x2[, out]) Add arguments element-wise. subtract(x1, x2[, out]) Subtract arguments, element-wise. multiply(x1, x2[, out]) Multiply arguments element-wise. divide(x1, x2[, out]) Divide arguments element-wise. logaddexp(x1, x2[, out]) Logarithm of the sum of exponentiations of the inputs. logaddexp2(x1, x2[, out]) Logarithm of the sum of exponentiations of the inputs in base-2. true_divide(x1, x2[, out]) Returns a true division of the inputs, element-wise. floor_divide(x1, x2[, out]) Return the largest integer smaller or equal to the division of the inputs. negative(x[, out]) Returns an array with the negative of each element of the original array. power(x1, x2[, out]) First array elements raised to powers from second array, element-wise. NOT IMPLEMENTED remainder(x1, x2[, out]) Return element-wise remainder of division. mod(x1, x2[, out]) Return element-wise remainder of division. fmod(x1, x2[, out]) Return the element-wise remainder of division. absolute(x[, out]) Calculate the absolute value element-wise. rint(x[, out]) Round elements of the array to the nearest integer. sign(x[, out]) Returns an element-wise indication of the sign of a number. conj(x[, out]) Return the complex conjugate, element-wise. exp(x[, out]) Calculate the exponential of all elements in the input array. exp2(x[, out]) Calculate 2**p for all p in the input array. log(x[, out]) Natural logarithm, element-wise. log2(x[, out]) Base-2 logarithm of x. log10(x[, out]) Return the base 10 logarithm of the input array, element-wise. expm1(x[, out]) Calculate exp(x) - 1 for all elements in the array. log1p(x[, out]) Return the natural logarithm of one plus the input array, element-wise. sqrt(x[, out]) Return the positive square-root of an array, element-wise. square(x[, out]) Return the element-wise square of the input. reciprocal(x[, out]) Return the reciprocal of the argument, element-wise. ones_like(x[, out]) Returns an array of ones with the same shape and type as a given array. """ def test_add(self): self._test2(np.add, self.q1, (self.q2, self.qs), (self.qm, )) def test_subtract(self): self._test2(np.subtract, self.q1, (self.q2, self.qs), (self.qm, )) def test_multiply(self): self._test2(np.multiply, self.q1, (self.q2, self.qs), (), 'prod') def test_divide(self): self._test2(np.divide, self.q1, (self.q2, self.qs, self.qless), (), 'div', convert2=False) def test_logaddexp(self): self._test2(np.logaddexp, self.qless, (self.qless, ), (self.q1, ), '') def test_logaddexp2(self): self._test2(np.logaddexp2, self.qless, (self.qless, ), (self.q1, ), 'div') def test_true_divide(self): self._test2(np.true_divide, self.q1, (self.q2, self.qs, self.qless), (), 'div', convert2=False) def test_floor_divide(self): self._test2(np.floor_divide, self.q1, (self.q2, self.qs, self.qless), (), 'div', convert2=False) def test_negative(self): self._test1(np.negative, (self.qless, self.q1), ()) def test_remainder(self): self._test2(np.remainder, self.q1, (self.q2, self.qs, self.qless), (), 'same', convert2=False) def test_mod(self): self._test2(np.mod, self.q1, (self.q2, self.qs, self.qless), (), 'same', convert2=False) def test_fmod(self): self._test2(np.fmod, self.q1, (self.q2, self.qs, self.qless), (), 'same', convert2=False) def test_absolute(self): self._test1(np.absolute, (self.q2, self.qs, self.qless, self.qi), (), 'same') def test_rint(self): self._test1(np.rint, (self.q2, self.qs, self.qless, self.qi), (), 'same') def test_conj(self): self._test1(np.conj, (self.q2, self.qs, self.qless, self.qi), (), 'same') def test_exp(self): self._test1(np.exp, (self.qless, ), (self.q1, ), '') def test_exp2(self): self._test1(np.exp2, (self.qless,), (self.q1, ), '') def test_log(self): self._test1(np.log, (self.qless,), (self.q1, ), '') def test_log2(self): self._test1(np.log2, (self.qless,), (self.q1, ), '') def test_log10(self): self._test1(np.log10, (self.qless,), (self.q1, ), '') def test_expm1(self): self._test1(np.expm1, (self.qless,), (self.q1, ), '') def test_sqrt(self): self._test1(np.sqrt, (self.q2, self.qs, self.qless, self.qi), (), 0.5) def test_square(self): self._test1(np.square, (self.q2, self.qs, self.qless, self.qi), (), 2) def test_reciprocal(self): self._test1(np.reciprocal, (self.q2, self.qs, self.qless, self.qi), (), -1) @helpers.requires_numpy() class TestTrigUfuncs(TestUFuncs): """Universal functions (ufunc) > Trigonometric functions http://docs.scipy.org/doc/numpy/reference/ufuncs.html#trigonometric-functions sin(x[, out]) Trigonometric sine, element-wise. cos(x[, out]) Cosine elementwise. tan(x[, out]) Compute tangent element-wise. arcsin(x[, out]) Inverse sine, element-wise. arccos(x[, out]) Trigonometric inverse cosine, element-wise. arctan(x[, out]) Trigonometric inverse tangent, element-wise. arctan2(x1, x2[, out]) Element-wise arc tangent of x1/x2 choosing the quadrant correctly. hypot(x1, x2[, out]) Given the “legs” of a right triangle, return its hypotenuse. sinh(x[, out]) Hyperbolic sine, element-wise. cosh(x[, out]) Hyperbolic cosine, element-wise. tanh(x[, out]) Compute hyperbolic tangent element-wise. arcsinh(x[, out]) Inverse hyperbolic sine elementwise. arccosh(x[, out]) Inverse hyperbolic cosine, elementwise. arctanh(x[, out]) Inverse hyperbolic tangent elementwise. deg2rad(x[, out]) Convert angles from degrees to radians. rad2deg(x[, out]) Convert angles from radians to degrees. """ def test_sin(self): self._test1(np.sin, (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless, np.arange(0, pi/2, pi/4) * self.ureg.radian, np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m ), (1*self.ureg.m, ), '', results=(None, None, np.sin(np.arange(0, pi/2, pi/4)*0.001))) self._test1(np.sin, (np.rad2deg(np.arange(0, pi/2, pi/4)) * self.ureg.degrees, ), results=(np.sin(np.arange(0, pi/2, pi/4)), )) def test_cos(self): self._test1(np.cos, (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless, np.arange(0, pi/2, pi/4) * self.ureg.radian, np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m, ), (1*self.ureg.m, ), '', results=(None, None, np.cos(np.arange(0, pi/2, pi/4)*0.001), ) ) self._test1(np.cos, (np.rad2deg(np.arange(0, pi/2, pi/4)) * self.ureg.degrees, ), results=(np.cos(np.arange(0, pi/2, pi/4)), ) ) def test_tan(self): self._test1(np.tan, (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless, np.arange(0, pi/2, pi/4) * self.ureg.radian, np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m ), (1*self.ureg.m, ), '', results=(None, None, np.tan(np.arange(0, pi/2, pi/4)*0.001))) self._test1(np.tan, (np.rad2deg(np.arange(0, pi/2, pi/4)) * self.ureg.degrees, ), results=(np.tan(np.arange(0, pi/2, pi/4)), )) def test_arcsin(self): self._test1(np.arcsin, (np.arange(0, .9, .1) * self.ureg.dimensionless, np.arange(0, .9, .1) * self.ureg.m / self.ureg.m ), (1*self.ureg.m, ), 'radian') def test_arccos(self): x = np.arange(0, .9, .1) * self.ureg.m self._test1(np.arccos, (np.arange(0, .9, .1) * self.ureg.dimensionless, np.arange(0, .9, .1) * self.ureg.m / self.ureg.m ), (1*self.ureg.m, ), 'radian') def test_arctan(self): self._test1(np.arctan, (np.arange(0, .9, .1) * self.ureg.dimensionless, np.arange(0, .9, .1) * self.ureg.m / self.ureg.m ), (1*self.ureg.m, ), 'radian') def test_arctan2(self): m = self.ureg.m j = self.ureg.J km = self.ureg.km self._test2(np.arctan2, np.arange(0, .9, .1) * m, (np.arange(0, .9, .1) * m, np.arange(.9, 0., -.1) * m, np.arange(0, .9, .1) * km, np.arange(.9, 0., -.1) * km, ), raise_with=np.arange(0, .9, .1) * j, output_units='radian') def test_hypot(self): self.assertTrue(np.hypot(3. * self.ureg.m, 4. * self.ureg.m) == 5. * self.ureg.m) self.assertTrue(np.hypot(3. * self.ureg.m, 400. * self.ureg.cm) == 5. * self.ureg.m) self.assertRaises(ValueError, np.hypot, 1. * self.ureg.m, 2. * self.ureg.J) def test_sinh(self): self._test1(np.sinh, (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless, np.arange(0, pi/2, pi/4) * self.ureg.radian, np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m ), (1*self.ureg.m, ), '', results=(None, None, np.sinh(np.arange(0, pi/2, pi/4)*0.001))) self._test1(np.sinh, (np.rad2deg(np.arange(0, pi/2, pi/4)) * self.ureg.degrees, ), results=(np.sinh(np.arange(0, pi/2, pi/4)), )) def test_cosh(self): self._test1(np.cosh, (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless, np.arange(0, pi/2, pi/4) * self.ureg.radian, np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m ), (1*self.ureg.m, ), '', results=(None, None, np.cosh(np.arange(0, pi/2, pi/4)*0.001))) self._test1(np.cosh, (np.rad2deg(np.arange(0, pi/2, pi/4)) * self.ureg.degrees, ), results=(np.cosh(np.arange(0, pi/2, pi/4)), )) def test_tanh(self): self._test1(np.tanh, (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless, np.arange(0, pi/2, pi/4) * self.ureg.radian, np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m ), (1*self.ureg.m, ), '', results=(None, None, np.tanh(np.arange(0, pi/2, pi/4)*0.001))) self._test1(np.tanh, (np.rad2deg(np.arange(0, pi/2, pi/4)) * self.ureg.degrees, ), results=(np.tanh(np.arange(0, pi/2, pi/4)), )) def test_arcsinh(self): self._test1(np.arcsinh, (np.arange(0, .9, .1) * self.ureg.dimensionless, np.arange(0, .9, .1) * self.ureg.m / self.ureg.m ), (1*self.ureg.m, ), 'radian') def test_arccosh(self): self._test1(np.arccosh, (np.arange(1., 1.9, .1) * self.ureg.dimensionless, np.arange(1., 1.9, .1) * self.ureg.m / self.ureg.m ), (1*self.ureg.m, ), 'radian') def test_arctanh(self): self._test1(np.arctanh, (np.arange(0, .9, .1) * self.ureg.dimensionless, np.arange(0, .9, .1) * self.ureg.m / self.ureg.m ), (.1 * self.ureg.m, ), 'radian') def test_deg2rad(self): self._test1(np.deg2rad, (np.arange(0, pi/2, pi/4) * self.ureg.degrees, ), (1*self.ureg.m, ), 'radians') def test_rad2deg(self): self._test1(np.rad2deg, (np.arange(0, pi/2, pi/4) * self.ureg.dimensionless, np.arange(0, pi/2, pi/4) * self.ureg.radian, np.arange(0, pi/2, pi/4) * self.ureg.mm / self.ureg.m, ), (1*self.ureg.m, ), 'degree', results=(None, None, np.rad2deg(np.arange(0, pi/2, pi/4)*0.001) * self.ureg.degree, )) class TestComparisonUfuncs(TestUFuncs): """Universal functions (ufunc) > Comparison functions http://docs.scipy.org/doc/numpy/reference/ufuncs.html#comparison-functions greater(x1, x2[, out]) Return the truth value of (x1 > x2) element-wise. greater_equal(x1, x2[, out]) Return the truth value of (x1 >= x2) element-wise. less(x1, x2[, out]) Return the truth value of (x1 < x2) element-wise. less_equal(x1, x2[, out]) Return the truth value of (x1 =< x2) element-wise. not_equal(x1, x2[, out]) Return (x1 != x2) element-wise. equal(x1, x2[, out]) Return (x1 == x2) element-wise. """ def test_greater(self): self._testn2(np.greater, self.q1, (self.q2, ), (self.qm, )) def test_greater_equal(self): self._testn2(np.greater_equal, self.q1, (self.q2, ), (self.qm, )) def test_less(self): self._testn2(np.less, self.q1, (self.q2, ), (self.qm, )) def test_less_equal(self): self._testn2(np.less_equal, self.q1, (self.q2, ), (self.qm, )) def test_not_equal(self): self._testn2(np.not_equal, self.q1, (self.q2, ), (self.qm, )) def test_equal(self): self._testn2(np.equal, self.q1, (self.q2, ), (self.qm, )) class TestFloatingUfuncs(TestUFuncs): """Universal functions (ufunc) > Floating functions http://docs.scipy.org/doc/numpy/reference/ufuncs.html#floating-functions isreal(x) Returns a bool array, where True if input element is real. iscomplex(x) Returns a bool array, where True if input element is complex. isfinite(x[, out]) Test element-wise for finite-ness (not infinity or not Not a Number). isinf(x[, out]) Test element-wise for positive or negative infinity. isnan(x[, out]) Test element-wise for Not a Number (NaN), return result as a bool array. signbit(x[, out]) Returns element-wise True where signbit is set (less than zero). copysign(x1, x2[, out]) Change the sign of x1 to that of x2, element-wise. nextafter(x1, x2[, out]) Return the next representable floating-point value after x1 in the direction of x2 element-wise. modf(x[, out1, out2]) Return the fractional and integral parts of an array, element-wise. ldexp(x1, x2[, out]) Compute y = x1 * 2**x2. frexp(x[, out1, out2]) Split the number, x, into a normalized fraction (y1) and exponent (y2) fmod(x1, x2[, out]) Return the element-wise remainder of division. floor(x[, out]) Return the floor of the input, element-wise. ceil(x[, out]) Return the ceiling of the input, element-wise. trunc(x[, out]) Return the truncated value of the input, element-wise. """ def test_isreal(self): self._testn(np.isreal, (self.q1, self.qm, self.qless)) def test_iscomplex(self): self._testn(np.iscomplex, (self.q1, self.qm, self.qless)) def test_isfinite(self): self._testn(np.isreal, (self.q1, self.qm, self.qless)) def test_isinf(self): self._testn(np.isinf, (self.q1, self.qm, self.qless)) def test_isnan(self): self._testn(np.isnan, (self.q1, self.qm, self.qless)) def test_signbit(self): self._testn(np.signbit, (self.q1, self.qm, self.qless)) def test_copysign(self): self._test2(np.copysign, self.q1, (self.q2, self.qs), (self.qm, )) def test_nextafter(self): self._test2(np.nextafter, self.q1, (self.q2, self.qs), (self.qm, )) def test_modf(self): self._test1_2o(np.modf, (self.q2, self.qs), ) def test_ldexp(self): x1, x2 = np.frexp(self.q2) self._test2(np.ldexp, x1, (x2, )) def test_frexp(self): self._test1_2o(np.frexp, (self.q2, self.qs), output_units=('same', None)) def test_fmod(self): # See TestMathUfuncs.test_fmod pass def test_floor(self): self._test1(np.floor, (self.q1, self.qm, self.qless)) def test_ceil(self): self._test1(np.ceil, (self.q1, self.qm, self.qless)) def test_trunc(self): self._test1(np.trunc, (self.q1, self.qm, self.qless)) pint-0.8.1/pint/testsuite/test_unit.py000066400000000000000000000620161311537545300201010ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import copy import math from pint.registry import (UnitRegistry, LazyRegistry) from pint.util import (UnitsContainer, ParserHelper) from pint import DimensionalityError, UndefinedUnitError from pint.compat import u, np, string_types from pint.testsuite import QuantityTestCase, helpers from pint.testsuite.parameterized import ParameterizedTestCase class TestUnit(QuantityTestCase): def test_creation(self): for arg in ('meter', UnitsContainer(meter=1), self.U_('m')): self.assertEqual(self.U_(arg)._units, UnitsContainer(meter=1)) self.assertRaises(TypeError, self.U_, 1) def test_deepcopy(self): x = self.U_(UnitsContainer(meter=1)) self.assertEqual(x, copy.deepcopy(x)) def test_unit_repr(self): x = self.U_(UnitsContainer(meter=1)) self.assertEqual(str(x), 'meter') self.assertEqual(repr(x), "") def test_unit_formatting(self): x = self.U_(UnitsContainer(meter=2, kilogram=1, second=-1)) for spec, result in (('{0}', str(x)), ('{0!s}', str(x)), ('{0!r}', repr(x)), ('{0:L}', r'\frac{\mathrm{kilogram} \cdot \mathrm{meter}^{2}}{\mathrm{second}}'), ('{0:P}', 'kilogram·meter²/second'), ('{0:H}', 'kilogram meter2/second'), ('{0:C}', 'kilogram*meter**2/second'), ('{0:Lx}', r'\si[]{\kilo\gram\meter\squared\per\second}'), ('{0:~}', 'kg * m ** 2 / s'), ('{0:L~}', r'\frac{\mathrm{kg} \cdot \mathrm{m}^{2}}{\mathrm{s}}'), ('{0:P~}', 'kg·m²/s'), ('{0:H~}', 'kg m2/s'), ('{0:C~}', 'kg*m**2/s'), ): self.assertEqual(spec.format(x), result) def test_unit_default_formatting(self): ureg = UnitRegistry() x = ureg.Unit(UnitsContainer(meter=2, kilogram=1, second=-1)) for spec, result in (('L', r'\frac{\mathrm{kilogram} \cdot \mathrm{meter}^{2}}{\mathrm{second}}'), ('P', 'kilogram·meter²/second'), ('H', 'kilogram meter2/second'), ('C', 'kilogram*meter**2/second'), ('~', 'kg * m ** 2 / s'), ('L~', r'\frac{\mathrm{kg} \cdot \mathrm{m}^{2}}{\mathrm{s}}'), ('P~', 'kg·m²/s'), ('H~', 'kg m2/s'), ('C~', 'kg*m**2/s'), ): ureg.default_format = spec self.assertEqual('{0}'.format(x), result, 'Failed for {0}, {1}'.format(spec, result)) def test_unit_mul(self): x = self.U_('m') self.assertEqual(x*1, self.Q_(1, 'm')) self.assertEqual(x*0.5, self.Q_(0.5, 'm')) self.assertEqual(x*self.Q_(1, 'm'), self.Q_(1, 'm**2')) self.assertEqual(1*x, self.Q_(1, 'm')) def test_unit_div(self): x = self.U_('m') self.assertEqual(x/1, self.Q_(1, 'm')) self.assertEqual(x/0.5, self.Q_(2.0, 'm')) self.assertEqual(x/self.Q_(1, 'm'), self.Q_(1)) def test_unit_rdiv(self): x = self.U_('m') self.assertEqual(1/x, self.Q_(1, '1/m')) def test_unit_pow(self): x = self.U_('m') self.assertEqual(x**2, self.U_('m**2')) def test_unit_hash(self): x = self.U_('m') self.assertEqual(hash(x), hash(x._units)) def test_unit_eqs(self): x = self.U_('m') self.assertEqual(x, self.U_('m')) self.assertNotEqual(x, self.U_('cm')) self.assertEqual(x, self.Q_(1, 'm')) self.assertNotEqual(x, self.Q_(2, 'm')) self.assertEqual(x, UnitsContainer({'meter': 1})) y = self.U_('cm/m') self.assertEqual(y, 0.01) self.assertEqual(self.U_('byte') == self.U_('byte'), True) self.assertEqual(self.U_('byte') != self.U_('byte'), False) def test_unit_cmp(self): x = self.U_('m') self.assertLess(x, self.U_('km')) self.assertGreater(x, self.U_('mm')) y = self.U_('m/mm') self.assertGreater(y, 1) self.assertLess(y, 1e6) def test_dimensionality(self): x = self.U_('m') self.assertEqual(x.dimensionality, UnitsContainer({'[length]': 1})) def test_dimensionless(self): self.assertTrue(self.U_('m/mm').dimensionless) self.assertFalse(self.U_('m').dimensionless) def test_unit_casting(self): self.assertEqual(int(self.U_('m/mm')), 1000) self.assertEqual(float(self.U_('mm/m')), 1e-3) self.assertEqual(complex(self.U_('mm/mm')), 1+0j) @helpers.requires_numpy() def test_array_interface(self): import numpy as np x = self.U_('m') arr = np.ones(10) self.assertQuantityEqual(arr*x, self.Q_(arr, 'm')) self.assertQuantityEqual(arr/x, self.Q_(arr, '1/m')) self.assertQuantityEqual(x/arr, self.Q_(arr, 'm')) class TestRegistry(QuantityTestCase): FORCE_NDARRAY = False def setup(self): self.ureg.autoconvert_offset_to_baseunit = False def test_base(self): ureg = UnitRegistry(None) ureg.define('meter = [length]') self.assertRaises(ValueError, ureg.define, 'meter = [length]') self.assertRaises(TypeError, ureg.define, list()) x = ureg.define('degC = kelvin; offset: 273.15') def test_define(self): ureg = UnitRegistry(None) self.assertIsInstance(dir(ureg), list) self.assertGreater(len(dir(ureg)), 0) def test_load(self): import pkg_resources from pint import unit data = pkg_resources.resource_filename(unit.__name__, 'default_en.txt') ureg1 = UnitRegistry() ureg2 = UnitRegistry(data) self.assertEqual(dir(ureg1), dir(ureg2)) self.assertRaises(ValueError, UnitRegistry(None).load_definitions, 'notexisting') def test_default_format(self): ureg = UnitRegistry() q = ureg.meter s1 = '{0}'.format(q) s2 = '{0:~}'.format(q) ureg.default_format = '~' s3 = '{0}'.format(q) self.assertEqual(s2, s3) self.assertNotEqual(s1, s3) self.assertEqual(ureg.default_format, '~') def test_parse_number(self): self.assertEqual(self.ureg.parse_expression('pi'), math.pi) self.assertEqual(self.ureg.parse_expression('x', x=2), 2) self.assertEqual(self.ureg.parse_expression('x', x=2.3), 2.3) self.assertEqual(self.ureg.parse_expression('x * y', x=2.3, y=3), 2.3 * 3) self.assertEqual(self.ureg.parse_expression('x', x=(1+1j)), (1+1j)) def test_parse_single(self): self.assertEqual(self.ureg.parse_expression('meter'), self.Q_(1, UnitsContainer(meter=1.))) self.assertEqual(self.ureg.parse_expression('second'), self.Q_(1, UnitsContainer(second=1.))) def test_parse_alias(self): self.assertEqual(self.ureg.parse_expression('metre'), self.Q_(1, UnitsContainer(meter=1.))) def test_parse_plural(self): self.assertEqual(self.ureg.parse_expression('meters'), self.Q_(1, UnitsContainer(meter=1.))) def test_parse_prefix(self): self.assertEqual(self.ureg.parse_expression('kilometer'), self.Q_(1, UnitsContainer(kilometer=1.))) #self.assertEqual(self.ureg._units['kilometer'], self.Q_(1000., UnitsContainer(meter=1.))) def test_parse_complex(self): self.assertEqual(self.ureg.parse_expression('kilometre'), self.Q_(1, UnitsContainer(kilometer=1.))) self.assertEqual(self.ureg.parse_expression('kilometres'), self.Q_(1, UnitsContainer(kilometer=1.))) def test_str_errors(self): self.assertEqual(str(UndefinedUnitError('rabbits')), "'{0!s}' is not defined in the unit registry".format('rabbits')) self.assertEqual(str(UndefinedUnitError(('rabbits', 'horses'))), "'{0!s}' are not defined in the unit registry".format(('rabbits', 'horses'))) self.assertEqual(u(str(DimensionalityError('meter', 'second'))), "Cannot convert from 'meter' to 'second'") self.assertEqual(str(DimensionalityError('meter', 'second', 'length', 'time')), "Cannot convert from 'meter' (length) to 'second' (time)") def test_parse_mul_div(self): self.assertEqual(self.ureg.parse_expression('meter*meter'), self.Q_(1, UnitsContainer(meter=2.))) self.assertEqual(self.ureg.parse_expression('meter**2'), self.Q_(1, UnitsContainer(meter=2.))) self.assertEqual(self.ureg.parse_expression('meter*second'), self.Q_(1, UnitsContainer(meter=1., second=1))) self.assertEqual(self.ureg.parse_expression('meter/second'), self.Q_(1, UnitsContainer(meter=1., second=-1))) self.assertEqual(self.ureg.parse_expression('meter/second**2'), self.Q_(1, UnitsContainer(meter=1., second=-2))) def test_parse_pretty(self): self.assertEqual(self.ureg.parse_expression('meter/second²'), self.Q_(1, UnitsContainer(meter=1., second=-2))) self.assertEqual(self.ureg.parse_expression('m³/s³'), self.Q_(1, UnitsContainer(meter=3., second=-3))) self.assertEqual(self.ureg.parse_expression('meter² · second'), self.Q_(1, UnitsContainer(meter=2., second=1))) self.assertEqual(self.ureg.parse_expression('meter⁰.⁵·second'), self.Q_(1, UnitsContainer(meter=0.5, second=1))) self.assertEqual(self.ureg.parse_expression('meter³⁷/second⁴.³²¹'), self.Q_(1, UnitsContainer(meter=37, second=-4.321))) def test_parse_factor(self): self.assertEqual(self.ureg.parse_expression('42*meter'), self.Q_(42, UnitsContainer(meter=1.))) self.assertEqual(self.ureg.parse_expression('meter*42'), self.Q_(42, UnitsContainer(meter=1.))) def test_rep_and_parse(self): q = self.Q_(1, 'g/(m**2*s)') self.assertEqual(self.Q_(q.magnitude, str(q.units)), q) def test_as_delta(self): parse = self.ureg.parse_units self.assertEqual(parse('kelvin', as_delta=True), UnitsContainer(kelvin=1)) self.assertEqual(parse('kelvin', as_delta=False), UnitsContainer(kelvin=1)) self.assertEqual(parse('kelvin**(-1)', as_delta=True), UnitsContainer(kelvin=-1)) self.assertEqual(parse('kelvin**(-1)', as_delta=False), UnitsContainer(kelvin=-1)) self.assertEqual(parse('kelvin**2', as_delta=True), UnitsContainer(kelvin=2)) self.assertEqual(parse('kelvin**2', as_delta=False), UnitsContainer(kelvin=2)) self.assertEqual(parse('kelvin*meter', as_delta=True), UnitsContainer(kelvin=1, meter= 1)) self.assertEqual(parse('kelvin*meter', as_delta=False), UnitsContainer(kelvin=1, meter=1)) def test_name(self): self.assertRaises(UndefinedUnitError, self.ureg.get_name, 'asdf') def test_symbol(self): self.assertRaises(UndefinedUnitError, self.ureg.get_symbol, 'asdf') self.assertEqual(self.ureg.get_symbol('meter'), 'm') self.assertEqual(self.ureg.get_symbol('second'), 's') self.assertEqual(self.ureg.get_symbol('hertz'), 'Hz') self.assertEqual(self.ureg.get_symbol('kilometer'), 'km') self.assertEqual(self.ureg.get_symbol('megahertz'), 'MHz') self.assertEqual(self.ureg.get_symbol('millisecond'), 'ms') def test_imperial_symbol(self): self.assertEqual(self.ureg.get_symbol('inch'), 'in') self.assertEqual(self.ureg.get_symbol('foot'), 'ft') self.assertEqual(self.ureg.get_symbol('inches'), 'in') self.assertEqual(self.ureg.get_symbol('feet'), 'ft') self.assertEqual(self.ureg.get_symbol('international_foot'), 'ft') self.assertEqual(self.ureg.get_symbol('international_inch'), 'in') def test_pint(self): p = self.ureg.pint l = self.ureg.liter ip = self.ureg.imperial_pint self.assertLess(p, l) self.assertLess(p, ip) def test_wraps(self): def func(x): return x ureg = self.ureg f0 = ureg.wraps(None, [None, ])(func) self.assertEqual(f0(3.), 3.) f0 = ureg.wraps(None, None, )(func) self.assertEqual(f0(3.), 3.) f1 = ureg.wraps(None, ['meter', ])(func) self.assertRaises(ValueError, f1, 3.) self.assertEqual(f1(3. * ureg.centimeter), 0.03) self.assertEqual(f1(3. * ureg.meter), 3.) self.assertRaises(ValueError, f1, 3 * ureg.second) f1b = ureg.wraps(None, [ureg.meter, ])(func) self.assertRaises(ValueError, f1b, 3.) self.assertEqual(f1b(3. * ureg.centimeter), 0.03) self.assertEqual(f1b(3. * ureg.meter), 3.) self.assertRaises(ValueError, f1b, 3 * ureg.second) f1 = ureg.wraps(None, 'meter')(func) self.assertRaises(ValueError, f1, 3.) self.assertEqual(f1(3. * ureg.centimeter), 0.03) self.assertEqual(f1(3. * ureg.meter), 3.) self.assertRaises(ValueError, f1, 3 * ureg.second) f2 = ureg.wraps('centimeter', ['meter', ])(func) self.assertRaises(ValueError, f2, 3.) self.assertEqual(f2(3. * ureg.centimeter), 0.03 * ureg.centimeter) self.assertEqual(f2(3. * ureg.meter), 3 * ureg.centimeter) f3 = ureg.wraps('centimeter', ['meter', ], strict=False)(func) self.assertEqual(f3(3), 3 * ureg.centimeter) self.assertEqual(f3(3. * ureg.centimeter), 0.03 * ureg.centimeter) self.assertEqual(f3(3. * ureg.meter), 3. * ureg.centimeter) def gfunc(x, y): return x + y g0 = ureg.wraps(None, [None, None])(gfunc) self.assertEqual(g0(3, 1), 4) g1 = ureg.wraps(None, ['meter', 'centimeter'])(gfunc) self.assertRaises(ValueError, g1, 3 * ureg.meter, 1) self.assertEqual(g1(3 * ureg.meter, 1 * ureg.centimeter), 4) self.assertEqual(g1(3 * ureg.meter, 1 * ureg.meter), 3 + 100) def hfunc(x, y): return x, y h0 = ureg.wraps(None, [None, None])(hfunc) self.assertEqual(h0(3, 1), (3, 1)) h1 = ureg.wraps(['meter', 'centimeter'], [None, None])(hfunc) self.assertEqual(h1(3, 1), [3 * ureg.meter, 1 * ureg.cm]) h2 = ureg.wraps(('meter', 'centimeter'), [None, None])(hfunc) self.assertEqual(h2(3, 1), (3 * ureg.meter, 1 * ureg.cm)) h3 = ureg.wraps((None,), (None, None))(hfunc) self.assertEqual(h3(3, 1), (3, 1)) def test_wrap_referencing(self): ureg = self.ureg def gfunc(x, y): return x + y def gfunc2(x, y): return x ** 2 + y def gfunc3(x, y): return x ** 2 * y rst = 3. * ureg.meter + 1. * ureg.centimeter g0 = ureg.wraps('=A', ['=A', '=A'])(gfunc) self.assertEqual(g0(3. * ureg.meter, 1. * ureg.centimeter), rst.to('meter')) self.assertEqual(g0(3, 1), 4) g1 = ureg.wraps('=A', ['=A', '=A'])(gfunc) self.assertEqual(g1(3. * ureg.meter, 1. * ureg.centimeter), rst.to('centimeter')) g2 = ureg.wraps('=A', ['=A', '=A'])(gfunc) self.assertEqual(g2(3. * ureg.meter, 1. * ureg.centimeter), rst.to('meter')) g3 = ureg.wraps('=A**2', ['=A', '=A**2'])(gfunc2) a = 3. * ureg.meter b = (2. * ureg.centimeter) ** 2 self.assertEqual(g3(a, b), gfunc2(a, b)) self.assertEqual(g3(3, 2), gfunc2(3, 2)) g4 = ureg.wraps('=A**2 * B', ['=A', '=B'])(gfunc3) self.assertEqual(g4(3. * ureg.meter, 2. * ureg.second), ureg('(3*meter)**2 * 2 *second')) self.assertEqual(g4(3. * ureg.meter, 2.), ureg('(3*meter)**2 * 2')) self.assertEqual(g4(3., 2. * ureg.second), ureg('3**2 * 2 * second')) def test_check(self): def func(x): return x ureg = self.ureg f0 = ureg.check('[length]')(func) self.assertRaises(AttributeError, f0, 3.) self.assertEqual(f0(3. * ureg.centimeter), 0.03 * ureg.meter) self.assertRaises(DimensionalityError, f0, 3. * ureg.kilogram) f0b = ureg.check(ureg.meter)(func) self.assertRaises(AttributeError, f0b, 3.) self.assertEqual(f0b(3. * ureg.centimeter), 0.03 * ureg.meter) self.assertRaises(DimensionalityError, f0b, 3. * ureg.kilogram) def gfunc(x, y): return x / y g0 = ureg.check(None, None)(gfunc) self.assertEqual(g0(6, 2), 3) self.assertEqual(g0(6 * ureg.parsec, 2), 3 * ureg.parsec) g1 = ureg.check('[speed]', '[time]')(gfunc) self.assertRaises(AttributeError, g1, 3.0, 1) self.assertRaises(DimensionalityError, g1, 1 * ureg.parsec, 1 * ureg.angstrom) self.assertRaises(TypeError, g1, 1 * ureg.km / ureg.hour, 1 * ureg.hour, 3.0) self.assertEqual(g1(3.6 * ureg.km / ureg.hour, 1 * ureg.second), 1 * ureg.meter / ureg.second ** 2) g2 = ureg.check('[speed]')(gfunc) self.assertRaises(AttributeError, g2, 3.0, 1) self.assertRaises(DimensionalityError, g2, 2 * ureg.parsec) self.assertRaises(DimensionalityError, g2, 2 * ureg.parsec, 1.0) self.assertEqual(g2(2.0 * ureg.km / ureg.hour, 2), 1 * ureg.km / ureg.hour) g3 = ureg.check('[speed]', '[time]', '[mass]')(gfunc) self.assertRaises(DimensionalityError, g3, 1 * ureg.parsec, 1 * ureg.angstrom) self.assertRaises(DimensionalityError, g3, 1 * ureg.parsec, 1 * ureg.angstrom, 1 * ureg.kilogram) def test_to_ref_vs_to(self): self.ureg.autoconvert_offset_to_baseunit = True q = 8. * self.ureg.inch t = 8. * self.ureg.degF dt = 8. * self.ureg.delta_degF self.assertEqual(q.to('yard').magnitude, self.ureg._units['inch'].converter.to_reference(8.)) self.assertEqual(t.to('kelvin').magnitude, self.ureg._units['degF'].converter.to_reference(8.)) self.assertEqual(dt.to('kelvin').magnitude, self.ureg._units['delta_degF'].converter.to_reference(8.)) def test_redefinition(self): d = UnitRegistry().define with self.capture_log() as buffer: d('meter = [fruits]') d('kilo- = 1000') d('[speed] = [vegetables]') # aliases d('bla = 3.2 meter = inch') d('myk- = 1000 = kilo-') self.assertEqual(len(buffer), 5) def test_convert_parse_str(self): ureg = self.ureg self.assertEqual(ureg.convert(1, 'meter', 'inch'), ureg.convert(1, UnitsContainer(meter=1), UnitsContainer(inch=1))) @helpers.requires_numpy() def test_convert_inplace(self): ureg = self.ureg # Conversions with single units take a different codepath than # Conversions with more than one unit. src_dst1 = UnitsContainer(meter=1), UnitsContainer(inch=1) src_dst2 = UnitsContainer(meter=1, second=-1), UnitsContainer(inch=1, minute=-1) for src, dst in (src_dst1, src_dst2): v = ureg.convert(1, src, dst), a = np.ones((3, 1)) ac = np.ones((3, 1)) r1 = ureg.convert(a, src, dst) np.testing.assert_allclose(r1, v * ac) self.assertIsNot(r1, a) r2 = ureg.convert(a, src, dst, inplace=True) np.testing.assert_allclose(r2, v * ac) self.assertIs(r2, a) def test_repeated_convert(self): # Because of caching, repeated conversions were failing. self.ureg.convert(1, "m", "ft") self.ureg.convert(1, "m", "ft") def test_singular_SI_prefix_convert(self): # Fix for issue 156 self.ureg.convert(1, 'mm', 'm') self.ureg.convert(1, 'ms', 's') self.ureg.convert(1, 'm', 'mm') self.ureg.convert(1, 's', 'ms') def test_parse_units(self): ureg = self.ureg self.assertEqual(ureg.parse_units(''), ureg.Unit('')) self.assertRaises(ValueError, ureg.parse_units, '2 * meter') class TestCompatibleUnits(QuantityTestCase): FORCE_NDARRAY= False def _test(self, input_units): gd = self.ureg.get_dimensionality dim = gd(input_units) equiv = self.ureg.get_compatible_units(input_units) for eq in equiv: self.assertEqual(gd(eq), dim) self.assertEqual(equiv, self.ureg.get_compatible_units(dim)) def _test2(self, units1, units2): equiv1 = self.ureg.get_compatible_units(units1) equiv2 = self.ureg.get_compatible_units(units2) self.assertEqual(equiv1, equiv2) def test_many(self): self._test(self.ureg.meter) self._test(self.ureg.seconds) self._test(self.ureg.newton) self._test(self.ureg.kelvin) def test_context_sp(self): gd = self.ureg.get_dimensionality # length, frequency, energy valid = [gd(self.ureg.meter), gd(self.ureg.hertz), gd(self.ureg.joule), 1/gd(self.ureg.meter)] with self.ureg.context('sp'): equiv = self.ureg.get_compatible_units(self.ureg.meter) result = set() for eq in equiv: dim = gd(eq) result.add(dim) self.assertIn(dim, valid) self.assertEqual(len(result), len(valid)) def test_get_base_units(self): ureg = UnitRegistry() self.assertEqual(ureg.get_base_units(''), (1, ureg.Unit(''))) self.assertEqual(ureg.get_base_units('meter'), ureg.get_base_units(ParserHelper(meter=1))) def test_get_compatible_units(self): ureg = UnitRegistry() self.assertEqual(ureg.get_compatible_units(''), frozenset()) self.assertEqual(ureg.get_compatible_units('meter'), ureg.get_compatible_units(ParserHelper(meter=1))) class TestRegistryWithDefaultRegistry(TestRegistry): @classmethod def setUpClass(cls): from pint import _DEFAULT_REGISTRY cls.ureg = _DEFAULT_REGISTRY cls.Q_ = cls.ureg.Quantity def test_lazy(self): x = LazyRegistry() x.test = 'test' self.assertIsInstance(x, UnitRegistry) y = LazyRegistry() q = y('meter') self.assertIsInstance(y, UnitRegistry) def test_redefinition(self): d = self.ureg.define self.assertRaises(ValueError, d, 'meter = [time]') self.assertRaises(ValueError, d, 'kilo- = 1000') self.assertRaises(ValueError, d, '[speed] = [length]') # aliases self.assertIn('inch', self.ureg._units) self.assertRaises(ValueError, d, 'bla = 3.2 meter = inch') self.assertRaises(ValueError, d, 'myk- = 1000 = kilo-') class TestConvertWithOffset(QuantityTestCase, ParameterizedTestCase): # The dicts in convert_with_offset are used to create a UnitsContainer. # We create UnitsContainer to avoid any auto-conversion of units. convert_with_offset = [ (({'degC': 1}, {'degC': 1}), 10), (({'degC': 1}, {'kelvin': 1}), 283.15), (({'degC': 1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 'error'), (({'degC': 1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 283150), (({'kelvin': 1}, {'degC': 1}), -263.15), (({'kelvin': 1}, {'kelvin': 1}), 10), (({'kelvin': 1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 'error'), (({'kelvin': 1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 10000), (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1}), 'error'), (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1}), 'error'), (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 10), (({'degC': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 'error'), (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1}), -273.14), (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1}), 0.01), (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'degC': 1, 'millimeter': 1, 'meter': -1}), 'error'), (({'kelvin': 1, 'millimeter': 1, 'meter': -1}, {'kelvin': 1, 'millimeter': 1, 'meter': -1}), 10), (({'degC': 2}, {'kelvin': 2}), 'error'), (({'degC': 1, 'degF': 1}, {'kelvin': 2}), 'error'), (({'degC': 1, 'kelvin': 1}, {'kelvin': 2}), 'error'), ] @ParameterizedTestCase.parameterize(("input", "expected_output"), convert_with_offset) def test_to_and_from_offset_units(self, input_tuple, expected): src, dst = input_tuple src, dst = UnitsContainer(src), UnitsContainer(dst) value = 10. convert = self.ureg.convert if isinstance(expected, string_types): self.assertRaises(DimensionalityError, convert, value, src, dst) if src != dst: self.assertRaises(DimensionalityError, convert, value, dst, src) else: self.assertQuantityAlmostEqual(convert(value, src, dst), expected, atol=0.001) if src != dst: self.assertQuantityAlmostEqual(convert(expected, dst, src), value, atol=0.001) pint-0.8.1/pint/testsuite/test_util.py000066400000000000000000000302561311537545300201000ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import division, unicode_literals, print_function, absolute_import import collections import copy import operator as op 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) class TestUnitsContainer(QuantityTestCase): def _test_inplace(self, operator, value1, value2, expected_result): value1 = copy.copy(value1) value2 = copy.copy(value2) id1 = id(value1) id2 = id(value2) value1 = operator(value1, value2) value2_cpy = copy.copy(value2) self.assertEqual(value1, expected_result) # Inplace operation creates copies self.assertNotEqual(id1, id(value1)) self.assertEqual(value2, value2_cpy) self.assertEqual(id2, id(value2)) def _test_not_inplace(self, operator, value1, value2, expected_result): id1 = id(value1) id2 = id(value2) value1_cpy = copy.copy(value1) value2_cpy = copy.copy(value2) result = operator(value1, value2) self.assertEqual(expected_result, result) self.assertEqual(value1, value1_cpy) self.assertEqual(value2, value2_cpy) self.assertNotEqual(id(result), id1) self.assertNotEqual(id(result), id2) def test_unitcontainer_creation(self): x = UnitsContainer(meter=1, second=2) y = UnitsContainer({'meter': 1.0, 'second': 2.0}) self.assertIsInstance(x['meter'], float) self.assertEqual(x, y) self.assertIsNot(x, y) z = copy.copy(x) self.assertEqual(x, z) self.assertIsNot(x, z) z = UnitsContainer(x) self.assertEqual(x, z) self.assertIsNot(x, z) def test_unitcontainer_repr(self): x = UnitsContainer() self.assertEqual(str(x), 'dimensionless') self.assertEqual(repr(x), '') x = UnitsContainer(meter=1, second=2) self.assertEqual(str(x), 'meter * second ** 2') self.assertEqual(repr(x), "") x = UnitsContainer(meter=1, second=2.5) self.assertEqual(str(x), 'meter * second ** 2.5') self.assertEqual(repr(x), "") def test_unitcontainer_bool(self): self.assertTrue(UnitsContainer(meter=1, second=2)) self.assertFalse(UnitsContainer()) def test_unitcontainer_comp(self): x = UnitsContainer(meter=1, second=2) y = UnitsContainer(meter=1., second=2) z = UnitsContainer(meter=1, second=3) self.assertTrue(x == y) self.assertFalse(x != y) self.assertFalse(x == z) self.assertTrue(x != z) def test_unitcontainer_arithmetic(self): x = UnitsContainer(meter=1) y = UnitsContainer(second=1) z = UnitsContainer(meter=1, second=-2) self._test_not_inplace(op.mul, x, y, UnitsContainer(meter=1, second=1)) self._test_not_inplace(op.truediv, x, y, UnitsContainer(meter=1, second=-1)) self._test_not_inplace(op.pow, z, 2, UnitsContainer(meter=2, second=-4)) self._test_not_inplace(op.pow, z, -2, UnitsContainer(meter=-2, second=4)) self._test_inplace(op.imul, x, y, UnitsContainer(meter=1, second=1)) self._test_inplace(op.itruediv, x, y, UnitsContainer(meter=1, second=-1)) self._test_inplace(op.ipow, z, 2, UnitsContainer(meter=2, second=-4)) self._test_inplace(op.ipow, z, -2, UnitsContainer(meter=-2, second=4)) def test_string_comparison(self): x = UnitsContainer(meter=1) y = UnitsContainer(second=1) z = UnitsContainer(meter=1, second=-2) self.assertEqual(x, 'meter') self.assertEqual('meter', x) self.assertNotEqual(x, 'meter ** 2') self.assertNotEqual(x, 'meter * meter') self.assertNotEqual(x, 'second') self.assertEqual(y, 'second') self.assertEqual(z, 'meter/second/second') def test_invalid(self): self.assertRaises(TypeError, UnitsContainer, {1: 2}) self.assertRaises(TypeError, UnitsContainer, {'1': '2'}) d = UnitsContainer() self.assertRaises(TypeError, d.__mul__, list()) self.assertRaises(TypeError, d.__pow__, list()) self.assertRaises(TypeError, d.__truediv__, list()) self.assertRaises(TypeError, d.__rtruediv__, list()) class TestToUnitsContainer(BaseTestCase): def test_str_conversion(self): self.assertEqual(to_units_container('m'), UnitsContainer(m=1)) def test_uc_conversion(self): a = UnitsContainer(m=1) self.assertIs(to_units_container(a), a) def test_quantity_conversion(self): from pint.registry import UnitRegistry ureg = UnitRegistry() self.assertEqual(to_units_container(ureg.Quantity(1, UnitsContainer(m=1))), UnitsContainer(m=1)) def test_unit_conversion(self): from pint.unit import _Unit self.assertEqual(to_units_container(_Unit(UnitsContainer(m=1))), UnitsContainer(m=1)) def test_dict_conversion(self): self.assertEqual(to_units_container(dict(m=1)), UnitsContainer(m=1)) class TestParseHelper(BaseTestCase): def test_basic(self): # Parse Helper ar mutables, so we build one everytime x = lambda: ParserHelper(1, meter=2) xp = lambda: ParserHelper(1, meter=2) y = lambda: ParserHelper(2, meter=2) self.assertEqual(x(), xp()) self.assertNotEqual(x(), y()) self.assertEqual(ParserHelper.from_string(''), ParserHelper()) self.assertEqual(repr(x()), "") self.assertEqual(ParserHelper(2), 2) self.assertEqual(x(), dict(meter=2)) self.assertEqual(x(), 'meter ** 2') self.assertNotEqual(y(), dict(meter=2)) self.assertNotEqual(y(), 'meter ** 2') self.assertNotEqual(xp(), object()) def test_calculate(self): # Parse Helper ar mutables, so we build one everytime x = lambda: ParserHelper(1., meter=2) y = lambda: ParserHelper(2., meter=-2) z = lambda: ParserHelper(2., meter=2) self.assertEqual(x() * 4., ParserHelper(4., meter=2)) self.assertEqual(x() * y(), ParserHelper(2.)) self.assertEqual(x() * 'second', ParserHelper(1., meter=2, second=1)) self.assertEqual(x() / 4., ParserHelper(0.25, meter=2)) self.assertEqual(x() / 'second', ParserHelper(1., meter=2, second=-1)) self.assertEqual(x() / z(), ParserHelper(0.5)) self.assertEqual(4. / z(), ParserHelper(2., meter=-2)) self.assertEqual('seconds' / z(), ParserHelper(0.5, seconds=1, meter=-2)) self.assertEqual(dict(seconds=1) / z(), ParserHelper(0.5, seconds=1, meter=-2)) def _test_eval_token(self, expected, expression, use_decimal=False): token = next(tokenizer(expression)) actual = ParserHelper.eval_token(token, use_decimal=use_decimal) self.assertEqual(expected, actual) self.assertEqual(type(expected), type(actual)) def test_eval_token(self): self._test_eval_token(1000.0, '1e3') self._test_eval_token(1000.0, '1E3') self._test_eval_token(Decimal(1000), '1e3', use_decimal=True) self._test_eval_token(1000, '1000') # integer numbers are represented as ints, not Decimals self._test_eval_token(1000, '1000', use_decimal=True) class TestStringProcessor(BaseTestCase): def _test(self, bef, aft): for pattern in ('{0}', '+{0}+'): b = pattern.format(bef) a = pattern.format(aft) self.assertEqual(string_preprocessor(b), a) def test_square_cube(self): self._test('bcd^3', 'bcd**3') self._test('bcd^ 3', 'bcd** 3') self._test('bcd ^3', 'bcd **3') self._test('bcd squared', 'bcd**2') self._test('bcd squared', 'bcd**2') self._test('bcd cubed', 'bcd**3') self._test('sq bcd', 'bcd**2') self._test('square bcd', 'bcd**2') self._test('cubic bcd', 'bcd**3') self._test('bcd efg', 'bcd*efg') def test_per(self): self._test('miles per hour', 'miles/hour') def test_numbers(self): self._test('1,234,567', '1234567') self._test('1e-24', '1e-24') self._test('1e+24', '1e+24') self._test('1e24', '1e24') self._test('1E-24', '1E-24') self._test('1E+24', '1E+24') self._test('1E24', '1E24') def test_space_multiplication(self): self._test('bcd efg', 'bcd*efg') self._test('bcd efg', 'bcd*efg') self._test('1 hour', '1*hour') self._test('1. hour', '1.*hour') self._test('1.1 hour', '1.1*hour') self._test('1E24 hour', '1E24*hour') self._test('1E-24 hour', '1E-24*hour') self._test('1E+24 hour', '1E+24*hour') self._test('1.2E24 hour', '1.2E24*hour') self._test('1.2E-24 hour', '1.2E-24*hour') self._test('1.2E+24 hour', '1.2E+24*hour') def test_joined_multiplication(self): self._test('1hour', '1*hour') self._test('1.hour', '1.*hour') self._test('1.1hour', '1.1*hour') self._test('1h', '1*h') self._test('1.h', '1.*h') self._test('1.1h', '1.1*h') def test_names(self): self._test('g_0', 'g_0') self._test('g0', 'g0') self._test('g', 'g') self._test('water_60F', 'water_60F') class TestGraph(BaseTestCase): def test_start_not_in_graph(self): g = collections.defaultdict(list) g[1] = set((2,)) g[2] = set((3,)) self.assertIs(find_connected_nodes(g, 9), None) def test_shortest_path(self): g = collections.defaultdict(list) g[1] = set((2,)) g[2] = set((3,)) p = find_shortest_path(g, 1, 2) self.assertEqual(p, [1, 2]) p = find_shortest_path(g, 1, 3) self.assertEqual(p, [1, 2, 3]) p = find_shortest_path(g, 3, 1) self.assertIs(p, None) g = collections.defaultdict(list) g[1] = set((2,)) g[2] = set((3, 1)) g[3] = set((2,)) p = find_shortest_path(g, 1, 2) self.assertEqual(p, [1, 2]) p = find_shortest_path(g, 1, 3) self.assertEqual(p, [1, 2, 3]) p = find_shortest_path(g, 3, 1) self.assertEqual(p, [3, 2, 1]) p = find_shortest_path(g, 2, 1) self.assertEqual(p, [2, 1]) class TestMatrix(BaseTestCase): def test_matrix_to_string(self): self.assertEqual(matrix_to_string([[1, 2], [3, 4]], row_headers=None, col_headers=None), '1\t2\n' '3\t4') self.assertEqual(matrix_to_string([[1, 2], [3, 4]], row_headers=None, col_headers=None, fmtfun=lambda x: '{0:.2f}'.format(x)), '1.00\t2.00\n' '3.00\t4.00') self.assertEqual(matrix_to_string([[1, 2], [3, 4]], row_headers=['c', 'd'], col_headers=None), 'c\t1\t2\n' 'd\t3\t4') self.assertEqual(matrix_to_string([[1, 2], [3, 4]], row_headers=None, col_headers=['a', 'b']), 'a\tb\n' '1\t2\n' '3\t4') self.assertEqual(matrix_to_string([[1, 2], [3, 4]], row_headers=['c', 'd'], col_headers=['a', 'b']), '\ta\tb\n' 'c\t1\t2\n' 'd\t3\t4') def test_transpose(self): self.assertEqual(transpose([[1, 2], [3, 4]]), [[1, 3], [2, 4]]) pint-0.8.1/pint/unit.py000066400000000000000000000200321311537545300150010ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.unit ~~~~~~~~~ Functions and classes related to unit definitions and conversions. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import division, unicode_literals, print_function, absolute_import import copy import operator from numbers import Number from .util import UnitsContainer, SharedRegistryObject, fix_str_conversions from .compat import string_types, NUMERIC_TYPES, long_type from .formatting import siunitx_format_unit from .definitions import UnitDefinition @fix_str_conversions class _Unit(SharedRegistryObject): """Implements a class to describe a unit supporting math operations. :type units: UnitsContainer, str, Unit or Quantity. """ #: Default formatting string. default_format = '' def __reduce__(self): from . import _build_unit return _build_unit, (self._units, ) def __new__(cls, units): inst = object.__new__(cls) if isinstance(units, (UnitsContainer, UnitDefinition)): inst._units = units elif isinstance(units, string_types): inst._units = inst._REGISTRY.parse_units(units)._units elif isinstance(units, _Unit): inst._units = units._units else: raise TypeError('units must be of type str, Unit or ' 'UnitsContainer; not {0}.'.format(type(units))) inst.__used = False inst.__handling = None return inst @property def debug_used(self): return self.__used def __copy__(self): ret = self.__class__(self._units) ret.__used = self.__used return ret def __deepcopy__(self, memo): ret = self.__class__(copy.deepcopy(self._units)) ret.__used = self.__used return ret def __str__(self): return format(self) def __repr__(self): return "".format(self._units) def __format__(self, spec): spec = spec or self.default_format # special cases if 'Lx' in spec: # the LaTeX siunitx code opts = '' ustr = siunitx_format_unit(self) ret = r'\si[%s]{%s}'%( opts, ustr ) return ret if '~' in spec: if not self._units: return '' units = UnitsContainer(dict((self._REGISTRY._get_symbol(key), value) for key, value in self._units.items())) spec = spec.replace('~', '') else: units = self._units return '%s' % (format(units, spec)) def format_babel(self, spec='', **kwspec): spec = spec or self.default_format if '~' in spec: if self.dimensionless: return '' units = UnitsContainer(dict((self._REGISTRY._get_symbol(key), value) for key, value in self._units.items())) spec = spec.replace('~', '') else: units = self._units return '%s' % (units.format_babel(spec, **kwspec)) # IPython related code def _repr_html_(self): return self.__format__('H') def _repr_latex_(self): return "$" + self.__format__('L') + "$" @property def dimensionless(self): """Return true if the Unit is dimensionless. """ return not bool(self.dimensionality) @property def dimensionality(self): """Unit's dimensionality (e.g. {length: 1, time: -1}) """ try: return self._dimensionality except AttributeError: dim = self._REGISTRY._get_dimensionality(self._units) self._dimensionality = dim return self._dimensionality def compatible_units(self, *contexts): if contexts: with self._REGISTRY.context(*contexts): return self._REGISTRY.get_compatible_units(self) return self._REGISTRY.get_compatible_units(self) def __mul__(self, other): if self._check(other): if isinstance(other, self.__class__): return self.__class__(self._units*other._units) else: qself = self._REGISTRY.Quantity(1.0, self._units) return qself * other if isinstance(other, Number) and other == 1: return self._REGISTRY.Quantity(other, self._units) return self._REGISTRY.Quantity(1, self._units) * other __rmul__ = __mul__ def __truediv__(self, other): if self._check(other): if isinstance(other, self.__class__): return self.__class__(self._units/other._units) else: qself = 1.0 * self return qself / other return self._REGISTRY.Quantity(1/other, self._units) def __rtruediv__(self, other): # As Unit and Quantity both handle truediv with each other rtruediv can # only be called for something different. if isinstance(other, NUMERIC_TYPES): return self._REGISTRY.Quantity(other, 1/self._units) elif isinstance(other, UnitsContainer): return self.__class__(other/self._units) else: return NotImplemented __div__ = __truediv__ __rdiv__ = __rtruediv__ def __pow__(self, other): if isinstance(other, NUMERIC_TYPES): return self.__class__(self._units**other) else: mess = 'Cannot power Unit by {}'.format(type(other)) raise TypeError(mess) def __hash__(self): return self._units.__hash__() def __eq__(self, other): # We compare to the base class of Unit because each Unit class is # unique. if self._check(other): if isinstance(other, self.__class__): return self._units == other._units else: return other == self._REGISTRY.Quantity(1, self._units) elif isinstance(other, NUMERIC_TYPES): return other == self._REGISTRY.Quantity(1, self._units) else: return self._units == other def __ne__(self, other): return not (self == other) def compare(self, other, op): self_q = self._REGISTRY.Quantity(1, self) if isinstance(other, NUMERIC_TYPES): return self_q.compare(other, op) elif isinstance(other, (_Unit, UnitsContainer, dict)): return self_q.compare(self._REGISTRY.Quantity(1, other), op) else: return NotImplemented __lt__ = lambda self, other: self.compare(other, op=operator.lt) __le__ = lambda self, other: self.compare(other, op=operator.le) __ge__ = lambda self, other: self.compare(other, op=operator.ge) __gt__ = lambda self, other: self.compare(other, op=operator.gt) def __int__(self): return int(self._REGISTRY.Quantity(1, self._units)) def __long__(self): return long_type(self._REGISTRY.Quantity(1, self._units)) def __float__(self): return float(self._REGISTRY.Quantity(1, self._units)) def __complex__(self): return complex(self._REGISTRY.Quantity(1, self._units)) __array_priority__ = 17 def __array_prepare__(self, array, context=None): return 1 def __array_wrap__(self, array, context=None): uf, objs, huh = context if uf.__name__ in ('true_divide', 'divide', 'floor_divide'): return self._REGISTRY.Quantity(array, 1/self._units) elif uf.__name__ in ('multiply',): return self._REGISTRY.Quantity(array, self._units) else: raise ValueError('Unsupproted operation for Unit') @property def systems(self): out = set() for uname in self._units.keys(): for sname, sys in self._REGISTRY._systems.items(): if uname in sys.members: out.add(sname) return frozenset(out) def build_unit_class(registry): class Unit(_Unit): pass Unit._REGISTRY = registry return Unit pint-0.8.1/pint/util.py000066400000000000000000000546211311537545300150120ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ pint.util ~~~~~~~~~ Miscellaneous functions for pint. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import division, unicode_literals, print_function, absolute_import from decimal import Decimal import locale import sys import re import operator from numbers import Number from fractions import Fraction from collections import Mapping import logging from token import STRING, NAME, OP, NUMBER from tokenize import untokenize from .compat import string_types, tokenizer, lru_cache, NullHandler, maketrans, NUMERIC_TYPES from .formatting import format_unit,siunitx_format_unit from .pint_eval import build_eval_tree from .errors import DefinitionSyntaxError logger = logging.getLogger(__name__) logger.addHandler(NullHandler()) def matrix_to_string(matrix, row_headers=None, col_headers=None, fmtfun=lambda x: str(int(x))): """Takes a 2D matrix (as nested list) and returns a string. """ ret = [] if col_headers: ret.append(('\t' if row_headers else '') + '\t'.join(col_headers)) if row_headers: ret += [rh + '\t' + '\t'.join(fmtfun(f) for f in row) for rh, row in zip(row_headers, matrix)] else: ret += ['\t'.join(fmtfun(f) for f in row) for row in matrix] return '\n'.join(ret) def transpose(matrix): """Takes a 2D matrix (as nested list) and returns the transposed version. """ return [list(val) for val in zip(*matrix)] def column_echelon_form(matrix, ntype=Fraction, transpose_result=False): """Calculates the column echelon form using Gaussian elimination. :param matrix: a 2D matrix as nested list. :param ntype: the numerical type to use in the calculation. :param transpose_result: indicates if the returned matrix should be transposed. :return: column echelon form, transformed identity matrix, swapped rows """ lead = 0 M = transpose(matrix) _transpose = transpose if transpose_result else lambda x: x rows, cols = len(M), len(M[0]) new_M = [] for row in M: r = [] for x in row: if isinstance(x, float): x = ntype.from_float(x) else: x = ntype(x) r.append(x) new_M.append(r) M = new_M # M = [[ntype(x) for x in row] for row in M] I = [[ntype(1) if n == nc else ntype(0) for nc in range(rows)] for n in range(rows)] swapped = [] for r in range(rows): if lead >= cols: return _transpose(M), _transpose(I), swapped i = r while M[i][lead] == 0: i += 1 if i != rows: continue i = r lead += 1 if cols == lead: return _transpose(M), _transpose(I), swapped M[i], M[r] = M[r], M[i] I[i], I[r] = I[r], I[i] swapped.append(i) lv = M[r][lead] M[r] = [mrx / lv for mrx in M[r]] I[r] = [mrx / lv for mrx in I[r]] for i in range(rows): if i == r: continue lv = M[i][lead] M[i] = [iv - lv*rv for rv, iv in zip(M[r], M[i])] I[i] = [iv - lv*rv for rv, iv in zip(I[r], I[i])] lead += 1 return _transpose(M), _transpose(I), swapped def pi_theorem(quantities, registry=None): """Builds dimensionless quantities using the Buckingham π theorem :param quantities: mapping between variable name and units :type quantities: dict :return: a list of dimensionless quantities expressed as dicts """ # Preprocess input and build the dimensionality Matrix quant = [] dimensions = set() if registry is None: getdim = lambda x: x else: getdim = registry.get_dimensionality for name, value in quantities.items(): if isinstance(value, string_types): value = ParserHelper.from_string(value) if isinstance(value, dict): dims = getdim(UnitsContainer(value)) elif not hasattr(value, 'dimensionality'): dims = getdim(value) else: dims = value.dimensionality if not registry and any(not key.startswith('[') for key in dims): logger.warning('A non dimension was found and a registry was not provided. ' 'Assuming that it is a dimension name: {0}.'.format(dims)) quant.append((name, dims)) dimensions = dimensions.union(dims.keys()) dimensions = list(dimensions) # Calculate dimensionless quantities M = [[dimensionality[dimension] for name, dimensionality in quant] for dimension in dimensions] M, identity, pivot = column_echelon_form(M, transpose_result=False) # Collect results # Make all numbers integers and minimize the number of negative exponents. # Remove zeros results = [] for rowm, rowi in zip(M, identity): if any(el != 0 for el in rowm): continue max_den = max(f.denominator for f in rowi) neg = -1 if sum(f < 0 for f in rowi) > sum(f > 0 for f in rowi) else 1 results.append(dict((q[0], neg * f.numerator * max_den / f.denominator) for q, f in zip(quant, rowi) if f.numerator != 0)) return results def solve_dependencies(dependencies): """Solve a dependency graph. :param dependencies: dependency dictionary. For each key, the value is an iterable indicating its dependencies. :return: list of sets, each containing keys of independents tasks dependent only of the previous tasks in the list. """ d = dict((key, set(dependencies[key])) for key in dependencies) r = [] while d: # values not in keys (items without dep) t = set(i for v in d.values() for i in v) - set(d.keys()) # and keys without value (items without dep) t.update(k for k, v in d.items() if not v) # can be done right away if not t: raise ValueError('Cyclic dependencies exist among these items: {}'.format(', '.join(repr(x) for x in d.items()))) r.append(t) # and cleaned up d = dict(((k, v - t) for k, v in d.items() if v)) return r def find_shortest_path(graph, start, end, path=None): path = (path or []) + [start] if start == end: return path if not start in graph: return None shortest = None for node in graph[start]: if node not in path: newpath = find_shortest_path(graph, node, end, path) if newpath: if not shortest or len(newpath) < len(shortest): shortest = newpath return shortest def find_connected_nodes(graph, start, visited=None): if not start in graph: return None visited = (visited or set()) visited.add(start) for node in graph[start]: if node not in visited: find_connected_nodes(graph, node, visited) return visited class udict(dict): """ Custom dict implementing __missing__. """ def __missing__(self, key): return 0. class UnitsContainer(Mapping): """The UnitsContainer stores the product of units and their respective exponent and implements the corresponding operations. UnitsContainer is a read-only mapping. All operations (even in place ones) return new instances. """ __slots__ = ('_d', '_hash') def __init__(self, *args, **kwargs): d = udict(*args, **kwargs) self._d = d for key, value in d.items(): if not isinstance(key, string_types): raise TypeError('key must be a str, not {0}'.format(type(key))) if not isinstance(value, Number): raise TypeError('value must be a number, not {0}'.format(type(value))) if not isinstance(value, float): d[key] = float(value) self._hash = hash(frozenset(self._d.items())) def copy(self): return self.__copy__() def add(self, key, value): newval = self._d[key] + value new = self.copy() if newval: new._d[key] = newval else: del new._d[key] 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)) 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) def __iter__(self): return iter(self._d) def __len__(self): return len(self._d) def __getitem__(self, key): return self._d[key] def __contains__(self, key): return key in self._d def __hash__(self): return self._hash def __getstate__(self): return {'_d': self._d, '_hash': self._hash} def __setstate__(self, state): self._d = state['_d'] self._hash = state['_hash'] def __eq__(self, other): if isinstance(other, UnitsContainer): other = other._d elif isinstance(other, string_types): other = ParserHelper.from_string(other) other = other._d return dict.__eq__(self._d, other) def __str__(self): return self.__format__('') def __repr__(self): tmp = '{%s}' % ', '.join(["'{0}': {1}".format(key, value) for key, value in sorted(self._d.items())]) return ''.format(tmp) def __format__(self, spec): return format_unit(self, spec) def format_babel(self, spec, **kwspec): return format_unit(self, spec, **kwspec) def __copy__(self): return UnitsContainer(self._d) def __mul__(self, other): d = udict(self._d) if not isinstance(other, self.__class__): err = 'Cannot multiply UnitsContainer by {0}' raise TypeError(err.format(type(other))) 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] return UnitsContainer(d) __rmul__ = __mul__ def __pow__(self, other): if not isinstance(other, NUMERIC_TYPES): err = 'Cannot power UnitsContainer by {0}' raise TypeError(err.format(type(other))) d = udict(self._d) for key, value in d.items(): d[key] *= other return UnitsContainer(d) def __truediv__(self, other): if not isinstance(other, self.__class__): err = 'Cannot divide UnitsContainer by {0}' raise TypeError(err.format(type(other))) d = udict(self._d) 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] return UnitsContainer(d) def __rtruediv__(self, other): if not isinstance(other, self.__class__) and other != 1: err = 'Cannot divide {0} by UnitsContainer' raise TypeError(err.format(type(other))) return self**-1 class ParserHelper(UnitsContainer): """ The ParserHelper stores in place the product of variables and their respective exponent and implements the corresponding operations. ParserHelper is a read-only mapping. All operations (even in place ones) return new instances. WARNING : The hash value used does not take into account the scale attribute so be careful if you use it as a dict key and then two unequal object can have the same hash. """ __slots__ = ('scale', ) def __init__(self, scale=1, *args, **kwargs): super(ParserHelper, self).__init__(*args, **kwargs) self.scale = scale @classmethod def from_word(cls, input_word): """Creates a ParserHelper object with a single variable with exponent one. Equivalent to: ParserHelper({'word': 1}) """ return cls(1, [(input_word, 1)]) @classmethod def from_string(cls, input_string): return cls._from_string(input_string) @classmethod def eval_token(cls, token, use_decimal=False): token_type = token.type token_text = token.string if token_type == NUMBER: try: return int(token_text) except ValueError: if use_decimal: return Decimal(token_text) return float(token_text) elif token_type == NAME: return ParserHelper.from_word(token_text) else: raise Exception('unknown token type') @classmethod @lru_cache() def _from_string(cls, input_string): """Parse linear expression mathematical units and return a quantity object. """ if not input_string: return cls() input_string = string_preprocessor(input_string) if '[' in input_string: input_string = input_string.replace('[', '__obra__').replace(']', '__cbra__') reps = True else: reps = False gen = tokenizer(input_string) ret = build_eval_tree(gen).evaluate(cls.eval_token) if isinstance(ret, Number): return ParserHelper(ret) if not reps: return ret return ParserHelper(ret.scale, dict((key.replace('__obra__', '[').replace('__cbra__', ']'), value) for key, value in ret.items())) def __copy__(self): return ParserHelper(scale=self.scale, **self) def copy(self): return self.__copy__() def __hash__(self): if self.scale != 1.0: mess = 'Only scale 1.0 ParserHelper instance should be considered hashable' raise ValueError(mess) return self._hash def __eq__(self, other): if isinstance(other, self.__class__): 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): return self.scale == other and not len(self._d) else: return self.scale == 1. and super(ParserHelper, self).__eq__(other) def operate(self, items, op=operator.iadd, cleanup=True): d = udict(self._d) for key, value in items: d[key] = op(d[key], value) if cleanup: keys = [key for key, value in d.items() if value == 0] for key in keys: del d[key] return self.__class__(self.scale, d) def __str__(self): tmp = '{%s}' % ', '.join(["'{0}': {1}".format(key, value) for key, value in sorted(self._d.items())]) return '{0} {1}'.format(self.scale, tmp) def __repr__(self): tmp = '{%s}' % ', '.join(["'{0}': {1}".format(key, value) for key, value in sorted(self._d.items())]) return ''.format(self.scale, tmp) def __mul__(self, other): if isinstance(other, string_types): new = self.add(other, 1) elif isinstance(other, Number): new = self.copy() new.scale *= other elif isinstance(other, self.__class__): new = self.operate(other.items()) new.scale *= other.scale else: new = self.operate(other.items()) return new __rmul__ = __mul__ def __pow__(self, other): d = self._d.copy() for key in self._d: d[key] *= other return self.__class__(self.scale**other, d) def __truediv__(self, other): if isinstance(other, string_types): new = self.add(other, -1) elif isinstance(other, Number): new = self.copy() new.scale /= other elif isinstance(other, self.__class__): new = self.operate(other.items(), operator.sub) new.scale /= other.scale else: new = self.operate(other.items(), operator.sub) return new __floordiv__ = __truediv__ def __rtruediv__(self, other): new = self.__pow__(-1) if isinstance(other, string_types): new = new.add(other, 1) elif isinstance(other, Number): new.scale *= other elif isinstance(other, self.__class__): new = self.operate(other.items(), operator.add) new.scale *= other.scale else: new = new.operate(other.items(), operator.add) return new #: List of regex substitution pairs. _subs_re = [('\N{DEGREE SIGN}', " degree"), (r"([\w\.\-\+\*\\\^])\s+", r"\1 "), # merge multiple spaces (r"({0}) squared", r"\1**2"), # Handle square and cube (r"({0}) cubed", r"\1**3"), (r"cubic ({0})", r"\1**3"), (r"square ({0})", r"\1**2"), (r"sq ({0})", r"\1**2"), (r"\b([0-9]+\.?[0-9]*)(?=[e|E][a-zA-Z]|[a-df-zA-DF-Z])", r"\1*"), # Handle numberLetter for multiplication (r"([\w\.\-])\s+(?=\w)", r"\1*"), # Handle space for multiplication ] #: Compiles the regex and replace {0} by a regex that matches an identifier. _subs_re = [(re.compile(a.format(r"[_a-zA-Z][_a-zA-Z0-9]*")), b) for a, b in _subs_re] _pretty_table = maketrans('⁰¹²³⁴⁵⁶⁷⁸⁹·⁻', '0123456789*-') _pretty_exp_re = re.compile(r"⁻?[⁰¹²³⁴⁵⁶⁷⁸⁹]+(?:\.[⁰¹²³⁴⁵⁶⁷⁸⁹]*)?") def string_preprocessor(input_string): input_string = input_string.replace(",", "") input_string = input_string.replace(" per ", "/") for a, b in _subs_re: input_string = a.sub(b, input_string) # Replace pretty format characters for pretty_exp in _pretty_exp_re.findall(input_string): exp = '**' + pretty_exp.translate(_pretty_table) input_string = input_string.replace(pretty_exp, exp) input_string = input_string.translate(_pretty_table) # Handle caret exponentiation input_string = input_string.replace("^", "**") return input_string def _is_dim(name): return name[0] == '[' and name[-1] == ']' class SharedRegistryObject(object): """Base class for object keeping a refrence to the registree. 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 _check(self, other): """Check if the other object use a registry and if so that it is the same registry. Return True is both use a registry and they use the same, False is other don't use a registry and raise ValueError if other don't use the same unit registry. """ if self._REGISTRY is getattr(other, '_REGISTRY', None): return True elif isinstance(other, SharedRegistryObject): mess = 'Cannot operate with {0} and {1} of different registries.' raise ValueError(mess.format(self.__class__.__name__, other.__class__.__name__)) else: return False def to_units_container(unit_like, registry=None): """ Convert a unit compatible type to a UnitsContainer. """ mro = type(unit_like).mro() if UnitsContainer in mro: return unit_like elif SharedRegistryObject in mro: return unit_like._units elif string_types in mro: if registry: return registry._parse_units(unit_like) else: return ParserHelper.from_string(unit_like) elif dict in mro: return UnitsContainer(unit_like) def infer_base_unit(q): """Return UnitsContainer of q with all prefixes stripped.""" d = udict() parse = q._REGISTRY.parse_unit_name for unit_name, power in q._units.items(): completely_parsed_unit = list(parse(unit_name))[-1] _, base_unit, __ = completely_parsed_unit d[base_unit] += power return UnitsContainer(dict((k, v) for k, v in d.items() if v != 0)) # remove values that resulted in a power of 0 def fix_str_conversions(cls): """Enable python2/3 compatible behaviour for __str__.""" def __bytes__(self): return self.__unicode__().encode(locale.getpreferredencoding()) cls.__unicode__ = __unicode__ = cls.__str__ cls.__bytes__ = __bytes__ if sys.version_info[0] == 2: cls.__str__ = __bytes__ else: cls.__str__ = __unicode__ return cls class SourceIterator(object): """Iterator to facilitate reading the definition files. Accepts any sequence (like a list of lines, a file or another SourceIterator) The iterator yields the line number and line (skipping comments and empty lines) and stripping white spaces. for lineno, line in SourceIterator(sequence): # do something here """ def __new__(cls, sequence): if isinstance(sequence, SourceIterator): return sequence obj = object.__new__(cls) if sequence is not None: obj.internal = enumerate(sequence, 1) obj.last = (None, None) return obj def __iter__(self): return self def __next__(self): line = '' while not line or line.startswith('#'): lineno, line = next(self.internal) line = line.split('#', 1)[0].strip() self.last = lineno, line return lineno, line next = __next__ def block_iter(self): """Iterate block including header. """ return BlockIterator(self) class BlockIterator(SourceIterator): """Like SourceIterator but stops when it finds '@end' It also raises an error if another '@' directive is found inside. """ def __new__(cls, line_iterator): obj = SourceIterator.__new__(cls, None) obj.internal = line_iterator.internal obj.last = line_iterator.last obj.done_last = False return obj def __next__(self): if not self.done_last: self.done_last = True return self.last lineno, line = SourceIterator.__next__(self) if line.startswith('@end'): raise StopIteration elif line.startswith('@'): raise DefinitionSyntaxError('cannot nest @ directives', lineno=lineno) return lineno, line next = __next__ pint-0.8.1/pint/xtranslated.txt000066400000000000000000000015261311537545300165510ustar00rootroot00000000000000 # a few unit definitions added to use the translations by unicode cldr dietary_calorie = 1000 * calorie = 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 liter_per_100kilometers = liter / (100 * kilometers) [US_consumption] = [length] / [volume] MPG = mile / gallon pint-0.8.1/setup.cfg000066400000000000000000000001241311537545300143170ustar00rootroot00000000000000[check-manifest] ignore = .travis.yml tox.ini [bdist_wheel] universal = 1 pint-0.8.1/setup.py000066400000000000000000000040421311537545300142130ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import sys try: reload(sys).setdefaultencoding("UTF-8") except: pass try: from setuptools import setup except ImportError: print('Please install or upgrade setuptools or pip to continue') sys.exit(1) tests_require = [] if sys.version_info < (2, 7): tests_require.append('unittest2') import codecs def read(filename): return codecs.open(filename, encoding='utf-8').read() long_description = '\n\n'.join([read('README'), read('AUTHORS'), read('CHANGES')]) __doc__ = long_description setup( name='Pint', version='0.8.1', description='Physical quantities module', long_description=long_description, keywords='physical quantities unit conversion science', author='Hernan E. Grecco', author_email='hernan.grecco@gmail.com', url='https://github.com/hgrecco/pint', test_suite='pint.testsuite.testsuite', tests_require=tests_require, zip_safe=True, packages=['pint'], package_data={ 'pint': ['default_en.txt', 'constants_en.txt'] }, include_package_data=True, license='BSD', classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: BSD License', 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX', 'Programming Language :: Python', 'Topic :: Scientific/Engineering', 'Topic :: Software Development :: Libraries', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.0', 'Programming Language :: Python :: 3.1', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', ]) pint-0.8.1/tox.ini000066400000000000000000000031021311537545300140100ustar00rootroot00000000000000[tox] envlist = py26, py27, py32, py33, py34, numpy26, numpy27, numpy32, numpy33, numpy34, py26u,py27u,py32u,py33u,py34u,numpy26u,numpy27u,numpy32u,numpy33u,numpy34u [testenv] deps = coverage commands = coverage run -p --source=pint setup.py test setenv= COVERAGE_FILE=.coverage.{envname} [testenv:py26] basepython=python2.6 deps = unittest2 coverage [testenv:numpy26] basepython=python2.6 deps = numpy unittest2 coverage [testenv:numpy27] basepython=python2.7 deps = numpy coverage [testenv:numpy32] basepython=python3.2 deps = numpy coverage [testenv:numpy33] basepython=python3.3 deps = numpy coverage [testenv:numpy34] basepython=python3.4 deps = numpy coverage [testenv:py26u] basepython=python2.6 deps = unittest2 uncertainties coverage [testenv:py27u] basepython=python2.7 deps = uncertainties coverage [testenv:py32u] basepython=python3.2 deps = uncertainties coverage [testenv:py33u] basepython=python3.3 deps = uncertainties coverage [testenv:py34u] basepython=python3.4 deps = uncertainties coverage [testenv:numpy26u] basepython=python2.6 deps = numpy unittest2 uncertainties coverage [testenv:numpy27u] basepython=python2.7 deps = numpy uncertainties coverage [testenv:numpy32u] basepython=python3.2 deps = numpy uncertainties coverage [testenv:numpy33u] basepython=python3.3 deps = numpy uncertainties coverage [testenv:numpy34u] basepython=python3.4 deps = numpy uncertainties coverage