Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-Pint for openSUSE:Factory checked in at 2021-01-26 14:46:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-Pint (Old) and /work/SRC/openSUSE:Factory/.python-Pint.new.28504 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-Pint" Tue Jan 26 14:46:04 2021 rev:11 rq:865060 version:0.16.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-Pint/python-Pint.changes 2020-09-21 17:34:09.632489274 +0200 +++ /work/SRC/openSUSE:Factory/.python-Pint.new.28504/python-Pint.changes 2021-01-26 14:49:48.155681927 +0100 @@ -1,0 +2,25 @@ +Wed Jan 20 16:54:45 UTC 2021 - John Vandenberg <jay...@gmail.com> + +- Use %python_alternative to fix multi Python 3 builds + +------------------------------------------------------------------- +Sat Oct 10 19:19:46 UTC 2020 - Arun Persaud <a...@gmx.de> + +- update to version 0.16.1: + * Fix unpickling, now it is using the APP_REGISTRY as expected. + (Issue #1175) + * require importlib-[resources|metadata] + +- changes from version 0.16 : + * Fixed issue where performing an operation of a Quantity with + certain units would perform an in-place unit conversion that + modified the operand in addition to the returned value (Issues + #1102 & #1144) + * Implements Logarithmic Units like dBm, dB or decade (Issue #71, + Thanks Dima Pustakhod, Clark Willison, Giorgio Signorello, Steven + Casagrande, Jonathan Wheeler) + * Drop dependency on setuptools pkg_resources to read package + resources, using std lib importlib.resources instead. (Issue + #1080) + +------------------------------------------------------------------- Old: ---- Pint-0.15.tar.gz New: ---- Pint-0.16.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-Pint.spec ++++++ --- /var/tmp/diff_new_pack.JU4gFo/_old 2021-01-26 14:49:48.767682762 +0100 +++ /var/tmp/diff_new_pack.JU4gFo/_new 2021-01-26 14:49:48.771682767 +0100 @@ -19,23 +19,25 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-Pint -Version: 0.15 +Version: 0.16.1 Release: 0 Summary: Physical quantities module License: BSD-3-Clause URL: https://github.com/hgrecco/pint Source: https://files.pythonhosted.org/packages/source/P/Pint/Pint-%{version}.tar.gz +BuildRequires: %{python_module importlib-metadata} +BuildRequires: %{python_module importlib-resources} BuildRequires: %{python_module setuptools_scm} BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros Requires: python-importlib-metadata +Requires: python-importlib-resources Requires: python-packaging Requires: python-uncertainties >= 3.0 Recommends: python-numpy BuildArch: noarch # SECTION test requirements -BuildRequires: %{python_module importlib-metadata} BuildRequires: %{python_module numpy} BuildRequires: %{python_module packaging} BuildRequires: %{python_module pytest >= 4.0} @@ -61,6 +63,7 @@ %install %python_install +%python_clone -a %{buildroot}%{_bindir}/pint-convert %python_expand %fdupes %{buildroot}%{$python_sitelib} %check @@ -68,10 +71,16 @@ # See: https://github.com/hgrecco/pint/issues/1006 %pytest -k 'not test_result_type_numpy_func' +%post +%python_install_alternative pint-convert + +%postun +%python_uninstall_alternative pint-convert + %files %{python_files} %license LICENSE %doc AUTHORS CHANGES README.rst -%{_bindir}/pint-convert +%python_alternative %{_bindir}/pint-convert %{python_sitelib}/Pint-0*-py*.egg-info %{python_sitelib}/pint/ ++++++ Pint-0.15.tar.gz -> Pint-0.16.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/.gitignore new/Pint-0.16.1/.gitignore --- old/Pint-0.15/.gitignore 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/.gitignore 2020-09-22 05:12:30.000000000 +0200 @@ -12,6 +12,7 @@ *pytest_cache* .eggs .mypy_cache +pip-wheel-metadata # WebDAV file system cache files .DAV/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/.readthedocs.yml new/Pint-0.16.1/.readthedocs.yml --- old/Pint-0.15/.readthedocs.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/Pint-0.16.1/.readthedocs.yml 2020-09-22 05:12:30.000000000 +0200 @@ -0,0 +1,12 @@ +version: 2 +build: + image: latest +sphinx: + configuration: docs/conf.py + fail_on_warning: false +python: + version: 3.7 + install: + - requirements: requirements_docs.txt + - method: pip + path: . diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/.travis.yml new/Pint-0.16.1/.travis.yml --- old/Pint-0.15/.travis.yml 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/.travis.yml 2020-09-22 05:12:30.000000000 +0200 @@ -15,7 +15,7 @@ # Refer to history of https://github.com/lebigot/uncertainties/blob/master/setup.py # for min/max Python versions supported by uncertainties - - PKGS="python=3.7 flake8 black isort" # Have linters fail first and quickly + - PKGS="python=3.7 flake8 black==19.10b0 isort" # Have linters fail first and quickly # Pinned packages to match readthedocs CI https://readthedocs.org/projects/pint/ - PKGS="python=3.7 ipython matplotlib nbsphinx numpy pandas jupyter_client ipykernel python-graphviz graphviz xarray sparse dask[complete] sphinx Pygments==2.3.1 docutils==0.14 alabaster commonmark==0.8.1 recommonmark==0.5.0" - PKGS="python=3.6" @@ -65,6 +65,7 @@ # this is superslow but suck it up until updates to pandas are made # - if [[ $PANDAS == '1' ]]; then pip install numpy cython pytest pytest-cov nbval; pip install git+https://github.com/pandas-dev/pandas.git@bdb7a1603f1e0948ca0cab011987f616e7296167; python -c 'import pandas; print(pandas.__version__)'; fi - conda list + - pip install . script: # if we're doing the pandas tests and hence have pytest available, we can diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/CHANGES new/Pint-0.16.1/CHANGES --- old/Pint-0.15/CHANGES 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/CHANGES 2020-09-22 05:12:30.000000000 +0200 @@ -1,6 +1,23 @@ Pint Changelog ============== +0.16.1 (2020-09-22) +------------------- + +- Fix unpickling, now it is using the APP_REGISTRY as expected. + (Issue #1175) + +0.16 (2020-09-13) +----------------- + +- Fixed issue where performing an operation of a Quantity with certain units would perform an in-place + unit conversion that modified the operand in addition to the returned value (Issues #1102 & #1144) +- Implements Logarithmic Units like dBm, dB or decade + (Issue #71, Thanks Dima Pustakhod, Clark Willison, Giorgio Signorello, Steven Casagrande, Jonathan Wheeler) +- Drop dependency on setuptools pkg_resources to read package resources, using std lib importlib.resources instead. + (Issue #1080) + + 0.15 (2020-08-22) ----------------- @@ -16,6 +33,8 @@ - Fixed right operand power for dimensionless Quantity to reflect numpy behavior. (Issue #1136) - Eliminated warning when setting a masked value on an underlying MaskedArray. - Add `sort` option to `formatting.formatter` to permit disabling sorting of component units in format string +- Implements Logarithmic Units like dBm, dB or decade + (Issue #71, Thanks Dima Pustakhod, Giorgio Signorello, Jonathan Wheeler) 0.14 (2020-07-01) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/MANIFEST.in new/Pint-0.16.1/MANIFEST.in --- old/Pint-0.15/MANIFEST.in 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/MANIFEST.in 2020-09-22 05:12:30.000000000 +0200 @@ -1,4 +1,4 @@ -include AUTHORS CHANGES LICENSE README.rst BADGES.rst version.txt readthedocs.yml .coveragerc +include AUTHORS CHANGES LICENSE README.rst BADGES.rst version.txt .coveragerc .readthedocs.yml recursive-include pint * recursive-include docs * recursive-include bench * diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/PKG-INFO new/Pint-0.16.1/PKG-INFO --- old/Pint-0.15/PKG-INFO 2020-08-22 21:22:44.000000000 +0200 +++ new/Pint-0.16.1/PKG-INFO 2020-09-22 05:12:31.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: Pint -Version: 0.15 +Version: 0.16.1 Summary: Physical quantities module Home-page: https://github.com/hgrecco/pint Author: Hernan E. Grecco diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/Pint.egg-info/PKG-INFO new/Pint-0.16.1/Pint.egg-info/PKG-INFO --- old/Pint-0.15/Pint.egg-info/PKG-INFO 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/Pint.egg-info/PKG-INFO 2020-09-22 05:12:31.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: Pint -Version: 0.15 +Version: 0.16.1 Summary: Physical quantities module Home-page: https://github.com/hgrecco/pint Author: Hernan E. Grecco diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/Pint.egg-info/SOURCES.txt new/Pint-0.16.1/Pint.egg-info/SOURCES.txt --- old/Pint-0.15/Pint.egg-info/SOURCES.txt 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/Pint.egg-info/SOURCES.txt 2020-09-22 05:12:31.000000000 +0200 @@ -1,5 +1,6 @@ .coveragerc .gitignore +.readthedocs.yml .travis.yml AUTHORS BADGES.rst @@ -8,7 +9,6 @@ MANIFEST.in README.rst pyproject.toml -readthedocs.yml setup.cfg setup.py Pint.egg-info/PKG-INFO @@ -31,6 +31,7 @@ docs/faq.rst docs/getting.rst docs/index.rst +docs/log_units.rst docs/make.bat docs/measurement.rst docs/nonmult.rst @@ -94,6 +95,7 @@ pint/testsuite/test_formatter.py pint/testsuite/test_infer_base_unit.py pint/testsuite/test_issues.py +pint/testsuite/test_log_units.py pint/testsuite/test_matplotlib.py pint/testsuite/test_measurement.py pint/testsuite/test_non_int.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/Pint.egg-info/requires.txt new/Pint-0.16.1/Pint.egg-info/requires.txt --- old/Pint-0.15/Pint.egg-info/requires.txt 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/Pint.egg-info/requires.txt 2020-09-22 05:12:31.000000000 +0200 @@ -1,6 +1,8 @@ -setuptools packaging +[:python_version < "3.7"] +importlib-resources + [:python_version < "3.8"] importlib-metadata diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/docs/index.rst new/Pint-0.16.1/docs/index.rst --- old/Pint-0.15/docs/index.rst 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/docs/index.rst 2020-09-22 05:12:30.000000000 +0200 @@ -122,6 +122,7 @@ defining-quantities numpy nonmult + log_units wrapping plotting serialization diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/docs/log_units.rst new/Pint-0.16.1/docs/log_units.rst --- old/Pint-0.15/docs/log_units.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/Pint-0.16.1/docs/log_units.rst 2020-09-22 05:12:30.000000000 +0200 @@ -0,0 +1,127 @@ +.. _log_units: + + +Logarithmic Units +================= + +.. warning:: + + Support for logarithmic units in Pint is currently in Beta. Please take + careful note of the information below, particularly around `compound log units`_ + to avoid calculation errors. Bug reports and pull requests are always + welcome, please see :doc:`contributing` for more information on + how you can help improve this feature (and Pint in general). + +Pint supports some logarithmic units, including `dB`, `dBm`, `octave`, and `decade` +as well as some conversions between them and their base units where applicable. +These units behave much like those described in :ref:`nonmult`, so many of +the recommendations there apply here as well. + +Setting up the ``UnitRegistry()`` +--------------------------------- + +Many of the examples below will fail without supplying the +``autoconvert_offset_to_baseunit=True`` flag. To use logarithmic units, +intialize your ``UnitRegistry()`` like so: + +.. doctest:: + + >>> from pint import UnitRegistry + >>> ureg = UnitRegistry(autoconvert_offset_to_baseunit=True) + >>> Q_ = ureg.Quantity + +If you can't pass that flag you will need to define all logarithmic units +:ref:`using the Quantity() constructor<Using the constructor>`, and you will +be restricted in the kinds of operations you can do without explicitly calling +`.to_base_units()` first. + +Defining log quantities +----------------------- + +After you've set up your ``UnitRegistry()`` with the ``autoconvert...`` flag, +you can define simple logarithmic quantities like most others: + +.. doctest:: + + >>> 20.0 * ureg.dBm + <Quantity(20.0, 'decibelmilliwatt')> + >>> ureg('20.0 dBm') + <Quantity(20.0, 'decibelmilliwatt')> + >>> ureg('20 dB') + <Quantity(20, 'decibel')> + + +Converting to and from base units +--------------------------------- + +Get a sense of how logarithmic units are handled by using the `.to()` and +`.to_base_units()` methods: + +.. doctest:: + + >>> ureg('20 dBm').to('mW') + <Quantity(100.0, 'milliwatt')> + >>> ureg('20 dB').to_base_units() + <Quantity(100.0, 'dimensionless')> + +.. note:: + + Notice in the above example how the `dB` unit is defined for + power quantities (10*log(p/p0)) not field (amplitude) quantities + (20*log(v/v0)). Take care that you're only using it to multiply power + levels, and not e.g. Voltages. + +Convert back from a base unit to a logarithmic unit using the `.to()` method: + +.. doctest:: + + >>> (100.0 * ureg('mW')).to('dBm') + <Quantity(20.0, 'decibelmilliwatt')> + >>> shift = Q_(4, '') + >>> shift + <Quantity(4, 'dimensionless')> + >>> shift.to('octave') + <Quantity(2.0, 'octave')> + +Compound log units +------------------ + +.. warning:: + + Support for compound logarithmic units is not comprehensive. The following + examples work, but many others will not. Consider converting the logarithmic + portion to base units before adding more units. + +Pint sometimes works with mixtures of logarithmic and other units. Below is an +example of computing RMS noise from a noise density and a bandwidth: + +.. doctest:: + + >>> noise_density = -161.0 * ureg.dBm / ureg.Hz + >>> bandwidth = 10.0 * ureg.kHz + >>> noise_power = noise_density * bandwidth + >>> noise_power.to('dBm') + <Quantity(-121.0, 'decibelmilliwatt')> + >>> noise_power.to('mW') + <Quantity(7.94328235e-13, 'milliwatt')> + +There are still issues with parsing compound units, so for now the following +will not work: + +.. doctest:: + + >>> -161.0 * ureg('dBm/Hz') == (-161.0 * ureg.dBm / ureg.Hz) + False + +But this will: + +.. doctest:: + + >>> ureg('-161.0 dBm/Hz') == (-161.0 * ureg.dBm / ureg.Hz) + True + >>> Q_(-161.0, 'dBm') / ureg.Hz == (-161.0 * ureg.dBm / ureg.Hz) + True + +To begin using this feature while avoiding problems, define logarithmic units +as single-unit quantities and convert them to their base units as quickly as +possible. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/docs/tutorial.rst new/Pint-0.16.1/docs/tutorial.rst --- old/Pint-0.15/docs/tutorial.rst 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/docs/tutorial.rst 2020-09-22 05:12:30.000000000 +0200 @@ -431,7 +431,7 @@ 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`` -.. code-block:: +.. code-block:: python from pint import UnitRegistry ureg = UnitRegistry() @@ -440,7 +440,7 @@ Then in ``yourmodule.py`` the code would be -.. code-block:: +.. code-block:: python from . import ureg, Q_ @@ -450,7 +450,7 @@ If you are pickling and unplicking Quantities within your project, you should also define the registry as the application registry -.. code-block:: +.. code-block:: python from pint import UnitRegistry, set_application_registry ureg = UnitRegistry() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/__init__.py new/Pint-0.16.1/pint/__init__.py --- old/Pint-0.15/pint/__init__.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/__init__.py 2020-09-22 05:12:30.000000000 +0200 @@ -14,9 +14,10 @@ import sys from .context import Context -from .errors import ( +from .errors import ( # noqa: F401 DefinitionSyntaxError, DimensionalityError, + LogarithmicUnitCalculusError, OffsetUnitCalculusError, RedefinitionError, UndefinedUnitError, @@ -88,6 +89,24 @@ return cls(*args) +def _unpickle_quantity(cls, *args): + """Rebuild quantity upon unpickling using the application registry. + """ + return _unpickle(_APP_REGISTRY.Quantity, *args) + + +def _unpickle_unit(cls, *args): + """Rebuild unit upon unpickling using the application registry. + """ + return _unpickle(_APP_REGISTRY.Unit, *args) + + +def _unpickle_measurement(cls, *args): + """Rebuild measurement upon unpickling using the application registry. + """ + return _unpickle(_APP_REGISTRY.Measurement, *args) + + def set_application_registry(registry): """Set the application registry, which is used for unpickling operations and when invoking pint.Quantity or pint.Unit directly. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/compat.py new/Pint-0.16.1/pint/compat.py --- old/Pint-0.15/pint/compat.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/compat.py 2020-09-22 05:12:30.000000000 +0200 @@ -127,6 +127,14 @@ except ImportError: HAS_BABEL = False +# Defines Logarithm and Exponential for Logarithmic Converter +if HAS_NUMPY: + from numpy import exp # noqa: F401 + from numpy import log # noqa: F401 +else: + from math import exp # noqa: F401 + from math import log # noqa: F401 + if not HAS_BABEL: babel_parse = babel_units = missing_dependency("Babel") # noqa: F811 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/constants_en.txt new/Pint-0.16.1/pint/constants_en.txt --- old/Pint-0.15/pint/constants_en.txt 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/constants_en.txt 2020-09-22 05:12:30.000000000 +0200 @@ -13,6 +13,7 @@ ln10 = 2.3025850929940456840179914546843642076011014886288 # natural logarithm of 10 wien_x = 4.9651142317442763036987591313228939440555849867973 # solution to (x-5)*exp(x)+5 = 0 => x = W(5/exp(5))+5 wien_u = 2.8214393721220788934031913302944851953458817440731 # solution to (u-3)*exp(u)+3 = 0 => u = W(3/exp(3))+3 +eulers_number = 2.71828182845904523536028747135266249775724709369995 #### DEFINED EXACT CONSTANTS #### diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/converters.py new/Pint-0.16.1/pint/converters.py --- old/Pint-0.15/pint/converters.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/converters.py 2020-09-22 05:12:30.000000000 +0200 @@ -9,10 +9,19 @@ """ +from .compat import HAS_NUMPY, exp, log # noqa: F401 + + class Converter: """Base class for value converters.""" - is_multiplicative = True + @property + def is_multiplicative(self): + return True + + @property + def is_logarithmic(self): + return False def to_reference(self, value, inplace=False): return value @@ -24,8 +33,6 @@ class ScaleConverter(Converter): """A linear transformation.""" - is_multiplicative = True - def __init__(self, scale): self.scale = scale @@ -74,3 +81,81 @@ value = (value - self.offset) / self.scale return value + + +class LogarithmicConverter(Converter): + """ Converts between linear units and logarithmic units, such as dB, octave, neper or pH. + Q_log = logfactor * log( Q_lin / scale ) / log(log_base) + + Parameters + ---------- + scale : float + unit of reference at denominator for logarithmic unit conversion + logbase : float + base of logarithm used in the logarithmic unit conversion + logfactor : float + factor multupled to logarithm for unit conversion + inplace : bool + controls if computation is done in place + """ + + def __init__(self, scale, logbase, logfactor): + """ + Parameters + ---------- + scale : float + unit of reference at denominator inside logarithm for unit conversion + logbase: float + base of logarithm used in unit conversion + logfactor: float + factor multiplied to logarithm for unit conversion + """ + + self.scale = scale + self.logbase = logbase + self.logfactor = logfactor + + @property + def is_multiplicative(self): + return False + + @property + def is_logarithmic(self): + return True + + def from_reference(self, value, inplace=False): + """Converts value from the reference unit to the logarithmic unit + + dBm <------ mW + y dBm = 10 log10( x / 1mW ) + """ + if inplace: + value /= self.scale + if HAS_NUMPY: + log(value, value) + else: + value = log(value) + value *= self.logfactor / log(self.logbase) + else: + value = self.logfactor * log(value / self.scale) / log(self.logbase) + + return value + + def to_reference(self, value, inplace=False): + """Converts value to the reference unit from the logarithmic unit + + dBm ------> mW + y dBm = 10 log10( x / 1mW ) + """ + if inplace: + value /= self.logfactor + value *= log(self.logbase) + if HAS_NUMPY: + exp(value, value) + else: + value = exp(value) + value *= self.scale + else: + value = self.scale * exp(log(self.logbase) * (value / self.logfactor)) + + return value diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/default_en.txt new/Pint-0.16.1/pint/default_en.txt --- old/Pint-0.15/pint/default_en.txt 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/default_en.txt 2020-09-22 05:12:30.000000000 +0200 @@ -108,7 +108,6 @@ mole = [substance] = mol kelvin = [temperature]; offset: 0 = K = degK = ??K = degree_Kelvin = degreeK # older names supported for compatibility radian = [] = rad -neper = [] = Np bit = [] count = [] @@ -136,13 +135,13 @@ steradian = radian ** 2 = sr square_degree = (?? / 180) ** 2 * sr = sq_deg = sqdeg -# Logarithmic ratio -bel = 0.5 * ln10 * neper - # Information -byte = 8 * bit = B = octet baud = bit / second = Bd = bps +byte = 8 * bit = B = octet +# byte = 8 * bit = _ = octet +## NOTE: B (byte) symbol can conflict with Bell + # Length angstrom = 1e-10 * meter = ?? = ??ngstr??m = ??? micron = micrometer = ?? @@ -174,7 +173,10 @@ fortnight = 2 * week year = 365.25 * day = a = yr = julian_year month = year / 12 -decade = 10 * year + +# decade = 10 * year +## NOTE: decade [time] can conflict with decade [dimensionless] + century = 100 * year = _ = centuries millennium = 1e3 * year = _ = millennia eon = 1e9 * year @@ -308,6 +310,7 @@ inch_H2O_60F = inch * water_60F * g_0 foot_H2O = foot * water * g_0 = ftH2O = feet_H2O centimeter_H2O = centimeter * water * g_0 = cmH2O = cm_H2O +sound_pressure_level = 20e-6 * pascal = SPL # Torque [torque] = [force] * [length] @@ -470,6 +473,26 @@ bohr_magneton = e * hbar / (2 * m_e) = ??_B = mu_B nuclear_magneton = e * hbar / (2 * m_p) = ??_N = mu_N +# Logaritmic Unit Definition +# Unit = scale; logbase; logfactor +# x_dB = [logfactor] * log( x_lin / [scale] ) / log( [logbase] ) + +# Logaritmic Units of dimensionless quantity: [ https://en.wikipedia.org/wiki/Level_(logarithmic_quantity) ] + +decibelmilliwatt = 1e-3 watt; logbase: 10; logfactor: 10 = dBm +decibelmicrowatt = 1e-6 watt; logbase: 10; logfactor: 10 = dBu + +decibel = 1 ; logbase: 10; logfactor: 10 = dB +# bell = 1 ; logbase: 10; logfactor: = B +## NOTE: B (Bell) symbol conflicts with byte + +decade = 1 ; logbase: 10; logfactor: 1 +## NOTE: decade [time] can conflict with decade [dimensionless] + +octave = 1 ; logbase: 2; logfactor: 1 = oct + +neper = 1 ; logbase: 2.71828182845904523536028747135266249775724709369995; logfactor: 0.5 = Np +# neper = 1 ; logbase: eulers_number; logfactor: 0.5 = Np #### UNIT GROUPS #### # Mostly for length, area, volume, mass, force diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/definitions.py new/Pint-0.16.1/pint/definitions.py --- old/Pint-0.15/pint/definitions.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/definitions.py 2020-09-22 05:12:30.000000000 +0200 @@ -10,7 +10,7 @@ from collections import namedtuple -from .converters import OffsetConverter, ScaleConverter +from .converters import LogarithmicConverter, OffsetConverter, ScaleConverter from .errors import DefinitionSyntaxError from .util import ParserHelper, UnitsContainer, _is_dim @@ -120,6 +120,10 @@ def is_multiplicative(self): return self._converter.is_multiplicative + @property + def is_logarithmic(self): + return self._converter.is_logarithmic + @classmethod def from_string(cls, definition, non_int_type=float): """Parse a definition. @@ -235,7 +239,7 @@ definition = PreprocessedDefinition.from_string(definition) if ";" in definition.value: - [converter, modifiers] = definition.value.split(";", 2) + [converter, modifiers] = definition.value.split(";", 1) try: modifiers = dict( @@ -265,11 +269,23 @@ ) reference = UnitsContainer(converter) - if modifiers.get("offset", 0) != 0: - converter = OffsetConverter(converter.scale, modifiers["offset"]) - else: + if not modifiers: converter = ScaleConverter(converter.scale) + elif "offset" in modifiers: + if modifiers.get("offset", 0.0) != 0.0: + converter = OffsetConverter(converter.scale, modifiers["offset"]) + else: + converter = ScaleConverter(converter.scale) + + elif "logbase" in modifiers and "logfactor" in modifiers: + converter = LogarithmicConverter( + converter.scale, modifiers["logbase"], modifiers["logfactor"] + ) + + else: + raise DefinitionSyntaxError("Unable to assing a converter to the unit") + return cls( definition.name, definition.symbol, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/errors.py new/Pint-0.16.1/pint/errors.py --- old/Pint-0.15/pint/errors.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/errors.py 2020-09-22 05:12:30.000000000 +0200 @@ -8,6 +8,9 @@ :license: BSD, see LICENSE for more details. """ +OFFSET_ERROR_DOCS_HTML = "https://pint.readthedocs.io/en/latest/nonmult.html" +LOG_ERROR_DOCS_HTML = "https://pint.readthedocs.io/en/latest/nonmult.html" + def _file_prefix(filename=None, lineno=None): if filename and lineno is not None: @@ -111,7 +114,22 @@ return ( "Ambiguous operation with offset unit (%s)." % ", ".join(str(u) for u in self.args) - + " See https://pint.readthedocs.io/en/latest/nonmult.html for guidance." + + " See " + + OFFSET_ERROR_DOCS_HTML + + " for guidance." + ) + + +class LogarithmicUnitCalculusError(PintTypeError): + """Raised on inappropriate operations with logarithmic units.""" + + def __str__(self): + return ( + "Ambiguous operation with logarithmic unit (%s)." + % ", ".join(str(u) for u in self.args) + + " See " + + LOG_ERROR_DOCS_HTML + + " for guidance." ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/measurement.py new/Pint-0.16.1/pint/measurement.py --- old/Pint-0.15/pint/measurement.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/measurement.py 2020-09-22 05:12:30.000000000 +0200 @@ -70,9 +70,9 @@ def __reduce__(self): # See notes in Quantity.__reduce__ - from . import _unpickle + from . import _unpickle_measurement - return _unpickle, (Measurement, self.magnitude, self._units) + return _unpickle_measurement, (Measurement, self.magnitude, self._units) def __repr__(self): return "<Measurement({}, {}, {})>".format( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/quantity.py new/Pint-0.16.1/pint/quantity.py --- old/Pint-0.15/pint/quantity.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/quantity.py 2020-09-22 05:12:30.000000000 +0200 @@ -192,11 +192,11 @@ """Allow pickling quantities. Since UnitRegistries are not pickled, upon unpickling the new object is always attached to the application registry. """ - from . import _unpickle + from . import _unpickle_quantity # Note: type(self) would be a mistake as subclasses built by # build_quantity_class can't be pickled - return _unpickle, (Quantity, self.magnitude, self._units) + return _unpickle_quantity, (Quantity, self.magnitude, self._units) def __new__(cls, value, units=None): if is_upcast_type(type(value)): @@ -1007,7 +1007,9 @@ 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) + magnitude = op( + self._convert_magnitude_not_inplace(other._units), other._magnitude + ) units = other._units else: units = self._units @@ -1055,7 +1057,7 @@ # 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) + magnitude = op(self._convert_magnitude_not_inplace(tu), other._magnitude) units = other._units else: raise OffsetUnitCalculusError(self._units, other._units) @@ -1542,7 +1544,11 @@ raise OffsetUnitCalculusError(self._units) if self.dimensionless: - return eq(self._convert_magnitude(self.UnitsContainer()), other, False) + return eq( + self._convert_magnitude_not_inplace(self.UnitsContainer()), + other, + False, + ) return bool_result(False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/registry.py new/Pint-0.16.1/pint/registry.py --- old/Pint-0.15/pint/registry.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/registry.py 2020-09-22 05:12:30.000000000 +0200 @@ -40,18 +40,16 @@ import os import re from collections import ChainMap, defaultdict -from contextlib import closing, contextmanager +from contextlib import contextmanager from decimal import Decimal from fractions import Fraction from io import StringIO from tokenize import NAME, NUMBER -import pkg_resources - from . import registry_helpers, systems from .compat import babel_parse, tokenizer from .context import Context, ContextChain -from .converters import ScaleConverter +from .converters import LogarithmicConverter, ScaleConverter from .definitions import ( AliasDefinition, Definition, @@ -81,6 +79,13 @@ to_units_container, ) +try: + import importlib.resources as importlib_resources +except ImportError: + # Backport for Python < 3.7 + import importlib_resources + + _BLOCK_RE = re.compile(r" |\(") @@ -531,8 +536,7 @@ if isinstance(file, str): try: if is_resource: - with closing(pkg_resources.resource_stream(__name__, file)) as fp: - rbytes = fp.read() + rbytes = importlib_resources.read_binary(__package__, file) return self.load_definitions( StringIO(rbytes.decode("utf-8")), is_resource ) @@ -1352,7 +1356,7 @@ raise UndefinedUnitError(u) def _validate_and_extract(self, units): - + # u is for unit, e is for exponent nonmult_units = [ (u, e) for u, e in units.items() if not self._is_multiplicative(u) ] @@ -1379,6 +1383,21 @@ return None + def _add_ref_of_log_unit(self, offset_unit, all_units): + + slct_unit = self._units[offset_unit] + if isinstance(slct_unit.converter, LogarithmicConverter): + # Extract reference unit + slct_ref = slct_unit.reference + # If reference unit is not dimensionless + if slct_ref != UnitsContainer(): + # Extract reference unit + (u, e) = [(u, e) for u, e in slct_ref.items()].pop() + # Add it back to the unit list + return all_units.add(u, e) + # Otherwise, return the units unmodified + return all_units + def _convert(self, value, src, dst, inplace=False): """Convert value from some source to destination units. @@ -1434,10 +1453,14 @@ if src_offset_unit: value = self._units[src_offset_unit].converter.to_reference(value, inplace) src = src.remove([src_offset_unit]) + # Add reference unit for multiplicative section + src = self._add_ref_of_log_unit(src_offset_unit, src) # clean dst units from offset units if dst_offset_unit: dst = dst.remove([dst_offset_unit]) + # Add reference unit for multiplicative section + dst = self._add_ref_of_log_unit(dst_offset_unit, dst) # Convert non multiplicative units to the dst. value = super()._convert(value, src, dst, inplace, False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/testsuite/test_converters.py new/Pint-0.16.1/pint/testsuite/test_converters.py --- old/Pint-0.15/pint/testsuite/test_converters.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/testsuite/test_converters.py 2020-09-22 05:12:30.000000000 +0200 @@ -1,7 +1,12 @@ import itertools from pint.compat import np -from pint.converters import Converter, OffsetConverter, ScaleConverter +from pint.converters import ( + Converter, + LogarithmicConverter, + OffsetConverter, + ScaleConverter, +) from pint.testsuite import BaseTestCase, helpers @@ -9,19 +14,38 @@ def test_converter(self): c = Converter() self.assertTrue(c.is_multiplicative) + self.assertFalse(c.is_logarithmic) self.assertTrue(c.to_reference(8)) self.assertTrue(c.from_reference(8)) def test_multiplicative_converter(self): c = ScaleConverter(20.0) + self.assertTrue(c.is_multiplicative) + self.assertFalse(c.is_logarithmic) 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.0, 2) + self.assertFalse(c.is_multiplicative) + self.assertFalse(c.is_logarithmic) self.assertEqual(c.from_reference(c.to_reference(100)), 100) self.assertEqual(c.to_reference(c.from_reference(100)), 100) + def test_log_converter(self): + c = LogarithmicConverter(scale=1, logbase=10, logfactor=1) + self.assertFalse(c.is_multiplicative) + self.assertTrue(c.is_logarithmic) + self.assertAlmostEqual(c.to_reference(0), 1) + self.assertAlmostEqual(c.to_reference(1), 10) + self.assertAlmostEqual(c.to_reference(2), 100) + self.assertAlmostEqual(c.from_reference(1), 0) + self.assertAlmostEqual(c.from_reference(10), 1) + self.assertAlmostEqual(c.from_reference(100), 2) + arb_value = 20.0 + self.assertAlmostEqual(c.from_reference(c.to_reference(arb_value)), arb_value) + self.assertAlmostEqual(c.to_reference(c.from_reference(arb_value)), arb_value) + @helpers.requires_numpy() def test_converter_inplace(self): for c in (ScaleConverter(20.0), OffsetConverter(20.0, 2)): @@ -35,3 +59,24 @@ r = fun(a, inplace) np.testing.assert_allclose(r, ac) comp(a, r) + + @helpers.requires_numpy() + def test_log_converter_inplace(self): + arb_value = 3.14 + c = LogarithmicConverter(scale=1, logbase=10, logfactor=1) + + from_to = lambda value, inplace: c.from_reference( + c.to_reference(value, inplace), inplace + ) + + to_from = lambda value, inplace: c.to_reference( + c.from_reference(value, inplace), inplace + ) + + for fun, (inplace, comp) in itertools.product( + (from_to, to_from), ((True, self.assertIs), (False, self.assertIsNot)) + ): + arb_array = arb_value * np.ones((1, 10)) + result = fun(arb_array, inplace) + np.testing.assert_allclose(result, arb_array) + comp(arb_array, result) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/testsuite/test_definitions.py new/Pint-0.16.1/pint/testsuite/test_definitions.py --- old/Pint-0.15/pint/testsuite/test_definitions.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/testsuite/test_definitions.py 2020-09-22 05:12:30.000000000 +0200 @@ -1,4 +1,4 @@ -from pint.converters import OffsetConverter, ScaleConverter +from pint.converters import LogarithmicConverter, OffsetConverter, ScaleConverter from pint.definitions import ( AliasDefinition, Definition, @@ -94,6 +94,67 @@ "degF = 9 / 5 * kelvin; offset: 255.372222 bla", ) + def test_log_unit_definition(self): + + x = Definition.from_string( + "decibelmilliwatt = 1e-3 watt; logbase: 10; logfactor: 10 = dBm" + ) + self.assertIsInstance(x, UnitDefinition) + self.assertFalse(x.is_base) + self.assertIsInstance(x.converter, LogarithmicConverter) + self.assertEqual(x.converter.scale, 1e-3) + self.assertEqual(x.converter.logbase, 10) + self.assertEqual(x.converter.logfactor, 10) + self.assertEqual(x.reference, UnitsContainer(watt=1)) + + x = Definition.from_string("decibel = 1 ; logbase: 10; logfactor: 10 = dB") + self.assertIsInstance(x, UnitDefinition) + self.assertFalse(x.is_base) + self.assertIsInstance(x.converter, LogarithmicConverter) + self.assertEqual(x.converter.scale, 1) + self.assertEqual(x.converter.logbase, 10) + self.assertEqual(x.converter.logfactor, 10) + self.assertEqual(x.reference, UnitsContainer()) + + x = Definition.from_string("bell = 1 ; logbase: 10; logfactor: 1 = B") + self.assertIsInstance(x, UnitDefinition) + self.assertFalse(x.is_base) + self.assertIsInstance(x.converter, LogarithmicConverter) + self.assertEqual(x.converter.scale, 1) + self.assertEqual(x.converter.logbase, 10) + self.assertEqual(x.converter.logfactor, 1) + self.assertEqual(x.reference, UnitsContainer()) + + x = Definition.from_string("decade = 1 ; logbase: 10; logfactor: 1") + self.assertIsInstance(x, UnitDefinition) + self.assertFalse(x.is_base) + self.assertIsInstance(x.converter, LogarithmicConverter) + self.assertEqual(x.converter.scale, 1) + self.assertEqual(x.converter.logbase, 10) + self.assertEqual(x.converter.logfactor, 1) + self.assertEqual(x.reference, UnitsContainer()) + + eulersnumber = 2.71828182845904523536028747135266249775724709369995 + x = Definition.from_string( + "neper = 1 ; logbase: %1.50f; logfactor: 0.5 = Np" % eulersnumber + ) + self.assertIsInstance(x, UnitDefinition) + self.assertFalse(x.is_base) + self.assertIsInstance(x.converter, LogarithmicConverter) + self.assertEqual(x.converter.scale, 1) + self.assertEqual(x.converter.logbase, eulersnumber) + self.assertEqual(x.converter.logfactor, 0.5) + self.assertEqual(x.reference, UnitsContainer()) + + x = Definition.from_string("octave = 1 ; logbase: 2; logfactor: 1 = oct") + self.assertIsInstance(x, UnitDefinition) + self.assertFalse(x.is_base) + self.assertIsInstance(x.converter, LogarithmicConverter) + self.assertEqual(x.converter.scale, 1) + self.assertEqual(x.converter.logbase, 2) + self.assertEqual(x.converter.logfactor, 1) + self.assertEqual(x.reference, UnitsContainer()) + def test_dimension_definition(self): x = DimensionDefinition("[time]", "", (), None, is_base=True) self.assertTrue(x.is_base) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/testsuite/test_errors.py new/Pint-0.16.1/pint/testsuite/test_errors.py --- old/Pint-0.15/pint/testsuite/test_errors.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/testsuite/test_errors.py 2020-09-22 05:12:30.000000000 +0200 @@ -3,12 +3,14 @@ from pint import ( DefinitionSyntaxError, DimensionalityError, + LogarithmicUnitCalculusError, OffsetUnitCalculusError, Quantity, RedefinitionError, UndefinedUnitError, UnitRegistry, ) +from pint.errors import LOG_ERROR_DOCS_HTML, OFFSET_ERROR_DOCS_HTML from pint.testsuite import BaseTestCase @@ -79,13 +81,34 @@ self.assertEqual( str(ex), "Ambiguous operation with offset unit (kilogram). See " - "https://pint.readthedocs.io/en/latest/nonmult.html for guidance.", + + OFFSET_ERROR_DOCS_HTML + + " for guidance.", ) ex = OffsetUnitCalculusError(Quantity("1 kg")._units, Quantity("1 s")._units) self.assertEqual( str(ex), "Ambiguous operation with offset unit (kilogram, second). See " - "https://pint.readthedocs.io/en/latest/nonmult.html for guidance.", + + OFFSET_ERROR_DOCS_HTML + + " for guidance.", + ) + + def test_logarithmic_unit_calculus_error(self): + Quantity = UnitRegistry(autoconvert_offset_to_baseunit=True).Quantity + ex = LogarithmicUnitCalculusError(Quantity("1 dB")._units) + self.assertEqual( + str(ex), + "Ambiguous operation with logarithmic unit (decibel). See " + + LOG_ERROR_DOCS_HTML + + " for guidance.", + ) + ex = LogarithmicUnitCalculusError( + Quantity("1 dB")._units, Quantity("1 octave")._units + ) + self.assertEqual( + str(ex), + "Ambiguous operation with logarithmic unit (decibel, octave). See " + + LOG_ERROR_DOCS_HTML + + " for guidance.", ) def test_pickle_definition_syntax_error(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/testsuite/test_issues.py new/Pint-0.16.1/pint/testsuite/test_issues.py --- old/Pint-0.15/pint/testsuite/test_issues.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/testsuite/test_issues.py 2020-09-22 05:12:30.000000000 +0200 @@ -5,7 +5,7 @@ import pytest -from pint import Context, DimensionalityError, UnitRegistry +from pint import Context, DimensionalityError, UnitRegistry, get_application_registry from pint.compat import np from pint.testsuite import QuantityTestCase, helpers from pint.unit import UnitsContainer @@ -520,7 +520,7 @@ lunar_module_height = Q_(10, "m") t1 = calculate_time_to_fall(lunar_module_height) - print(t1) + # print(t1) self.assertAlmostEqual(t1, Q_(1.4285714285714286, "s")) moon_gravity = Q_(1.625, "m/s^2") @@ -580,7 +580,7 @@ @ureg.check("[length]", "[length]/[time]^2") def pendulum_period(length, G=Q_(1, "standard_gravity")): - print(length) + # print(length) return (2 * math.pi * (length / G) ** 0.5).to("s") length = Q_(1, ureg.m) @@ -746,12 +746,43 @@ ureg.enable_contexts("c3") @helpers.requires_numpy() + def test_issue1144_1102(self): + # Performing operations shouldn't modify the original objects + # Issue 1144 + ddc = "delta_degree_Celsius" + q1 = ureg.Quantity([-287.78, -32.24, -1.94], ddc) + q2 = ureg.Quantity(70.0, "degree_Fahrenheit") + q1 - q2 + assert all(q1 == ureg.Quantity([-287.78, -32.24, -1.94], ddc)) + assert q2 == ureg.Quantity(70.0, "degree_Fahrenheit") + q2 - q1 + assert all(q1 == ureg.Quantity([-287.78, -32.24, -1.94], ddc)) + assert q2 == ureg.Quantity(70.0, "degree_Fahrenheit") + # Issue 1102 + val = [30.0, 45.0, 60.0] * ureg.degree + val == 1 + 1 == val + assert all(val == ureg.Quantity([30.0, 45.0, 60.0], "degree")) + # Test for another bug identified by searching on "_convert_magnitude" + q2 = ureg.Quantity(3, "degree_Kelvin") + q1 - q2 + assert all(q1 == ureg.Quantity([-287.78, -32.24, -1.94], ddc)) + + @helpers.requires_numpy() def test_issue_1136(self): assert (2 ** ureg.Quantity([2, 3], "") == 2 ** np.array([2, 3])).all() with pytest.raises(DimensionalityError): 2 ** ureg.Quantity([2, 3], "m") + def test_issue1175(self): + import pickle + + foo1 = get_application_registry().Quantity(1, "s") + foo2 = pickle.loads(pickle.dumps(foo1)) + self.assertIsInstance(foo1, foo2.__class__) + self.assertIsInstance(foo2, foo1.__class__) + if np is not None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/testsuite/test_log_units.py new/Pint-0.16.1/pint/testsuite/test_log_units.py --- old/Pint-0.15/pint/testsuite/test_log_units.py 1970-01-01 01:00:00.000000000 +0100 +++ new/Pint-0.16.1/pint/testsuite/test_log_units.py 2020-09-22 05:12:30.000000000 +0200 @@ -0,0 +1,276 @@ +import math + +import pytest + +from pint import OffsetUnitCalculusError, UnitRegistry +from pint.testsuite import QuantityTestCase +from pint.unit import Unit, UnitsContainer + + +@pytest.fixture(scope="module") +def auto_ureg(): + return UnitRegistry(autoconvert_offset_to_baseunit=True) + + +@pytest.fixture(scope="module") +def ureg(): + return UnitRegistry() + + +class TestLogarithmicQuantity(QuantityTestCase): + + FORCE_NDARRAY = False + + def test_log_quantity_creation(self): + + # Following Quantity Creation Pattern + for args in ( + (4.2, "dBm"), + (4.2, UnitsContainer(decibelmilliwatt=1)), + (4.2, self.ureg.dBm), + ): + x = self.Q_(*args) + self.assertEqual(x.magnitude, 4.2) + self.assertEqual(x.units, UnitsContainer(decibelmilliwatt=1)) + + x = self.Q_(self.Q_(4.2, "dBm")) + self.assertEqual(x.magnitude, 4.2) + self.assertEqual(x.units, UnitsContainer(decibelmilliwatt=1)) + + x = self.Q_(4.2, UnitsContainer(decibelmilliwatt=1)) + y = self.Q_(x) + self.assertEqual(x.magnitude, y.magnitude) + self.assertEqual(x.units, y.units) + self.assertIsNot(x, y) + + # Using multiplications for dB units requires autoconversion to baseunits + new_reg = UnitRegistry(autoconvert_offset_to_baseunit=True) + x = new_reg.Quantity("4.2 * dBm") + self.assertEqual(x.magnitude, 4.2) + self.assertEqual(x.units, UnitsContainer(decibelmilliwatt=1)) + + with self.capture_log() as buffer: + self.assertEqual(4.2 * new_reg.dBm, new_reg.Quantity(4.2, 2 * new_reg.dBm)) + self.assertEqual(len(buffer), 1) + + def test_log_convert(self): + # # 1 dB = 1/10 * bel + # self.assertQuantityAlmostEqual(self.Q_(1.0, "dB").to("dimensionless"), self.Q_(1, "bell") / 10) + # # Uncomment Bell unit in default_en.txt + + # ## Test dB to dB units octave - decade + # 1 decade = log2(10) octave + self.assertQuantityAlmostEqual( + self.Q_(1.0, "decade"), self.Q_(math.log(10, 2), "octave") + ) + # ## Test dB to dB units dBm - dBu + # 0 dBm = 1mW = 1e3 uW = 30 dBu + self.assertAlmostEqual(self.Q_(0.0, "dBm"), self.Q_(29.999999999999996, "dBu")) + + def test_mix_regular_log_units(self): + # Test regular-logarithmic mixed definition, such as dB/km or dB/cm + + # Multiplications and divisions with a mix of Logarithmic Units and regular Units is normally not possible. + # The reason is that dB are considered by pint like offset units. + # Multiplications and divisions that involve offset units are badly defined, so pint raises an error + with self.assertRaises(OffsetUnitCalculusError): + (-10.0 * self.ureg.dB) / (1 * self.ureg.cm) + + # However, if the flag autoconvert_offset_to_baseunit=True is given to UnitRegistry, then pint converts the unit to base. + # With this flag on multiplications and divisions are now possible: + new_reg = UnitRegistry(autoconvert_offset_to_baseunit=True) + self.assertQuantityAlmostEqual(-10 * new_reg.dB / new_reg.cm, 0.1 / new_reg.cm) + + +log_unit_names = [ + "decibelmilliwatt", + "dBm", + "decibelmicrowatt", + "dBu", + "decibel", + "dB", + "decade", + "octave", + "oct", +] + + +@pytest.mark.parametrize("unit_name", log_unit_names) +def test_unit_by_attribute(ureg, unit_name): + """Can the logarithmic units be accessed by attribute lookups?""" + unit = getattr(ureg, unit_name) + assert isinstance(unit, Unit) + + +@pytest.mark.parametrize("unit_name", log_unit_names) +def test_unit_parsing(ureg, unit_name): + """Can the logarithmic units be understood by the parser?""" + unit = ureg.parse_units(unit_name) + assert isinstance(unit, Unit) + + +@pytest.mark.parametrize("mag", [1.0, 4.2]) +@pytest.mark.parametrize("unit_name", log_unit_names) +def test_quantity_by_constructor(ureg, unit_name, mag): + """Can Quantity() objects be constructed using logarithmic units?""" + q = ureg.Quantity(mag, unit_name) + assert q.magnitude == pytest.approx(mag) + assert q.units == getattr(ureg, unit_name) + + +@pytest.mark.parametrize("mag", [1.0, 4.2]) +@pytest.mark.parametrize("unit_name", log_unit_names) +def test_quantity_by_multiplication(auto_ureg, unit_name, mag): + """Test that logarithmic units can be defined with multiplication + + Requires setting `autoconvert_offset_to_baseunit` to True + """ + unit = getattr(auto_ureg, unit_name) + q = mag * unit + assert q.magnitude == pytest.approx(mag) + assert q.units == unit + + +@pytest.mark.parametrize( + "unit1,unit2", + [ + ("decibelmilliwatt", "dBm"), + ("decibelmicrowatt", "dBu"), + ("decibel", "dB"), + ("octave", "oct"), + ], +) +def test_unit_equivalence(ureg, unit1, unit2): + """Are certain pairs of units equivalent?""" + assert getattr(ureg, unit1) == getattr(ureg, unit2) + + +@pytest.mark.parametrize( + "db_value,scalar", + [ + (0.0, 1.0), # 0 dB == 1x + (-10.0, 0.1), # -10 dB == 0.1x + (10.0, 10.0), + (30.0, 1e3), + (60.0, 1e6), + ], +) +def test_db_conversion(ureg, db_value, scalar): + """Test that a dB value can be converted to a scalar and back. + """ + Q_ = ureg.Quantity + assert Q_(db_value, "dB").to("dimensionless").magnitude == pytest.approx(scalar) + assert Q_(scalar, "dimensionless").to("dB").magnitude == pytest.approx(db_value) + + +@pytest.mark.parametrize( + "octave,scalar", + [ + (2.0, 4.0), # 2 octave == 4x + (1.0, 2.0), # 1 octave == 2x + (0.0, 1.0), + (-1.0, 0.5), + (-2.0, 0.25), + ], +) +def test_octave_conversion(ureg, octave, scalar): + """Test that an octave can be converted to a scalar and back. + """ + Q_ = ureg.Quantity + assert Q_(octave, "octave").to("dimensionless").magnitude == pytest.approx(scalar) + assert Q_(scalar, "dimensionless").to("octave").magnitude == pytest.approx(octave) + + +@pytest.mark.parametrize( + "decade,scalar", + [ + (2.0, 100.0), # 2 decades == 100x + (1.0, 10.0), # 1 octave == 2x + (0.0, 1.0), + (-1.0, 0.1), + (-2.0, 0.01), + ], +) +def test_decade_conversion(ureg, decade, scalar): + """Test that a decade can be converted to a scalar and back. + """ + Q_ = ureg.Quantity + assert Q_(decade, "decade").to("dimensionless").magnitude == pytest.approx(scalar) + assert Q_(scalar, "dimensionless").to("decade").magnitude == pytest.approx(decade) + + +@pytest.mark.parametrize( + "dbm_value,mw_value", + [ + (0.0, 1.0), # 0.0 dBm == 1.0 mW + (10.0, 10.0), + (20.0, 100.0), + (-10.0, 0.1), + (-20.0, 0.01), + ], +) +def test_dbm_mw_conversion(ureg, dbm_value, mw_value): + """Test dBm values can convert to mW and back. + """ + Q_ = ureg.Quantity + assert Q_(dbm_value, "dBm").to("mW").magnitude == pytest.approx(mw_value) + assert Q_(mw_value, "mW").to("dBm").magnitude == pytest.approx(dbm_value) + + +@pytest.mark.xfail +def test_compound_log_unit_multiply_definition(auto_ureg): + """Check that compound log units can be defined using multiply. + """ + Q_ = auto_ureg.Quantity + canonical_def = Q_(-161, "dBm") / auto_ureg.Hz + mult_def = -161 * auto_ureg("dBm/Hz") + assert mult_def == canonical_def + + +@pytest.mark.xfail +def test_compound_log_unit_quantity_definition(auto_ureg): + """Check that compound log units can be defined using ``Quantity()``. + """ + Q_ = auto_ureg.Quantity + canonical_def = Q_(-161, "dBm") / auto_ureg.Hz + quantity_def = Q_(-161, "dBm/Hz") + assert quantity_def == canonical_def + + +def test_compound_log_unit_parse_definition(auto_ureg): + Q_ = auto_ureg.Quantity + canonical_def = Q_(-161, "dBm") / auto_ureg.Hz + parse_def = auto_ureg("-161 dBm/Hz") + assert parse_def == canonical_def + + +def test_compound_log_unit_parse_expr(auto_ureg): + """Check that compound log units can be defined using ``parse_expression()``. + """ + Q_ = auto_ureg.Quantity + canonical_def = Q_(-161, "dBm") / auto_ureg.Hz + parse_def = auto_ureg.parse_expression("-161 dBm/Hz") + assert canonical_def == parse_def + + +@pytest.mark.xfail +def test_dbm_db_addition(auto_ureg): + """Test a dB value can be added to a dBm and the answer is correct. + """ + power = (5 * auto_ureg.dBm) + (10 * auto_ureg.dB) + assert power.to("dBm").magnitude == pytest.approx(15) + + +@pytest.mark.xfail +@pytest.mark.parametrize( + "freq1,octaves,freq2", + [(100, 2.0, 400), (50, 1.0, 100), (200, 0.0, 200),], # noqa: E231 +) +def test_frequency_octave_addition(auto_ureg, freq1, octaves, freq2): + """Test an Octave can be added to a frequency correctly + """ + freq1 = freq1 * auto_ureg.Hz + shift = octaves * auto_ureg.Octave + new_freq = freq1 + shift + assert new_freq.units == freq1.units + assert new_freq.magnitude == pytest.approx(freq2) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/pint/unit.py new/Pint-0.16.1/pint/unit.py --- old/Pint-0.15/pint/unit.py 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/pint/unit.py 2020-09-22 05:12:30.000000000 +0200 @@ -28,9 +28,9 @@ def __reduce__(self): # See notes in Quantity.__reduce__ - from . import _unpickle + from . import _unpickle_unit - return _unpickle, (Unit, self._units) + return _unpickle_unit, (Unit, self._units) def __init__(self, units): super().__init__() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/readthedocs.yml new/Pint-0.16.1/readthedocs.yml --- old/Pint-0.15/readthedocs.yml 2020-08-22 21:22:43.000000000 +0200 +++ new/Pint-0.16.1/readthedocs.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,12 +0,0 @@ -version: 2 -build: - image: latest -sphinx: - configuration: docs/conf.py - fail_on_warning: false -python: - version: 3.7 - install: - - requirements: requirements_docs.txt - - method: pip - path: . diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Pint-0.15/setup.cfg new/Pint-0.16.1/setup.cfg --- old/Pint-0.15/setup.cfg 2020-08-22 21:22:44.000000000 +0200 +++ new/Pint-0.16.1/setup.cfg 2020-09-22 05:12:31.000000000 +0200 @@ -28,9 +28,9 @@ include_package_data = True python_requires = >=3.6 install_requires = - setuptools packaging importlib-metadata; python_version < '3.8' + importlib-resources; python_version < '3.7' setup_requires = setuptools; setuptools_scm test_suite = pint.testsuite.testsuite scripts = pint/pint-convert