summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHernan Grecco <hernan.grecco@gmail.com>2020-02-21 23:41:43 -0300
committerGitHub <noreply@github.com>2020-02-21 23:41:43 -0300
commit78b1e52958a326cba4983c3ef84016756274c92c (patch)
treedfcaa026318ffb0c06fc82729955da8479c581c2
parent36ebb0f0e280bbb5b1cd32475e4b13d0222a371c (diff)
parent92dc58e7b1de6a7ad0aaaad48f63490a80e1d82d (diff)
downloadpint-_decimal.tar.gz
Merge branch 'master' into _decimal_decimal
-rw-r--r--AUTHORS1
-rw-r--r--CHANGES30
-rw-r--r--MANIFEST.in2
-rw-r--r--README.rst17
-rw-r--r--docs/defining.rst14
-rw-r--r--docs/index.rst1
-rw-r--r--docs/numpy.ipynb11
-rw-r--r--docs/pint-convert.rst91
-rw-r--r--docs/pint-pandas.ipynb37
-rw-r--r--docs/tutorial.rst69
-rw-r--r--pint/compat.py27
-rw-r--r--pint/constants_en.txt2
-rw-r--r--pint/default_en.txt2
-rw-r--r--pint/default_en_0.6.txt360
-rw-r--r--pint/definitions.py16
-rw-r--r--pint/numpy_func.py18
-rwxr-xr-xpint/pint-convert120
-rw-r--r--pint/quantity.py83
-rw-r--r--pint/registry.py101
-rw-r--r--pint/testsuite/helpers.py4
-rw-r--r--pint/testsuite/test_babel.py35
-rw-r--r--pint/testsuite/test_issues.py18
-rw-r--r--pint/testsuite/test_numpy.py38
-rw-r--r--pint/testsuite/test_quantity.py9
-rw-r--r--pint/testsuite/test_unit.py41
-rw-r--r--pint/unit.py35
-rw-r--r--pint/util.py16
-rw-r--r--pyproject.toml5
-rw-r--r--setup.cfg5
-rw-r--r--setup.py2
-rw-r--r--version.py2
31 files changed, 702 insertions, 510 deletions
diff --git a/AUTHORS b/AUTHORS
index 21ba865..db3229e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -27,6 +27,7 @@ Other contributors, listed alphabetically, are:
* Joel B. Mohler <joel@kiwistrawberry.us>
* John David Reaver <jdreaver@adlerhorst.com>
* Jonas Olson <jolson@kth.se>
+* Jules Chéron <me@julescheron.com>
* Kaido Kert <kaidokert@gmail.com>
* Kenneth D. Mankoff <mankoff@gmail.com>
* Kevin Davies <kdavies4@gmail.com>
diff --git a/CHANGES b/CHANGES
index a263a98..b897f29 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,14 +1,34 @@
Pint Changelog
==============
-0.11 (unreleased)
+0.12 (unreleased)
-----------------
- Add full support for Decimal and Fraction at the registry level.
**BREAKING CHANGE**:
`use_decimal` is deprecated. Use `non_int_type=Decimal` when instantiating
the registry.
-- Moved Pi to defintions files.
+- Fixed bug where numpy.pad didn't work without specifying constant_values or
+ end_values (Issue #1026)
+
+
+0.11 (2020-02-19)
+-----------------
+
+- Added pint-convert script.
+- Remove `default_en_0.6.txt`.
+- Make `__str__` and `__format__` locale configurable.
+ (Issue #984)
+- Quantities wrapping NumPy arrays will no longer warning for the changed
+ array function behavior introduced in 0.10.
+ (Issue #1029, Thanks Jon Thielen)
+- **BREAKING CHANGE**:
+ The array protocol fallback deprecated in version 0.10 has been removed.
+ (Issue #1029, Thanks Jon Thielen)
+- Now we use `pyproject.toml` for providing `setuptools_scm` settings
+- Remove `default_en_0.6.txt`
+- Reorganize long_description.
+- Moved Pi to definitions files.
- Use ints (not floats) a defaults at many points in the codebase as in Python 3
the true division is the default one.
- **BREAKING CHANGE**:
@@ -17,6 +37,10 @@ Pint Changelog
It is unlikely that this change affects the end user.
- Added additional NumPy function implementations (allclose, intersect1d)
(Issue #979, Thanks Jon Thielen)
+- Allow constants in units by using a leading underscore (Issue #989, Thanks
+ Juan Nunez-Iglesias)
+- Fixed bug where to_compact handled prefix units incorrectly (Issue #960)
+
0.10.1 (2020-01-07)
-------------------
@@ -233,7 +257,7 @@ Pint Changelog
- Added several new units and Systems
(Issues #749, #737, )
- Started experimental pandas support
- (Issue #746 and others. Thanks andrewgsavage, znicholls and others)
+ (Issue #746 and others. Thanks andrewgsavage, znicholls and others)
- wraps and checks now supports kwargs and defaults.
(Issue #660, thanks jondoesntgit)
diff --git a/MANIFEST.in b/MANIFEST.in
index 0cf6865..cd897ad 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-include AUTHORS CHANGES LICENSE README.rst BADGES.rst version.txt readthedocs.yml
+include AUTHORS CHANGES LICENSE README.rst BADGES.rst version.txt readthedocs.yml .coveragerc
recursive-include pint *
recursive-include docs *
recursive-include bench *
diff --git a/README.rst b/README.rst
index 6c99466..97cda30 100644
--- a/README.rst
+++ b/README.rst
@@ -87,12 +87,20 @@ Documentation
Full documentation is available at http://pint.readthedocs.org/
+
GUI Website
-----------
This Website_ wraps Pint's "dimensional analysis" methods to provide a GUI.
+Command-line converter
+----------------------
+
+A command-line script `pint-convert` provides a quick way to convert between
+units or get conversion factors.
+
+
Design principles
-----------------
@@ -142,6 +150,13 @@ ufuncs are supported including automatic conversion of units. For example
quantity will be radian.
+Pint is maintained by a community of scientists, programmers and entusiasts around the world.
+See AUTHORS_ for a complete list.
+
+To review an ordered list of notable changes for each version of a project,
+see CHANGES_
+
+
.. _Website: http://www.dimensionalanalysis.org/
.. _`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/
@@ -150,3 +165,5 @@ quantity will be radian.
.. _`Babel`: http://babel.pocoo.org/
.. _`Pandas Extension Types`: https://pandas.pydata.org/pandas-docs/stable/extending.html#extension-types
.. _`pint-pandas Jupyter notebook`: https://github.com/hgrecco/pint-pandas/blob/master/notebooks/pandas_support.ipynb
+.. _`AUTHORS`: https://github.com/hgrecco/pint/blob/master/AUTHORS
+.. _`CHANGES`: https://github.com/hgrecco/pint/blob/master/CHANGES
diff --git a/docs/defining.rst b/docs/defining.rst
index 7aba5bb..152893a 100644
--- a/docs/defining.rst
+++ b/docs/defining.rst
@@ -138,3 +138,17 @@ Same for aliases and derived dimensions:
.. warning::
Units, prefixes, aliases and dimensions added programmatically are forgotten when the
program ends.
+
+
+Units with constants
+--------------------
+
+Some units, such as ``L/100km``, contain constants. These can be defined with a
+leading underscore:
+
+.. doctest::
+
+ >>> ureg.define('_100km = 100 * kilometer')
+ >>> ureg.define('mpg = 1 * mile / gallon')
+ >>> fuel_ec_europe = 5 * ureg.L / ureg._100km
+ >>> fuel_ec_us = (1 / fuel_ec_europe).to(ureg.mpg)
diff --git a/docs/index.rst b/docs/index.rst
index e72ed87..5d22dda 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -128,6 +128,7 @@ User Guide
systems
currencies
pint-pandas.ipynb
+ pint-convert
More information
----------------
diff --git a/docs/numpy.ipynb b/docs/numpy.ipynb
index 2dc6078..41b6491 100644
--- a/docs/numpy.ipynb
+++ b/docs/numpy.ipynb
@@ -24,10 +24,6 @@
"# Import NumPy\n",
"import numpy as np\n",
"\n",
- "# Disable Pint's old fallback behavior (must come before importing Pint)\n",
- "import os\n",
- "os.environ['PINT_ARRAY_PROTOCOL_FALLBACK'] = \"0\"\n",
- "\n",
"# Import Pint\n",
"import pint\n",
"ureg = pint.UnitRegistry()\n",
@@ -778,10 +774,9 @@
"To achive these function and ufunc overrides, Pint uses the ``__array_function__`` and ``__array_ufunc__`` protocols respectively, as recommened by NumPy. This means that functions and ufuncs that Pint does not explicitly handle will error, rather than return a value with units stripped (in contrast to Pint's behavior prior to v0.10). For more\n",
"information on these protocols, see <https://docs.scipy.org/doc/numpy-1.17.0/user/basics.dispatch.html>.\n",
"\n",
- "This behaviour introduces some performance penalties and increased memory usage. Quantities that must be converted to other units require additional memory and CPU cycles. Therefore, for numerically intensive code, you might want to convert the objects first and then use directly the magnitude, such as by using Pint's `wraps` utility (see [wrapping](wrapping.html)).\n",
+ "This behaviour introduces some performance penalties and increased memory usage. Quantities that must be converted to other units require additional memory and CPU cycles. Therefore, for numerically intensive code, you might want to convert the objects first and then use directly the magnitude, such as by using Pint's `wraps` utility (see [wrapping](wrapping.rst)).\n",
"\n",
- "Array interface protocol attributes (such as `__array_struct__` and\n",
- "`__array_interface__`) are available on Pint Quantities by deferring to the corresponding `__array_*` attribute on the magnitude as casted to an ndarray. This has been found to be potentially incorrect and to cause unexpected behavior, and has therefore been deprecated. As of the next minor version of Pint (or when the `PINT_ARRAY_PROTOCOL_FALLBACK` environment variable is set to 0 prior to importing Pint as done at the beginning of this page), attempting to access these attributes will instead raise an AttributeError."
+ "Attempting to access array interface protocol attributes (such as `__array_struct__` and `__array_interface__`) on Pint Quantities will raise an AttributeError, since a Quantity is meant to behave as a \"duck array,\" and not a pure ndarray."
]
}
],
@@ -801,7 +796,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.6.7"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/docs/pint-convert.rst b/docs/pint-convert.rst
new file mode 100644
index 0000000..8d548f8
--- /dev/null
+++ b/docs/pint-convert.rst
@@ -0,0 +1,91 @@
+.. _convert:
+
+Command-line script
+===================
+
+The script `pint-convert` allows a quick conversion to a target system or
+between arbitrary compatible units.
+
+By default, `pint-convert` converts to SI units::
+
+ $ pint-convert 225lb
+ 225 pound = 102.05828325 kg
+
+use the `--sys` argument to change it::
+
+ $ pint-convert --sys US 102kg
+ 102 kilogram = 224.871507429 lb
+
+or specify directly the target units::
+
+ $ pint-convert 102kg lb
+ 102 kilogram = 224.871507429 lb
+
+The input quantity can contain expressions::
+
+ $ pint-convert 7ft+2in
+ 7.166666666666667 foot = 2.1844 m
+
+in some cases parentheses and quotes may be needed::
+
+ $ pint-convert "225lb/(7ft+2in)"
+ 31.3953488372093 pound / foot = 46.7214261353 kg/m
+
+If a number is omitted, 1 is assumed::
+
+ $ pint-convert km mi
+ 1 kilometer = 0.621371192237 mi
+
+The default precision is 12 significant figures, it can be changed with `-p`,
+but note that the accuracy may be affected by floating-point errors::
+
+ $ pint-convert -p 3 mi
+ 1 mile = 1.61e+03 m
+
+ $ pint-convert -p 30 ly km
+ 1 light_year = 9460730472580.80078125 km
+
+Some contexts are automatically enabled, allowing conversion between not fully
+compatible units::
+
+ $ pint-convert 540nm
+ 540 nanometer = 5.4e-07 m
+
+ $ pint-convert kcal/mol
+ $ 1.0 kilocalorie / mole = 4184 kg·m²/mol/s²
+
+ $ pint-convert 540nm kcal/mol
+ 540 nanometer = 52.9471025594 kcal/mol
+
+With the `uncertainties` package, the experimental uncertainty in the physical
+constants is considered, and the result is given in compact notation, with the
+uncertainty in the last figures in parentheses::
+
+ $ pint-convert Eh eV
+ 1 hartree = 27.21138624599(5) eV
+
+The precision is limited by both the maximum number of significant digits (`-p`)
+and the maximum number of uncertainty digits (`-u`, 2 by default)::
+
+ $ pint-convert -p 20 Eh eV
+ 1 hartree = 27.211386245988(52) eV
+
+ $ pint-convert -p 20 -u 4 Eh eV
+ 1 hartree = 27.21138624598847(5207) eV
+
+The uncertainty can be disabled with `-U`)::
+
+ $ pint-convert -p 20 -U Eh eV
+ 1 hartree = 27.211386245988471444 eV
+
+Correlations between experimental constants are also known, and taken into
+account. Use `-C` to disable it::
+
+ $ pint-convert --sys atomic m_p
+ 1 proton_mass = 1836.15267344(11) m_e
+
+ $ pint-convert --sys atomic -C m_p
+ 1 proton_mass = 1836.15267344(79) m_e
+
+Again, note that results may differ slightly, usually in the last figure, from
+more authoritative sources, mainly due to floating-point errors.
diff --git a/docs/pint-pandas.ipynb b/docs/pint-pandas.ipynb
index 390831e..e213aff 100644
--- a/docs/pint-pandas.ipynb
+++ b/docs/pint-pandas.ipynb
@@ -6,6 +6,12 @@
"source": [
"# Pandas support\n",
"\n",
+ "<div class=\"alert alert-warning\">\n",
+ "\n",
+ "**Warning:** pandas support is currently experimental, don't expect everything to work.\n",
+ "\n",
+ "</div>\n",
+ "\n",
"It is convenient to use the Pandas package when dealing with numerical data, so Pint provides PintArray. A PintArray is a Pandas Extension Array, which allows Pandas to recognise the Quantity and store it in Pandas DataFrames and Series."
]
},
@@ -13,6 +19,19 @@
"cell_type": "markdown",
"metadata": {},
"source": [
+ "## Installation\n",
+ "\n",
+ "\n",
+ "Pandas support is provided by `pint-pandas`. It is not available on PyPI yet, to install it use\n",
+ "```\n",
+ "python -m pip install git+https://github.com/hgrecco/pint-pandas.git\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
"## Basic example"
]
},
@@ -531,8 +550,8 @@
}
],
"source": [
- "df = pd.read_csv(io.StringIO(test_data),header=[0,1])\n",
- "# df = pd.read_csv(\"/path/to/test_data.csv\",header=[0,1])\n",
+ "df = pd.read_csv(io.StringIO(test_data), header=[0, 1])\n",
+ "# df = pd.read_csv(\"/path/to/test_data.csv\", header=[0, 1])\n",
"df"
]
},
@@ -690,7 +709,7 @@
}
],
"source": [
- "df_.speed*df_.torque"
+ "df_.speed * df_.torque"
]
},
{
@@ -874,7 +893,7 @@
}
],
"source": [
- "df_['mech power'] = df_.speed*df_.torque\n",
+ "df_['mech power'] = df_.speed * df_.torque\n",
"df_['fluid power'] = df_['fuel flow rate'] * df_['rail pressure']\n",
"df_"
]
@@ -1219,7 +1238,7 @@
}
],
"source": [
- "pint.PintType.ureg.default_format = \"~P\"\n",
+ "pintpandas.PintType.ureg.default_format = \"~P\"\n",
"df_.pint.dequantify()"
]
},
@@ -1372,7 +1391,7 @@
"metadata": {},
"outputs": [],
"source": [
- "PA_ = pint.PintArray"
+ "PA_ = pintpandas.PintArray"
]
},
{
@@ -1388,8 +1407,8 @@
"metadata": {},
"outputs": [],
"source": [
- "ureg=pint.UnitRegistry()\n",
- "Q_=ureg.Quantity"
+ "ureg = pint.UnitRegistry()\n",
+ "Q_ = ureg.Quantity"
]
},
{
@@ -1405,7 +1424,7 @@
"metadata": {},
"outputs": [],
"source": [
- "pint.PintType.ureg = ureg"
+ "pintpandas.PintType.ureg = ureg"
]
},
{
diff --git a/docs/tutorial.rst b/docs/tutorial.rst
index 158d2ad..ac9c2b3 100644
--- a/docs/tutorial.rst
+++ b/docs/tutorial.rst
@@ -251,14 +251,14 @@ or
.. note:: Pint´s rule for parsing strings with a mixture of numbers and
units is that **units are treated with the same precedence as numbers**.
-
+
For example, the unit of
.. doctest::
>>> Q_('3 l / 100 km')
<Quantity(0.03, 'kilometer * liter')>
-
+
may be unexpected first but is a consequence of applying this rule. Use
brackets to get the expected result:
@@ -272,6 +272,38 @@ brackets to get the expected result:
exposed to when parsing information from untrusted sources.
+Strings containing values can be parsed using the ``ureg.parse_pattern`` function. A ``format``-like string with the units defined in it is used as the pattern:
+
+.. doctest::
+
+ >>> input_string = '10 feet 10 inches'
+ >>> pattern = '{feet} feet {inch} inches'
+ >>> ureg.parse_pattern(input_string, pattern)
+ [10.0 <Unit('foot')>, 10.0 <Unit('inch')>]
+
+To search for multiple matches, set the ``many`` parameter to ``True``. The following example also demonstrates how the parser is able to find matches in amongst filler characters:
+
+.. doctest::
+
+ >>> input_string = '10 feet - 20 feet ! 30 feet.'
+ >>> pattern = '{feet} feet'
+ >>> ureg.parse_pattern(input_string, pattern, many=True)
+ [[10.0 <Unit('foot')>], [20.0 <Unit('foot')>], [30.0 <Unit('foot')>]]
+
+The full power of regex can also be employed when writing patterns:
+
+.. doctest::
+
+ >>> input_string = "10` - 20 feet ! 30 ft."
+ >>> pattern = r"{feet}(`| feet| ft)"
+ >>> ureg.parse_pattern(input_string, pattern, many=True)
+ [[10.0 <Unit('foot')>], [20.0 <Unit('foot')>], [30.0 <Unit('foot')>]]
+
+*Note that the curly brackets (``{}``) are converted to a float-matching pattern by the parser.*
+
+This function is useful for tasks such as bulk extraction of units from thousands of uniform strings or even very large texts with units dotted around in no particular pattern.
+
+
.. _sec-string-formatting:
String formatting
@@ -303,12 +335,12 @@ Pint supports float formatting for numpy arrays as well:
>>> # scientific form formatting with unit pretty printing
>>> print('The array is {:+.2E~P}'.format(accel))
The array is [-1.10E+00 +1.00E-06 +1.25E+00 +1.30E+00] m/s²
-
+
Pint also supports 'f-strings'_ from python>=3.6 :
.. doctest::
- >>> accel = 1.3 * ureg['meter/second**2']
+ >>> accel = 1.3 * ureg['meter/second**2']
>>> print(f'The str is {accel}')
The str is 1.3 meter / second ** 2
>>> print(f'The str is {accel:.3e}')
@@ -318,7 +350,7 @@ Pint also supports 'f-strings'_ from python>=3.6 :
>>> print(f'The str is {accel:~.3e}')
The str is 1.300e+00 m / s ** 2
>>> print(f'The str is {accel:~H}')
- The str is 1.3 m/s²
+ The str is 1.3 m/s²
But Pint also extends the standard formatting capabilities for unicode and
LaTeX representations:
@@ -349,11 +381,11 @@ If you want to use abbreviated unit names, prefix the specification with `~`:
The same is true for latex (`L`) and HTML (`H`) specs.
.. note::
- The abbreviated unit is drawn from the unit registry where the 3rd item in the
- equivalence chain (ie 1 = 2 = **3**) will be returned when the prefix '~' is
+ The abbreviated unit is drawn from the unit registry where the 3rd item in the
+ equivalence chain (ie 1 = 2 = **3**) will be returned when the prefix '~' is
used. The 1st item in the chain is the canonical name of the unit.
-The formatting specs (ie 'L', 'H', 'P') can be used with Python string 'formatting
+The formatting specs (ie 'L', 'H', 'P') can be used with Python string 'formatting
syntax'_ for custom float representations. For example, scientific notation:
..doctest::
@@ -387,10 +419,27 @@ Finally, if Babel_ is installed you can translate unit names to any language
>>> accel.format_babel(locale='fr_FR')
'1.3 mètre par seconde²'
-You can also specify the format locale at u
+You can also specify the format locale at the registry level either at creation:
>>> ureg = UnitRegistry(fmt_locale='fr_FR')
+or later:
+
+.. doctest::
+
+ >>> ureg.set_fmt_locale('fr_FR')
+
+and by doing that, string formatting is now localized:
+
+.. doctest::
+
+ >>> str(accel)
+ '1.3 mètre par seconde²'
+ >>> "%s" % accel
+ '1.3 mètre par seconde²'
+ >>> "{}".format(accel)
+ '1.3 mètre par seconde²'
+
Using Pint in your projects
---------------------------
@@ -438,4 +487,4 @@ also define the registry as the application registry::
.. _`serious security problems`: http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
.. _`Babel`: http://babel.pocoo.org/
.. _'formatting syntax': https://docs.python.org/3/library/string.html#format-specification-mini-language
-.. _'f-strings': https://www.python.org/dev/peps/pep-0498/ \ No newline at end of file
+.. _'f-strings': https://www.python.org/dev/peps/pep-0498/
diff --git a/pint/compat.py b/pint/compat.py
index 6fb27ee..e8e1a1b 100644
--- a/pint/compat.py
+++ b/pint/compat.py
@@ -7,7 +7,6 @@
:copyright: 2013 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
-import os
import tokenize
from decimal import Decimal
from io import BytesIO
@@ -37,27 +36,6 @@ class BehaviorChangeWarning(UserWarning):
pass
-array_function_change_msg = """The way Pint handles NumPy operations has changed with the
-implementation of NEP 18. Unimplemented NumPy operations will now fail instead of making
-assumptions about units. Some functions, eg concat, will now return Quanties with units, where
-they returned ndarrays previously. See https://github.com/hgrecco/pint/pull/905.
-
-To hide this warning, wrap your first creation of an array Quantity with
-warnings.catch_warnings(), like the following:
-
-import numpy as np
-import warnings
-from pint import Quantity
-
-with warnings.catch_warnings():
- warnings.simplefilter("ignore")
- Quantity([])
-
-To disable the new behavior, see
-https://www.numpy.org/neps/nep-0018-array-function-protocol.html#implementation
-"""
-
-
try:
import numpy as np
from numpy import ndarray
@@ -93,12 +71,9 @@ try:
return False
HAS_NUMPY_ARRAY_FUNCTION = _test_array_function_protocol()
- SKIP_ARRAY_FUNCTION_CHANGE_WARNING = not HAS_NUMPY_ARRAY_FUNCTION
NP_NO_VALUE = np._NoValue
- ARRAY_FALLBACK = bool(int(os.environ.get("PINT_ARRAY_PROTOCOL_FALLBACK", 1)))
-
except ImportError:
np = None
@@ -110,9 +85,7 @@ except ImportError:
NUMPY_VER = "0"
NUMERIC_TYPES = (Number, Decimal)
HAS_NUMPY_ARRAY_FUNCTION = False
- SKIP_ARRAY_FUNCTION_CHANGE_WARNING = True
NP_NO_VALUE = None
- ARRAY_FALLBACK = False
def _to_magnitude(value, force_ndarray=False, force_ndarray_like=False):
if force_ndarray or force_ndarray_like:
diff --git a/pint/constants_en.txt b/pint/constants_en.txt
index fa485fa..c3ecbf1 100644
--- a/pint/constants_en.txt
+++ b/pint/constants_en.txt
@@ -8,7 +8,7 @@
#### MATHEMATICAL CONSTANTS ####
# As computed by Maxima with fpprec:50
-pi = 3.1415926535897932384626433832795028841971693993751 = π # pi
+pi = 3.1415926535897932384626433832795028841971693993751 = π # pi
tansec = 4.8481368111333441675396429478852851658848753880815e-6 # tangent of 1 arc-second ~ arc_second/radian
ln10 = 2.3025850929940456840179914546843642076011014886288 # natural logarithm of 10
wien_x = 4.9651142317442763036987591313228939440555849867973 # solution to (x-5)*exp(x)+5 = 0 => x = W(5/exp(5))+5
diff --git a/pint/default_en.txt b/pint/default_en.txt
index 6d5fe6a..8bd4133 100644
--- a/pint/default_en.txt
+++ b/pint/default_en.txt
@@ -36,7 +36,7 @@
# [density] = [mass] / [volume]
#
# Note that primary dimensions don't need to be declared; they can be
-# defined or the first time in a unit definition.
+# defined for the first time in a unit definition.
# E.g. see below `meter = [length]`
#
#
diff --git a/pint/default_en_0.6.txt b/pint/default_en_0.6.txt
deleted file mode 100644
index fb722c0..0000000
--- a/pint/default_en_0.6.txt
+++ /dev/null
@@ -1,360 +0,0 @@
-# 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
diff --git a/pint/definitions.py b/pint/definitions.py
index 327cc87..338d1fc 100644
--- a/pint/definitions.py
+++ b/pint/definitions.py
@@ -15,8 +15,8 @@ from .errors import DefinitionSyntaxError
from .util import ParserHelper, UnitsContainer, _is_dim
-class ParsedDefinition(
- namedtuple("ParsedDefinition", "name symbol aliases value rhs_parts")
+class PreprocessedDefinition(
+ namedtuple("PreprocessedDefinition", "name symbol aliases value rhs_parts")
):
"""Splits a definition into the constitutive parts.
@@ -126,7 +126,7 @@ class Definition:
Parameters
----------
- definition : str or ParsedDefinition
+ definition : str or PreprocessedDefinition
non_int_type : type
Returns
@@ -135,7 +135,7 @@ class Definition:
"""
if isinstance(definition, str):
- definition = ParsedDefinition.from_string(definition)
+ definition = PreprocessedDefinition.from_string(definition)
if definition.name.startswith("@alias "):
return AliasDefinition.from_string(definition, non_int_type)
@@ -186,7 +186,7 @@ class PrefixDefinition(Definition):
@classmethod
def from_string(cls, definition, non_int_type=float):
if isinstance(definition, str):
- definition = ParsedDefinition.from_string(definition)
+ definition = PreprocessedDefinition.from_string(definition)
aliases = tuple(alias.strip("-") for alias in definition.aliases)
if definition.symbol:
@@ -230,7 +230,7 @@ class UnitDefinition(Definition):
@classmethod
def from_string(cls, definition, non_int_type=float):
if isinstance(definition, str):
- definition = ParsedDefinition.from_string(definition)
+ definition = PreprocessedDefinition.from_string(definition)
if ";" in definition.value:
[converter, modifiers] = definition.value.split(";", 2)
@@ -296,7 +296,7 @@ class DimensionDefinition(Definition):
@classmethod
def from_string(cls, definition, non_int_type=float):
if isinstance(definition, str):
- definition = ParsedDefinition.from_string(definition)
+ definition = PreprocessedDefinition.from_string(definition)
converter = ParserHelper.from_string(definition.value, non_int_type)
@@ -338,7 +338,7 @@ class AliasDefinition(Definition):
def from_string(cls, definition, non_int_type=float):
if isinstance(definition, str):
- definition = ParsedDefinition.from_string(definition)
+ definition = PreprocessedDefinition.from_string(definition)
name = definition.name[len("@alias ") :].lstrip()
return AliasDefinition(name, tuple(definition.rhs_parts))
diff --git a/pint/numpy_func.py b/pint/numpy_func.py
index 1fd7d8c..c03887b 100644
--- a/pint/numpy_func.py
+++ b/pint/numpy_func.py
@@ -654,20 +654,22 @@ def _pad(array, pad_width, mode="constant", **kwargs):
def _recursive_convert(arg, unit):
if iterable(arg):
return tuple(_recursive_convert(a, unit=unit) for a in arg)
- elif _is_quantity(arg):
- return arg.m_as(unit)
- else:
- return arg
+ elif not _is_quantity(arg):
+ if arg == 0 or np.isnan(arg):
+ arg = unit._REGISTRY.Quantity(arg, unit)
+ else:
+ arg = unit._REGISTRY.Quantity(arg, "dimensionless")
+
+ return arg.m_as(unit)
# pad only dispatches on array argument, so we know it is a Quantity
units = array.units
# Handle flexible constant_values and end_values, converting to units if Quantity
# and ignoring if not
- if mode == "constant":
- kwargs["constant_values"] = _recursive_convert(kwargs["constant_values"], units)
- elif mode == "linear_ramp":
- kwargs["end_values"] = _recursive_convert(kwargs["end_values"], units)
+ for key in ("constant_values", "end_values"):
+ if key in kwargs:
+ kwargs[key] = _recursive_convert(kwargs[key], units)
return units._REGISTRY.Quantity(
np.pad(array._magnitude, pad_width, mode=mode, **kwargs), units
diff --git a/pint/pint-convert b/pint/pint-convert
new file mode 100755
index 0000000..da7df23
--- /dev/null
+++ b/pint/pint-convert
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+
+"""
+ pint-convert
+ ~~~~~~~~~~~~
+
+ :copyright: 2020 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import argparse
+import re
+import sys
+
+from pint import UnitRegistry
+
+parser = argparse.ArgumentParser(description='Unit converter.', usage=argparse.SUPPRESS)
+parser.add_argument('-s', '--system', metavar='sys', default='SI', help='unit system to convert to (default: SI)')
+parser.add_argument('-p', '--prec', metavar='n', type=int, default=12, help='number of maximum significant figures (default: 12)')
+parser.add_argument('-u', '--prec-unc', metavar='n', type=int, default=2, help='number of maximum uncertainty digits (default: 2)')
+parser.add_argument('-U', '--no-unc', dest='unc', action='store_false', help='ignore uncertainties in constants')
+parser.add_argument('-C', '--no-corr', dest='corr', action='store_false', help='ignore correlations between constants')
+parser.add_argument('fr', metavar='from', type=str, help='unit or quantity to convert from')
+parser.add_argument('to', type=str, nargs='?', help='unit to convert to')
+try:
+ args = parser.parse_args()
+except SystemExit:
+ parser.print_help()
+ raise
+
+ureg = UnitRegistry()
+ureg.auto_reduce_dimensions = True
+ureg.autoconvert_offset_to_baseunit = True
+ureg.enable_contexts('Gau', 'ESU', 'sp', 'energy', 'boltzmann')
+ureg.default_system = args.system
+
+if args.unc:
+ import uncertainties
+ # Measured constans subject to correlation
+ # R_i: Rydberg constant
+ # g_e: Electron g factor
+ # m_u: Atomic mass constant
+ # m_e: Electron mass
+ # m_p: Proton mass
+ # m_n: Neutron mass
+ R_i = (ureg._units['R_inf'].converter.scale, 0.0000000000021e7)
+ g_e = (ureg._units['g_e'].converter.scale, 0.00000000000035)
+ m_u = (ureg._units['m_u'].converter.scale, 0.00000000050e-27)
+ m_e = (ureg._units['m_e'].converter.scale, 0.00000000028e-30)
+ m_p = (ureg._units['m_p'].converter.scale, 0.00000000051e-27)
+ m_n = (ureg._units['m_n'].converter.scale, 0.00000000095e-27)
+ if args.corr:
+ # Correlation matrix between measured constants (to be completed below)
+ # R_i g_e m_u m_e m_p m_n
+ corr = [[ 1.0 , -0.00206, 0.00369, 0.00436, 0.00194, 0.00233], # R_i
+ [ -0.00206, 1.0 , 0.99029, 0.99490, 0.97560, 0.52445], # g_e
+ [ 0.00369, 0.99029, 1.0 , 0.99536, 0.98516, 0.52959], # m_u
+ [ 0.00436, 0.99490, 0.99536, 1.0 , 0.98058, 0.52714], # m_e
+ [ 0.00194, 0.97560, 0.98516, 0.98058, 1.0 , 0.51521], # m_p
+ [ 0.00233, 0.52445, 0.52959, 0.52714, 0.51521, 1.0 ]] # m_n
+ (R_i, g_e, m_u, m_e, m_p, m_n) = uncertainties.correlated_values_norm([R_i, g_e, m_u, m_e, m_p, m_n], corr)
+ else:
+ R_i = uncertainties.ufloat(*R_i)
+ g_e = uncertainties.ufloat(*g_e)
+ m_u = uncertainties.ufloat(*m_u)
+ m_e = uncertainties.ufloat(*m_e)
+ m_p = uncertainties.ufloat(*m_p)
+ m_n = uncertainties.ufloat(*m_n)
+ ureg._units['R_inf'].converter.scale = R_i
+ ureg._units['g_e'].converter.scale = g_e
+ ureg._units['m_u'].converter.scale = m_u
+ ureg._units['m_e'].converter.scale = m_e
+ ureg._units['m_p'].converter.scale = m_p
+ ureg._units['m_n'].converter.scale = m_n
+
+ # Measured constants with zero correlation
+ ureg._units['gravitational_constant'].converter.scale = uncertainties.ufloat(ureg._units['gravitational_constant'].converter.scale, 0.00015e-11)
+ ureg._units['d_220'].converter.scale = uncertainties.ufloat(ureg._units['d_220'].converter.scale, 0.000000032e-10)
+ ureg._units['K_alpha_Cu_d_220'].converter.scale = uncertainties.ufloat(ureg._units['K_alpha_Cu_d_220'].converter.scale, 0.00000022)
+ ureg._units['K_alpha_Mo_d_220'].converter.scale = uncertainties.ufloat(ureg._units['K_alpha_Mo_d_220'].converter.scale, 0.00000019)
+ ureg._units['K_alpha_W_d_220'].converter.scale = uncertainties.ufloat(ureg._units['K_alpha_W_d_220'].converter.scale, 0.000000098)
+
+ ureg._root_units_cache = dict()
+ ureg._build_cache()
+
+def convert(u_from, u_to=None, unc=None, factor=None):
+ q = ureg.Quantity(u_from)
+ fmt = '.{}g'.format(args.prec)
+ if unc:
+ q = q.plus_minus(unc)
+ if u_to:
+ nq = q.to(u_to)
+ else:
+ nq = q.to_base_units()
+ if (factor):
+ q *= ureg.Quantity(factor)
+ nq *= ureg.Quantity(factor).to_base_units()
+ prec_unc = use_unc(nq.magnitude, fmt, args.prec_unc)
+ if (prec_unc > 0):
+ fmt = '.{}uS'.format(prec_unc)
+ else:
+ try:
+ nq = nq.magnitude.n * nq.units
+ except:
+ pass
+ fmt = '{:' + fmt + '} {:~P}'
+ print(('{:} = ' + fmt).format(q, nq.magnitude, nq.units))
+
+def use_unc(num, fmt, prec_unc):
+ unc = 0
+ try:
+ if (isinstance(num, uncertainties.UFloat)):
+ full = ('{:'+fmt+'}').format(num)
+ unc = re.search(r'\+\/-[0.]*([\d.]*)', full).group(1)
+ unc = len(unc.replace('.', ''))
+ except:
+ pass
+ return max(0, min(prec_unc, unc))
+
+convert(args.fr, args.to)
diff --git a/pint/quantity.py b/pint/quantity.py
index 1d9ed21..d434a0f 100644
--- a/pint/quantity.py
+++ b/pint/quantity.py
@@ -20,13 +20,9 @@ import warnings
from pkg_resources.extern.packaging import version
-from .compat import SKIP_ARRAY_FUNCTION_CHANGE_WARNING # noqa: F401
from .compat import (
- ARRAY_FALLBACK,
NUMPY_VER,
- BehaviorChangeWarning,
_to_magnitude,
- array_function_change_msg,
babel_parse,
eq,
is_duck_array_type,
@@ -165,8 +161,6 @@ class Quantity(PrettyIPython, SharedRegistryObject):
return _unpickle, (Quantity, self.magnitude, self._units)
def __new__(cls, value, units=None):
- global SKIP_ARRAY_FUNCTION_CHANGE_WARNING
-
if is_upcast_type(type(value)):
raise TypeError(f"Quantity cannot wrap upcast type {type(value)}")
elif units is None:
@@ -219,12 +213,6 @@ class Quantity(PrettyIPython, SharedRegistryObject):
inst.__used = False
inst.__handling = None
- if not SKIP_ARRAY_FUNCTION_CHANGE_WARNING and isinstance(
- inst._magnitude, ndarray
- ):
- warnings.warn(array_function_change_msg, BehaviorChangeWarning)
- SKIP_ARRAY_FUNCTION_CHANGE_WARNING = True
-
return inst
@property
@@ -256,6 +244,9 @@ class Quantity(PrettyIPython, SharedRegistryObject):
return ret
def __str__(self):
+ if self._REGISTRY.fmt_locale is not None:
+ return self.format_babel()
+
return format(self)
def __bytes__(self):
@@ -274,6 +265,9 @@ class Quantity(PrettyIPython, SharedRegistryObject):
_exp_pattern = re.compile(r"([0-9]\.?[0-9]*)e(-?)\+?0*([0-9]+)")
def __format__(self, spec):
+ if self._REGISTRY.fmt_locale is not None:
+ return self.format_babel(spec)
+
spec = spec or self.default_format
if "L" in spec:
@@ -508,6 +502,40 @@ class Quantity(PrettyIPython, SharedRegistryObject):
return self._REGISTRY.get_compatible_units(self._units)
+ def is_compatible_with(self, other, *contexts, **ctx_kwargs):
+ """ check if the other object is compatible
+
+ Parameters
+ ----------
+ other
+ The object to check. Treated as dimensionless if not a
+ Quantity, Unit or str.
+ *contexts : str or pint.Context
+ Contexts to use in the transformation.
+ **ctx_kwargs :
+ Values for the Context/s
+
+ Returns
+ -------
+ bool
+ """
+ if contexts:
+ try:
+ self.to(other, *contexts, **ctx_kwargs)
+ return True
+ except DimensionalityError:
+ return False
+
+ if isinstance(other, (self._REGISTRY.Quantity, self._REGISTRY.Unit)):
+ return self.dimensionality == other.dimensionality
+
+ if isinstance(other, str):
+ return (
+ self.dimensionality == self._REGISTRY.parse_units(other).dimensionality
+ )
+
+ return self.dimensionless
+
def _convert_magnitude_not_inplace(self, other, *contexts, **ctx_kwargs):
if contexts:
with self._REGISTRY.context(*contexts, **ctx_kwargs):
@@ -693,6 +721,8 @@ class Quantity(PrettyIPython, SharedRegistryObject):
if unit is None:
unit = infer_base_unit(self)
+ else:
+ unit = infer_base_unit(self.__class__(1, unit))
q_base = self.to(unit)
@@ -1665,34 +1695,7 @@ class Quantity(PrettyIPython, SharedRegistryObject):
def __getattr__(self, item):
if item.startswith("__array_"):
# Handle array protocol attributes other than `__array__`
- if ARRAY_FALLBACK:
- # Deprecated fallback behavior
- warnings.warn(
- (
- f"Array protocol attribute {item} accessed, with unit of the "
- "Quantity being stripped. This attribute will become unavailable "
- "in the next minor version of Pint. To make this potentially "
- "incorrect attribute unavailable now, set the "
- "PINT_ARRAY_PROTOCOL_FALLBACK environment variable to 0 before "
- "importing Pint."
- ),
- DeprecationWarning,
- stacklevel=2,
- )
-
- if is_duck_array_type(type(self._magnitude)):
- # Defer to magnitude, and don't catch any AttributeErrors
- return getattr(self._magnitude, item)
- else:
- # If an `__array_` attribute is requested but the magnitude is not
- # a duck array, we convert the magnitude to a numpy ndarray.
- magnitude_as_array = _to_magnitude(
- self._magnitude, force_ndarray=True
- )
- return getattr(magnitude_as_array, item)
- else:
- # TODO (next minor version): ARRAY_FALLBACK is removed and this becomes the standard behavior
- raise AttributeError(f"Array protocol attribute {item} not available.")
+ raise AttributeError(f"Array protocol attribute {item} not available.")
elif item in HANDLED_UFUNCS or item in self._wrapped_numpy_methods:
magnitude_as_duck_array = _to_magnitude(
self._magnitude, force_ndarray_like=True
diff --git a/pint/registry.py b/pint/registry.py
index 9379770..99873ed 100644
--- a/pint/registry.py
+++ b/pint/registry.py
@@ -84,6 +84,19 @@ from .util import (
_BLOCK_RE = re.compile(r" |\(")
+@functools.lru_cache()
+def pattern_to_regex(pattern):
+ if hasattr(pattern, "finditer"):
+ pattern = pattern.pattern
+
+ # Replace "{unit_name}" match string with float regex with unit_name as group
+ pattern = re.sub(
+ r"{(\w+)}", r"(?P<\1>[+-]?[0-9]+(?:.[0-9]+)?(?:[Ee][+-]?[0-9]+)?)", pattern
+ )
+
+ return re.compile(pattern)
+
+
class RegistryMeta(type):
"""This is just to call after_init at the right time
instead of asking the developer to do it when subclassing.
@@ -161,6 +174,9 @@ class BaseRegistry(metaclass=RegistryMeta):
#: type: Dict[str, (SourceIterator -> None)]
_parsers = None
+ #: Babel.Locale instance or None
+ fmt_locale = None
+
#: List to be used in addition of units when dir(registry) is called.
#: Also used for autocompletion in IPython.
_dir = [
@@ -207,7 +223,7 @@ class BaseRegistry(metaclass=RegistryMeta):
self.auto_reduce_dimensions = auto_reduce_dimensions
#: Default locale identifier string, used when calling format_babel without explicit locale.
- self.fmt_locale = self.set_fmt_locale(fmt_locale)
+ self.set_fmt_locale(fmt_locale)
#: Numerical type used for non integer values.
self.non_int_type = non_int_type
@@ -871,6 +887,33 @@ class BaseRegistry(metaclass=RegistryMeta):
src_dim = self._get_dimensionality(input_units)
return self._cache.dimensional_equivalents[src_dim]
+ def is_compatible_with(self, obj1, obj2, *contexts, **ctx_kwargs):
+ """ check if the other object is compatible
+
+ Parameters
+ ----------
+ obj1, obj2
+ The objects to check against each other. Treated as
+ dimensionless if not a Quantity, Unit or str.
+ *contexts : str or pint.Context
+ Contexts to use in the transformation.
+ **ctx_kwargs :
+ Values for the Context/s
+
+ Returns
+ -------
+ bool
+ """
+ if isinstance(obj1, (self.Quantity, self.Unit)):
+ return obj1.is_compatible_with(obj2, *contexts, **ctx_kwargs)
+
+ if isinstance(obj1, str):
+ return self.parse_expression(obj1).is_compatible_with(
+ obj2, *contexts, **ctx_kwargs
+ )
+
+ return not isinstance(obj2, (self.Quantity, self.Unit))
+
def convert(self, value, src, dst, inplace=False):
"""Convert value from some source to destination units.
@@ -1114,6 +1157,62 @@ class BaseRegistry(metaclass=RegistryMeta):
else:
raise Exception("unknown token type")
+ def parse_pattern(
+ self, input_string, pattern, case_sensitive=True, use_decimal=False, many=False
+ ):
+ """Parse a string with a given regex pattern and returns result.
+
+ Parameters
+ ----------
+ input_string :
+
+ pattern_string:
+ The regex parse string
+ case_sensitive :
+ (Default value = True)
+ use_decimal :
+ (Default value = False)
+ many :
+ Match many results
+ (Default value = False)
+
+
+ Returns
+ -------
+
+ """
+
+ if not input_string:
+ return self.Quantity(1)
+
+ # Parse string
+ pattern = pattern_to_regex(pattern)
+ matched = re.finditer(pattern, input_string)
+
+ # Extract result(s)
+ results = []
+ for match in matched:
+ # Extract units from result
+ match = match.groupdict()
+
+ # Parse units
+ units = []
+ for unit, value in match.items():
+ # Construct measure by multiplying value by unit
+ units.append(
+ float(value)
+ * self.parse_expression(unit, case_sensitive, use_decimal)
+ )
+
+ # Add to results
+ results.append(units)
+
+ # Return first match only
+ if not many:
+ return results[0]
+
+ return results
+
def parse_expression(
self, input_string, case_sensitive=True, use_decimal=False, **values,
):
diff --git a/pint/testsuite/helpers.py b/pint/testsuite/helpers.py
index 5a91391..2bf743c 100644
--- a/pint/testsuite/helpers.py
+++ b/pint/testsuite/helpers.py
@@ -67,6 +67,10 @@ def requires_babel():
return unittest.skipUnless(HAS_BABEL, "Requires Babel with units support")
+def requires_not_babel():
+ return unittest.skipIf(HAS_BABEL, "Requires Babel is not installed")
+
+
def requires_uncertainties():
return unittest.skipUnless(HAS_UNCERTAINTIES, "Requires Uncertainties")
diff --git a/pint/testsuite/test_babel.py b/pint/testsuite/test_babel.py
index 2aac0cd..def7c83 100644
--- a/pint/testsuite/test_babel.py
+++ b/pint/testsuite/test_babel.py
@@ -5,6 +5,14 @@ from pint.testsuite import BaseTestCase, helpers
class TestBabel(BaseTestCase):
+ @helpers.requires_not_babel()
+ def test_no_babel(self):
+ ureg = UnitRegistry()
+ distance = 24.0 * ureg.meter
+ self.assertRaises(
+ Exception, distance.format_babel, locale="fr_FR", length="long"
+ )
+
@helpers.requires_babel()
def test_format(self):
ureg = UnitRegistry()
@@ -46,9 +54,32 @@ class TestBabel(BaseTestCase):
mks = ureg.get_system("mks")
self.assertEqual(mks.format_babel(locale="fr_FR"), "métrique")
- def test_nobabel(self):
+ @helpers.requires_babel()
+ def test_no_registry_locale(self):
ureg = UnitRegistry()
distance = 24.0 * ureg.meter
self.assertRaises(
- Exception, distance.format_babel, locale="fr_FR", length="long"
+ Exception, distance.format_babel,
)
+
+ @helpers.requires_babel()
+ def test_str(self):
+ ureg = UnitRegistry()
+ d = 24.0 * ureg.meter
+
+ s = "24.0 meter"
+ self.assertEqual(str(d), s)
+ self.assertEqual("%s" % d, s)
+ self.assertEqual("{}".format(d), s)
+
+ ureg.set_fmt_locale("fr_FR")
+ s = "24.0 mètres"
+ self.assertEqual(str(d), s)
+ self.assertEqual("%s" % d, s)
+ self.assertEqual("{}".format(d), s)
+
+ ureg.set_fmt_locale(None)
+ s = "24.0 meter"
+ self.assertEqual(str(d), s)
+ self.assertEqual("%s" % d, s)
+ self.assertEqual("{}".format(d), s)
diff --git a/pint/testsuite/test_issues.py b/pint/testsuite/test_issues.py
index 3036022..5f42717 100644
--- a/pint/testsuite/test_issues.py
+++ b/pint/testsuite/test_issues.py
@@ -697,6 +697,24 @@ class TestIssues(QuantityTestCase):
with self.assertRaises(DimensionalityError):
q.to("joule")
+ def test_issue507(self):
+ # leading underscore in unit works with numbers
+ ureg.define("_100km = 100 * kilometer")
+ battery_ec = 16 * ureg.kWh / ureg._100km # noqa: F841
+ # ... but not with text
+ ureg.define("_home = 4700 * kWh / year")
+ with self.assertRaises(AttributeError):
+ home_elec_power = 1 * ureg._home # noqa: F841
+ # ... or with *only* underscores
+ ureg.define("_ = 45 * km")
+ with self.assertRaises(AttributeError):
+ one_blank = 1 * ureg._ # noqa: F841
+
+ def test_issue960(self):
+ q = (1 * ureg.nanometer).to_compact("micrometer")
+ assert q.units == ureg.nanometer
+ assert q.magnitude == 1
+
try:
diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py
index 0efb476..8e3d116 100644
--- a/pint/testsuite/test_numpy.py
+++ b/pint/testsuite/test_numpy.py
@@ -1,7 +1,6 @@
import copy
import operator as op
import unittest
-from unittest.mock import patch
from pint import DimensionalityError, OffsetUnitCalculusError, UnitStrippedWarning
from pint.compat import np
@@ -885,7 +884,7 @@ class TestNumpyUnclassified(TestNumpyMethods):
@helpers.requires_array_function_protocol()
def test_result_type_numpy_func(self):
- self.assertEqual(np.result_type(self.q), np.dtype("int64"))
+ self.assertEqual(np.result_type(self.q), np.dtype("int"))
@helpers.requires_array_function_protocol()
def test_nan_to_num_numpy_func(self):
@@ -1031,28 +1030,15 @@ class TestNumpyUnclassified(TestNumpyMethods):
np.array([[1, 0, 2], [3, 0, 4]]) * self.ureg.m,
)
- @patch("pint.quantity.ARRAY_FALLBACK", False)
def test_ndarray_downcast(self):
with self.assertWarns(UnitStrippedWarning):
np.asarray(self.q)
- @patch("pint.quantity.ARRAY_FALLBACK", False)
def test_ndarray_downcast_with_dtype(self):
with self.assertWarns(UnitStrippedWarning):
qarr = np.asarray(self.q, dtype=np.float64)
self.assertEqual(qarr.dtype, np.float64)
- def test_array_protocol_fallback(self):
- with self.assertWarns(DeprecationWarning) as cm:
- for attr in ("__array_struct__", "__array_interface__"):
- getattr(self.q, attr)
- warning_text = str(cm.warnings[0].message)
- self.assertTrue(
- f"unit of the Quantity being stripped" in warning_text
- and "will become unavailable" in warning_text
- )
-
- @patch("pint.quantity.ARRAY_FALLBACK", False)
def test_array_protocol_unavailable(self):
for attr in ("__array_struct__", "__array_interface__"):
self.assertRaises(AttributeError, getattr, self.q, attr)
@@ -1067,14 +1053,32 @@ class TestNumpyUnclassified(TestNumpyMethods):
def test_pad(self):
# Tests reproduced with modification from NumPy documentation
a = [1, 2, 3, 4, 5] * self.ureg.m
+ b = self.Q_([4.0, 6.0, 8.0, 9.0, -3.0], "degC")
+
+ self.assertQuantityEqual(
+ np.pad(a, (2, 3), "constant"), [0, 0, 1, 2, 3, 4, 5, 0, 0, 0] * self.ureg.m,
+ )
+ self.assertQuantityEqual(
+ np.pad(a, (2, 3), "constant", constant_values=(0, 600 * self.ureg.cm)),
+ [0, 0, 1, 2, 3, 4, 5, 6, 6, 6] * self.ureg.m,
+ )
self.assertQuantityEqual(
- np.pad(a, (2, 3), "constant", constant_values=(4, 600 * self.ureg.cm)),
- [4, 4, 1, 2, 3, 4, 5, 6, 6, 6] * self.ureg.m,
+ np.pad(
+ b, (2, 1), "constant", constant_values=(np.nan, self.Q_(10, "degC"))
+ ),
+ self.Q_([np.nan, np.nan, 4, 6, 8, 9, -3, 10], "degC"),
+ )
+ self.assertRaises(
+ DimensionalityError, np.pad, a, (2, 3), "constant", constant_values=4
)
self.assertQuantityEqual(
np.pad(a, (2, 3), "edge"), [1, 1, 1, 2, 3, 4, 5, 5, 5, 5] * self.ureg.m
)
self.assertQuantityEqual(
+ np.pad(a, (2, 3), "linear_ramp"),
+ [0, 0, 1, 2, 3, 4, 5, 3, 1, 0] * self.ureg.m,
+ )
+ self.assertQuantityEqual(
np.pad(a, (2, 3), "linear_ramp", end_values=(5, -4) * self.ureg.m),
[5, 3, 1, 2, 3, 4, 5, 2, -1, -4] * self.ureg.m,
)
diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py
index fbfd773..51faf1a 100644
--- a/pint/testsuite/test_quantity.py
+++ b/pint/testsuite/test_quantity.py
@@ -6,7 +6,7 @@ import warnings
from unittest.mock import patch
from pint import DimensionalityError, OffsetUnitCalculusError, UnitRegistry
-from pint.compat import BehaviorChangeWarning, np
+from pint.compat import np
from pint.testsuite import QuantityTestCase, helpers
from pint.testsuite.parameterized import ParameterizedTestCase
from pint.unit import UnitsContainer
@@ -531,11 +531,8 @@ class TestQuantity(QuantityTestCase):
iter(x)
@helpers.requires_array_function_protocol()
- @patch("pint.quantity.SKIP_ARRAY_FUNCTION_CHANGE_WARNING", False)
- def test_array_function_warning_on_creation(self):
- # Test that warning is raised on first creation, but not second
- with self.assertWarns(BehaviorChangeWarning):
- self.Q_([])
+ def test_no_longer_array_function_warning_on_creation(self):
+ # Test that warning is no longer raised on first creation
with warnings.catch_warnings():
warnings.filterwarnings("error")
self.Q_([])
diff --git a/pint/testsuite/test_unit.py b/pint/testsuite/test_unit.py
index 0b6c086..d7eb649 100644
--- a/pint/testsuite/test_unit.py
+++ b/pint/testsuite/test_unit.py
@@ -658,6 +658,47 @@ class TestRegistry(QuantityTestCase):
self.assertEqual(ureg.parse_units(""), ureg.Unit(""))
self.assertRaises(ValueError, ureg.parse_units, "2 * meter")
+ def test_parse_string_pattern(self):
+ ureg = self.ureg
+ self.assertEqual(
+ ureg.parse_pattern("10'11", r"{foot}'{inch}"),
+ [ureg.Quantity(10.0, "foot"), ureg.Quantity(11.0, "inch")],
+ )
+
+ def test_parse_string_pattern_no_preprocess(self):
+ """Were preprocessors enabled, this would be interpreted as 10*11, not
+ two separate units, and thus cause the parsing to fail"""
+ ureg = self.ureg
+ self.assertEqual(
+ ureg.parse_pattern("10 11", r"{kg} {lb}"),
+ [ureg.Quantity(10.0, "kilogram"), ureg.Quantity(11.0, "pound")],
+ )
+
+ def test_parse_pattern_many_results(self):
+ ureg = self.ureg
+ self.assertEqual(
+ ureg.parse_pattern(
+ "1.5kg or 2kg will be fine, if you do not have 3kg",
+ r"{kg}kg",
+ many=True,
+ ),
+ [
+ [ureg.Quantity(1.5, "kilogram")],
+ [ureg.Quantity(2.0, "kilogram")],
+ [ureg.Quantity(3.0, "kilogram")],
+ ],
+ )
+
+ def test_parse_pattern_many_results_two_units(self):
+ ureg = self.ureg
+ self.assertEqual(
+ ureg.parse_pattern("10'10 or 10'11", "{foot}'{inch}", many=True),
+ [
+ [ureg.Quantity(10.0, "foot"), ureg.Quantity(10.0, "inch")],
+ [ureg.Quantity(10.0, "foot"), ureg.Quantity(11.0, "inch")],
+ ],
+ )
+
class TestCompatibleUnits(QuantityTestCase):
FORCE_NDARRAY = False
diff --git a/pint/unit.py b/pint/unit.py
index 5db27cd..25084b4 100644
--- a/pint/unit.py
+++ b/pint/unit.py
@@ -15,6 +15,7 @@ from numbers import Number
from .compat import NUMERIC_TYPES, is_upcast_type
from .definitions import UnitDefinition
+from .errors import DimensionalityError
from .formatting import siunitx_format_unit
from .util import PrettyIPython, SharedRegistryObject, UnitsContainer
@@ -143,6 +144,40 @@ class Unit(PrettyIPython, SharedRegistryObject):
return self._REGISTRY.get_compatible_units(self)
+ def is_compatible_with(self, other, *contexts, **ctx_kwargs):
+ """ check if the other object is compatible
+
+ Parameters
+ ----------
+ other
+ The object to check. Treated as dimensionless if not a
+ Quantity, Unit or str.
+ *contexts : str or pint.Context
+ Contexts to use in the transformation.
+ **ctx_kwargs :
+ Values for the Context/s
+
+ Returns
+ -------
+ bool
+ """
+ if contexts:
+ try:
+ (1 * self).to(other, *contexts, **ctx_kwargs)
+ return True
+ except DimensionalityError:
+ return False
+
+ if isinstance(other, (self._REGISTRY.Quantity, self._REGISTRY.Unit)):
+ return self.dimensionality == other.dimensionality
+
+ if isinstance(other, str):
+ return (
+ self.dimensionality == self._REGISTRY.parse_units(other).dimensionality
+ )
+
+ return self.dimensionless
+
def __mul__(self, other):
if self._check(other):
if isinstance(other, self.__class__):
diff --git a/pint/util.py b/pint/util.py
index 68bed2e..aeebff1 100644
--- a/pint/util.py
+++ b/pint/util.py
@@ -897,12 +897,16 @@ def infer_base_unit(q):
def getattr_maybe_raise(self, item):
- """Helper function to invoke at the beginning of all overridden ``__getattr__``
- methods. Raise AttributeError if the user tries to ask for a _ or __ attribute.
+ """Helper function invoked at start of all overridden ``__getattr__``.
+
+ Raise AttributeError if the user tries to ask for a _ or __ attribute,
+ *unless* it is immediately followed by a number, to enable units
+ encompassing constants, such as ``L / _100km``.
Parameters
----------
- item :
+ item : string
+ Item to be found.
Returns
@@ -911,7 +915,11 @@ def getattr_maybe_raise(self, item):
"""
# Double-underscore attributes are tricky to detect because they are
# automatically prefixed with the class name - which may be a subclass of self
- if item.startswith("_") or item.endswith("__"):
+ if (
+ item.endswith("__")
+ or len(item.lstrip("_")) == 0
+ or (item.startswith("_") and not item.lstrip("_")[0].isdigit())
+ ):
raise AttributeError("%r object has no attribute %r" % (self, item))
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..771af68
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,5 @@
+[build-system]
+requires = ["setuptools>=41", "wheel", "setuptools_scm[toml]>=3.4.3"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools_scm]
diff --git a/setup.cfg b/setup.cfg
index a95b2ca..2777599 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -4,7 +4,7 @@ author = Hernan E. Grecco
author_email = hernan.grecco@gmail.com
license = BSD
description = Physical quantities module
-long_description = file: README.rst, AUTHORS, CHANGES
+long_description = file: README.rst
keywords = physical, quantities, unit, conversion, science
url = https://github.com/hgrecco/pint
classifiers =
@@ -30,6 +30,7 @@ python_requires = >=3.6
install_requires = setuptools
setup_requires = setuptools; setuptools_scm
test_suite = pint.testsuite.testsuite
+scripts = pint/pint-convert
[options.extras_require]
numpy = numpy >= 1.14
@@ -74,4 +75,4 @@ use_parentheses=True
line_length=88
[zest.releaser]
-python-file-with-version = version.py \ No newline at end of file
+python-file-with-version = version.py
diff --git a/setup.py b/setup.py
index 57e6ccd..f4f9665 100644
--- a/setup.py
+++ b/setup.py
@@ -2,4 +2,4 @@
from setuptools import setup
if __name__ == "__main__":
- setup(use_scm_version=True)
+ setup()
diff --git a/version.py b/version.py
index 17a86c7..16c455f 100644
--- a/version.py
+++ b/version.py
@@ -2,5 +2,5 @@
# flake8: noqa
# fmt: off
-__version__ = '0.11.dev0'
+__version__ = '0.12.dev0'
# fmt: on