From 3ad6e39185e38c3617861bad93b8b616f8e6b4b8 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Tue, 12 Feb 2019 22:40:50 +0000 Subject: [PATCH 01/44] create quantityscalar and sequence classes --- pint/quantity.py | 131 ++++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 53 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index c88bba1d7..b0aa07981 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -78,7 +78,7 @@ def wrapped(self, *args, **kwargs): @fix_str_conversions -class _Quantity(PrettyIPython, SharedRegistryObject): +class BaseQuantity(PrettyIPython, SharedRegistryObject): """Implements a class to describe a physical quantity: the product of a numerical value and a unit of measurement. @@ -95,7 +95,9 @@ def __reduce__(self): from . import _build_quantity return _build_quantity, (self.magnitude, self._units) - def __new__(cls, value, units=None): + + @classmethod + def _new(cls, value, units=None): if units is None: if isinstance(value, string_types): if value == '': @@ -132,18 +134,8 @@ def __new__(cls, value, units=None): inst.__used = False inst.__handling = None - # Only instances where the magnitude is iterable should have __iter__() - if hasattr(inst._magnitude,"__iter__"): - inst.__iter__ = cls._iter return inst - def _iter(self): - """ - Will be become __iter__() for instances with iterable magnitudes - """ - # # Allow exception to propagate in case of non-iterable magnitude - it_mag = iter(self.magnitude) - return iter((self.__class__(mag, self._units) for mag in it_mag)) @property def debug_used(self): return self.__used @@ -825,7 +817,6 @@ def _imul_div(self, other, magnitude_op, units_op=None): self._magnitude = magnitude_op(self._magnitude, other._magnitude) self._units = units_op(self._units, other._units) - return self @check_implemented @@ -1395,42 +1386,6 @@ def __getattr__(self, item): raise AttributeError("Neither Quantity object nor its magnitude ({}) " "has attribute '{}'".format(self._magnitude, item)) - def __getitem__(self, key): - try: - value = self._magnitude[key] - return self.__class__(value, self._units) - except TypeError: - raise TypeError("Neither Quantity object nor its magnitude ({})" - "supports indexing".format(self._magnitude)) - - def __setitem__(self, key, value): - try: - if math.isnan(value): - self._magnitude[key] = value - return - except (TypeError, DimensionalityError): - pass - - try: - if isinstance(value, self.__class__): - factor = self.__class__(value.magnitude, value._units / self._units).to_root_units() - else: - factor = self.__class__(value, self._units ** (-1)).to_root_units() - - if isinstance(factor, self.__class__): - if not factor.dimensionless: - raise DimensionalityError(value, self.units, - extra_msg='. Assign a quantity with the same dimensionality or ' - 'access the magnitude directly as ' - '`obj.magnitude[%s] = %s`' % (key, value)) - self._magnitude[key] = factor.magnitude - else: - self._magnitude[key] = factor - - except TypeError: - raise TypeError("Neither Quantity object nor its magnitude ({})" - "supports indexing".format(self._magnitude)) - def tolist(self): units = self._units return [self.__class__(value, units).tolist() if isinstance(value, list) else self.__class__(value, units) @@ -1671,14 +1626,84 @@ def _ok_for_muldiv(self, no_offset_units=None): def to_timedelta(self): return datetime.timedelta(microseconds=self.to('microseconds').magnitude) +class QuantitySequenceMixin(object): + def __getitem__(self, key): + try: + value = self._magnitude[key] + return self.__class__(value, self._units) + except TypeError: + raise TypeError("Neither Quantity object nor its magnitude ({})" + "supports indexing".format(self._magnitude)) + + def __setitem__(self, key, value): + try: + if math.isnan(value): + self._magnitude[key] = value + return + except (TypeError, DimensionalityError): + pass + try: + if isinstance(value, self.__class__): + factor = self.__class__(value.magnitude, value._units / self._units).to_root_units() + else: + factor = self.__class__(value, self._units ** (-1)).to_root_units() -def build_quantity_class(registry, force_ndarray=False): + if isinstance(factor, self.__class__): + if not factor.dimensionless: + raise DimensionalityError(value, self.units, + extra_msg='. Assign a quantity with the same dimensionality or ' + 'access the magnitude directly as ' + '`obj.magnitude[%s] = %s`' % (key, value)) + self._magnitude[key] = factor.magnitude + else: + self._magnitude[key] = factor + + except TypeError: + raise TypeError("Neither Quantity object nor its magnitude ({})" + "supports indexing".format(self._magnitude)) + def __iter__(self): + """ + Will be become __iter__() for instances with iterable magnitudes + """ + # # Allow exception to propagate in case of non-iterable magnitude + it_mag = iter(self.magnitude) + return iter((self.__class__(mag, self._units) for mag in it_mag)) - class Quantity(_Quantity): - pass +def build_quantity_class(registry, force_ndarray=False): + + class Quantity(BaseQuantity): + def __new__(cls, value, units=None): + if units is None: + if isinstance(value, string_types): + if value == '': + raise ValueError('Expression to parse as Quantity cannot ' + 'be an empty string.') + inst = cls._REGISTRY.parse_expression(value) + return cls.__new__(cls, inst) + elif isinstance(value, cls): + inst = copy.copy(value) + else: + inst = object.__new__(cls) + inst._magnitude = _to_magnitude(value, inst.force_ndarray) + inst._units = UnitsContainer() + return inst + if hasattr(value, "__iter__"): + return QuantitySequence._new(value,units) + else: + return QuantityScalar._new(value,units) + Quantity._REGISTRY = registry Quantity.force_ndarray = force_ndarray - + + class QuantityScalar(Quantity): + def __new__(cls, value, units=None): + inst = Quantity.__new__(Quantity, value, units) + return inst + + class QuantitySequence(Quantity,QuantitySequenceMixin): + def __new__(cls, value, units=None): + inst = Quantity.__new__(Quantity, value, units) + return inst return Quantity From 0d0097d03d68c6c21ccf3a425973a5b2a4e7ed47 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Thu, 14 Feb 2019 20:00:43 +0000 Subject: [PATCH 02/44] add __array_function__ --- pint/quantity.py | 205 +++++++++++++++++++++++++++++------------------ 1 file changed, 128 insertions(+), 77 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index b0aa07981..e4e439981 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -76,7 +76,50 @@ def wrapped(self, *args, **kwargs): return result return wrapped - +HANDLED_FUNCTIONS = {} + +def implements(numpy_function): + """Register an __array_function__ implementation for BaseQuantity objects.""" + def decorator(func): + HANDLED_FUNCTIONS[numpy_function] = func + return func + return decorator + +def convert_to_consistent_units(*args): + """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities + into the units of the first Quantiy/Sequence of quantities. Other args are left untouched. + """ + out_units=None + for arg in args: + if isinstance(arg,BaseQuantity): + out_units = arg.units + elif hasattr(arg, "__iter__") and not isinstance(arg, string_types): + if isinstance(arg[0],BaseQuantity): + out_units = arg[0].units + if out_units is not None: + break + + new_args=[] + for arg in args: + if isinstance(arg,BaseQuantity): + arg = arg.m_as(out_units) + elif hasattr(arg, "__iter__") and not isinstance(arg, string_types): + if isinstance(arg[0],BaseQuantity): + arg = [item.m_as(out_units) for item in arg] + new_args.append(arg) + return out_units, new_args + +def implement_func(func_str): + func = getattr(np,func_str) + @implements(func) + def _(*args): + out_units, new_args = convert_to_consistent_units(*args) + Q_ = out_units._REGISTRY.Quantity + return Q_(func(*new_args), out_units) + +for func_str in ['linspace', 'concatenate', 'hstack', 'vstack']: + implement_func(func_str) + @fix_str_conversions class BaseQuantity(PrettyIPython, SharedRegistryObject): """Implements a class to describe a physical quantity: @@ -87,6 +130,12 @@ class BaseQuantity(PrettyIPython, SharedRegistryObject): :param units: units of the physical quantity to be created. :type units: UnitsContainer, str or Quantity. """ + def __array_function__(self, func, types, args, kwargs): + if func not in HANDLED_FUNCTIONS: + return NotImplemented + if not all(issubclass(t, BaseQuantity) for t in types): + return NotImplemented + return HANDLED_FUNCTIONS[func](*args, **kwargs) #: Default formatting string. default_format = '' @@ -120,7 +169,7 @@ def _new(cls, value, units=None): inst._magnitude = _to_magnitude(value, inst.force_ndarray) inst._units = inst._REGISTRY.parse_units(units)._units elif isinstance(units, SharedRegistryObject): - if isinstance(units, _Quantity) and units.magnitude != 1: + if isinstance(units, BaseQuantity) and units.magnitude != 1: inst = copy.copy(units) logger.warning('Creating new Quantity using a non unity ' 'Quantity as units.') @@ -223,7 +272,7 @@ def __format__(self, spec): def _repr_pretty_(self, p, cycle): if cycle: - super(_Quantity, self)._repr_pretty_(p, cycle) + super(BaseQuantity, self)._repr_pretty_(p, cycle) else: p.pretty(self.magnitude) p.text(" ") @@ -1123,7 +1172,7 @@ def __neg__(self): def __eq__(self, other): # We compare to the base class of Quantity because # each Quantity class is unique. - if not isinstance(other, _Quantity): + if not isinstance(other, BaseQuantity): if _eq(other, 0, True): # Handle the special case in which we compare to zero # (or an array of zeros) @@ -1255,49 +1304,6 @@ def __bool__(self): tuple(__prod_units.keys()) + \ tuple(__copy_units) + tuple(__skip_other_args) - def clip(self, first=None, second=None, out=None, **kwargs): - min = kwargs.get('min', first) - max = kwargs.get('max', second) - - if min is None and max is None: - raise TypeError('clip() takes at least 3 arguments (2 given)') - - if max is None and 'min' not in kwargs: - min, max = max, min - - kwargs = {'out': out} - - if min is not None: - if isinstance(min, self.__class__): - kwargs['min'] = min.to(self).magnitude - elif self.dimensionless: - kwargs['min'] = min - else: - raise DimensionalityError('dimensionless', self._units) - - if max is not None: - if isinstance(max, self.__class__): - kwargs['max'] = max.to(self).magnitude - elif self.dimensionless: - kwargs['max'] = max - else: - raise DimensionalityError('dimensionless', self._units) - - return self.__class__(self.magnitude.clip(**kwargs), self._units) - - def fill(self, value): - self._units = value._units - return self.magnitude.fill(value.magnitude) - - def put(self, indices, values, mode='raise'): - if isinstance(values, self.__class__): - values = values.to(self).magnitude - elif self.dimensionless: - values = self.__class__(values, '').to(self) - else: - raise DimensionalityError('dimensionless', self._units) - self.magnitude.put(indices, values, mode) - @property def real(self): return self.__class__(self._magnitude.real, self._units) @@ -1358,38 +1364,32 @@ def __numpy_method_wrap(self, func, *args, **kwargs): return value - def __len__(self): - return len(self._magnitude) - def __getattr__(self, item): - # Attributes starting with `__array_` are common attributes of NumPy ndarray. - # They are requested by numpy functions. - if item.startswith('__array_'): - warnings.warn("The unit of the quantity is stripped.", UnitStrippedWarning) - if isinstance(self._magnitude, ndarray): - return getattr(self._magnitude, item) - else: - # If an `__array_` attributes is requested but the magnitude is not an ndarray, - # we convert the magnitude to a numpy ndarray. - self._magnitude = _to_magnitude(self._magnitude, force_ndarray=True) - return getattr(self._magnitude, item) - elif item in self.__handled: - if not isinstance(self._magnitude, ndarray): - self._magnitude = _to_magnitude(self._magnitude, True) - attr = getattr(self._magnitude, item) - if callable(attr): - return functools.partial(self.__numpy_method_wrap, attr) - return attr - try: - return getattr(self._magnitude, item) - except AttributeError as ex: - raise AttributeError("Neither Quantity object nor its magnitude ({}) " - "has attribute '{}'".format(self._magnitude, item)) + # def __getattr__(self, item): + # # Attributes starting with `__array_` are common attributes of NumPy ndarray. + # # They are requested by numpy functions. + # if item.startswith('__array_'): + # warnings.warn("The unit of the quantity is stripped.", UnitStrippedWarning) + # if isinstance(self._magnitude, ndarray): + # return getattr(self._magnitude, item) + # else: + # # If an `__array_` attributes is requested but the magnitude is not an ndarray, + # # we convert the magnitude to a numpy ndarray. + # self._magnitude = _to_magnitude(self._magnitude, force_ndarray=True) + # return getattr(self._magnitude, item) + # elif item in self.__handled: + # if not isinstance(self._magnitude, ndarray): + # self._magnitude = _to_magnitude(self._magnitude, True) + # attr = getattr(self._magnitude, item) + # if callable(attr): + # return functools.partial(self.__numpy_method_wrap, attr) + # return attr + # try: + # return getattr(self._magnitude, item) + # except AttributeError as ex: + # raise AttributeError("Neither Quantity object nor its magnitude ({}) " + # "has attribute '{}'".format(self._magnitude, item)) - def tolist(self): - units = self._units - return [self.__class__(value, units).tolist() if isinstance(value, list) else self.__class__(value, units) - for value in self._magnitude.tolist()] __array_priority__ = 17 @@ -1627,6 +1627,9 @@ def to_timedelta(self): return datetime.timedelta(microseconds=self.to('microseconds').magnitude) class QuantitySequenceMixin(object): + def __len__(self): + return len(self._magnitude) + def __getitem__(self, key): try: value = self._magnitude[key] @@ -1670,6 +1673,54 @@ def __iter__(self): it_mag = iter(self.magnitude) return iter((self.__class__(mag, self._units) for mag in it_mag)) + def clip(self, first=None, second=None, out=None, **kwargs): + min = kwargs.get('min', first) + max = kwargs.get('max', second) + + if min is None and max is None: + raise TypeError('clip() takes at least 3 arguments (2 given)') + + if max is None and 'min' not in kwargs: + min, max = max, min + + kwargs = {'out': out} + + if min is not None: + if isinstance(min, self.__class__): + kwargs['min'] = min.to(self).magnitude + elif self.dimensionless: + kwargs['min'] = min + else: + raise DimensionalityError('dimensionless', self._units) + + if max is not None: + if isinstance(max, self.__class__): + kwargs['max'] = max.to(self).magnitude + elif self.dimensionless: + kwargs['max'] = max + else: + raise DimensionalityError('dimensionless', self._units) + + return self.__class__(self.magnitude.clip(**kwargs), self._units) + + def fill(self, value): + self._units = value._units + return self.magnitude.fill(value.magnitude) + + def put(self, indices, values, mode='raise'): + if isinstance(values, self.__class__): + values = values.to(self).magnitude + elif self.dimensionless: + values = self.__class__(values, '').to(self) + else: + raise DimensionalityError('dimensionless', self._units) + self.magnitude.put(indices, values, mode) + + def tolist(self): + units = self._units + return [self.__class__(value, units).tolist() if isinstance(value, list) else self.__class__(value, units) + for value in self._magnitude.tolist()] + def build_quantity_class(registry, force_ndarray=False): From 431d5b49104b126711181ab3fa77f806d4fc3505 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sat, 16 Feb 2019 21:34:25 +0000 Subject: [PATCH 03/44] replace _Q with BaseQuantity --- pint/__init__.py | 2 +- pint/quantity.py | 6 +-- pint/registry.py | 4 +- pint/testsuite/__init__.py | 8 ++-- pint/testsuite/test_numpy.py | 2 +- pint/testsuite/test_quantity.py | 70 ++++++++++++++++----------------- pint/testsuite/test_util.py | 2 +- pint/util.py | 2 +- 8 files changed, 48 insertions(+), 48 deletions(-) diff --git a/pint/__init__.py b/pint/__init__.py index 08ac13241..2a22694ab 100644 --- a/pint/__init__.py +++ b/pint/__init__.py @@ -46,7 +46,7 @@ _APP_REGISTRY = _DEFAULT_REGISTRY -def _build_quantity(value, units): +def _buildBaseQuantity(value, units): """Build Quantity using the Application registry. Used only for unpickling operations. """ diff --git a/pint/quantity.py b/pint/quantity.py index dff1283da..82f1a3220 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -157,8 +157,8 @@ def __array_function__(self, func, types, args, kwargs): default_format = '' def __reduce__(self): - from . import _build_quantity - return _build_quantity, (self.magnitude, self._units) + from . import _buildBaseQuantity + return _buildBaseQuantity, (self.magnitude, self._units) @classmethod @@ -1787,7 +1787,7 @@ def tolist(self): for value in self._magnitude.tolist()] -def build_quantity_class(registry, force_ndarray=False): +def buildBaseQuantity_class(registry, force_ndarray=False): class Quantity(BaseQuantity): def __new__(cls, value, units=None): diff --git a/pint/registry.py b/pint/registry.py index 32baa024a..920ef2597 100644 --- a/pint/registry.py +++ b/pint/registry.py @@ -119,8 +119,8 @@ def __init__(self, filename='', force_ndarray=False, on_redefinition='warn', aut from .unit import build_unit_class self.Unit = build_unit_class(self) - from .quantity import build_quantity_class - self.Quantity = build_quantity_class(self, force_ndarray) + from .quantity import buildBaseQuantity_class + self.Quantity = buildBaseQuantity_class(self, force_ndarray) from .measurement import build_measurement_class self.Measurement = build_measurement_class(self, force_ndarray) diff --git a/pint/testsuite/__init__.py b/pint/testsuite/__init__.py index 4b02bc8e5..7cf1cb99c 100644 --- a/pint/testsuite/__init__.py +++ b/pint/testsuite/__init__.py @@ -13,7 +13,7 @@ from pint.compat import ndarray, np from pint import logger, UnitRegistry -from pint.quantity import _Quantity +from pint.quantity import BaseQuantity from pint.testsuite.helpers import PintOutputChecker from logging.handlers import BufferingHandler @@ -79,15 +79,15 @@ def setUpClass(cls): cls.U_ = cls.ureg.Unit def _get_comparable_magnitudes(self, first, second, msg): - if isinstance(first, _Quantity) and isinstance(second, _Quantity): + if isinstance(first, BaseQuantity) and isinstance(second, BaseQuantity): second = second.to(first) self.assertEqual(first.units, second.units, msg=msg + ' Units are not equal.') m1, m2 = first.magnitude, second.magnitude - elif isinstance(first, _Quantity): + elif isinstance(first, BaseQuantity): self.assertTrue(first.dimensionless, msg=msg + ' The first is not dimensionless.') first = first.to('') m1, m2 = first.magnitude, second - elif isinstance(second, _Quantity): + elif isinstance(second, BaseQuantity): self.assertTrue(second.dimensionless, msg=msg + ' The second is not dimensionless.') second = second.to('') m1, m2 = first, second.magnitude diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 360b29b53..42ec259f7 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -458,7 +458,7 @@ def test_exponentiation_array_exp_2(self): self.assertRaises(DimensionalityError, op.pow, arr_cp, q_cp) # ..not for op.ipow ! # q_cp is treated as if it is an array. The units are ignored. - # _Quantity.__ipow__ is never called + # BaseQuantity.__ipow__ is never called arr_cp = copy.copy(arr) q_cp = copy.copy(q) self.assertRaises(DimensionalityError, op.ipow, arr_cp, q_cp) diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py index fdb246007..c5ba7fbbb 100644 --- a/pint/testsuite/test_quantity.py +++ b/pint/testsuite/test_quantity.py @@ -19,7 +19,7 @@ class TestQuantity(QuantityTestCase): FORCE_NDARRAY = False - def test_quantity_creation(self): + def testBaseQuantity_creation(self): for args in ((4.2, 'meter'), (4.2, UnitsContainer(meter=1)), (4.2, self.ureg.meter), @@ -44,13 +44,13 @@ def test_quantity_creation(self): self.assertEqual(4.2 * self.ureg.meter, self.Q_(4.2, 2 * self.ureg.meter)) self.assertEqual(len(buffer), 1) - def test_quantity_bool(self): + def testBaseQuantity_bool(self): self.assertTrue(self.Q_(1, None)) self.assertTrue(self.Q_(1, 'meter')) self.assertFalse(self.Q_(0, None)) self.assertFalse(self.Q_(0, 'meter')) - def test_quantity_comparison(self): + def testBaseQuantity_comparison(self): x = self.Q_(4.2, 'meter') y = self.Q_(4.2, 'meter') z = self.Q_(5, 'meter') @@ -81,16 +81,16 @@ def test_quantity_comparison(self): self.assertLess(self.Q_(10, 'meter'), self.Q_(5, 'kilometer')) - def test_quantity_comparison_convert(self): + def testBaseQuantity_comparison_convert(self): self.assertEqual(self.Q_(1000, 'millimeter'), self.Q_(1, 'meter')) self.assertEqual(self.Q_(1000, 'millimeter/min'), self.Q_(1000/60, 'millimeter/s')) - def test_quantity_repr(self): + def testBaseQuantity_repr(self): x = self.Q_(4.2, UnitsContainer(meter=1)) self.assertEqual(str(x), '4.2 meter') self.assertEqual(repr(x), "") - def test_quantity_hash(self): + def testBaseQuantity_hash(self): x = self.Q_(4.2, 'meter') x2 = self.Q_(4200, 'millimeter') y = self.Q_(2, 'second') @@ -106,7 +106,7 @@ def test_quantity_hash(self): z2 = ureg2.Quantity(0.5, 'hertz') self.assertEqual(hash(y * z), hash(y2 * z2)) - def test_quantity_format(self): + def testBaseQuantity_format(self): x = self.Q_(4.12345678, UnitsContainer(meter=2, kilogram=1, second=-1)) for spec, result in (('{0}', str(x)), ('{0!s}', str(x)), ('{0!r}', repr(x)), ('{0.magnitude}', str(x.magnitude)), ('{0.units}', str(x.units)), @@ -511,7 +511,7 @@ def _test_not_inplace(self, operator, value1, value2, expected_result, unit=None self.assertNotEqual(id(result), id1) self.assertNotEqual(id(result), id2) - def _test_quantity_add_sub(self, unit, func): + def _testBaseQuantity_add_sub(self, unit, func): x = self.Q_(unit, 'centimeter') y = self.Q_(unit, 'inch') z = self.Q_(unit, 'second') @@ -533,7 +533,7 @@ def _test_quantity_add_sub(self, unit, func): self.assertRaises(DimensionalityError, op.sub, x, 10) self.assertRaises(DimensionalityError, op.sub, x, z) - def _test_quantity_iadd_isub(self, unit, func): + def _testBaseQuantity_iadd_isub(self, unit, func): x = self.Q_(unit, 'centimeter') y = self.Q_(unit, 'inch') z = self.Q_(unit, 'second') @@ -555,7 +555,7 @@ def _test_quantity_iadd_isub(self, unit, func): self.assertRaises(DimensionalityError, op.sub, x, 10) self.assertRaises(DimensionalityError, op.sub, x, z) - def _test_quantity_mul_div(self, unit, func): + def _testBaseQuantity_mul_div(self, unit, func): func(op.mul, unit * 10.0, '4.2*meter', '42*meter', unit) func(op.mul, '4.2*meter', unit * 10.0, '42*meter', unit) func(op.mul, '4.2*meter', '10*inch', '42*meter*inch', unit) @@ -563,7 +563,7 @@ def _test_quantity_mul_div(self, unit, func): func(op.truediv, '4.2*meter', unit * 10.0, '0.42*meter', unit) func(op.truediv, '4.2*meter', '10*inch', '0.42*meter/inch', unit) - def _test_quantity_imul_idiv(self, unit, func): + def _testBaseQuantity_imul_idiv(self, unit, func): #func(op.imul, 10.0, '4.2*meter', '42*meter') func(op.imul, '4.2*meter', 10.0, '42*meter', unit) func(op.imul, '4.2*meter', '10*inch', '42*meter*inch', unit) @@ -571,7 +571,7 @@ def _test_quantity_imul_idiv(self, unit, func): func(op.itruediv, '4.2*meter', unit * 10.0, '0.42*meter', unit) func(op.itruediv, '4.2*meter', '10*inch', '0.42*meter/inch', unit) - def _test_quantity_floordiv(self, unit, func): + def _testBaseQuantity_floordiv(self, unit, func): a = self.Q_('10*meter') b = self.Q_('3*second') self.assertRaises(DimensionalityError, op.floordiv, a, b) @@ -583,7 +583,7 @@ def _test_quantity_floordiv(self, unit, func): func(op.floordiv, unit * 10.0, '4.2*meter/meter', 2, unit) func(op.floordiv, '10*meter', '4.2*inch', 93, unit) - def _test_quantity_mod(self, unit, func): + def _testBaseQuantity_mod(self, unit, func): a = self.Q_('10*meter') b = self.Q_('3*second') self.assertRaises(DimensionalityError, op.mod, a, b) @@ -594,11 +594,11 @@ def _test_quantity_mod(self, unit, func): self.assertRaises(DimensionalityError, op.imod, a, 3) func(op.mod, unit * 10.0, '4.2*meter/meter', 1.6, unit) - def _test_quantity_ifloordiv(self, unit, func): + def _testBaseQuantity_ifloordiv(self, unit, func): func(op.ifloordiv, 10.0, '4.2*meter/meter', 2, unit) func(op.ifloordiv, '10*meter', '4.2*inch', 93, unit) - def _test_quantity_divmod_one(self, a, b): + def _testBaseQuantity_divmod_one(self, a, b): if isinstance(a, string_types): a = self.Q_(a) if isinstance(b, string_types): @@ -625,17 +625,17 @@ def _test_quantity_divmod_one(self, a, b): copy_a //= b self.assertEqual(copy_a, q) - def _test_quantity_divmod(self): - self._test_quantity_divmod_one('10*meter', '4.2*inch') - self._test_quantity_divmod_one('-10*meter', '4.2*inch') - self._test_quantity_divmod_one('-10*meter', '-4.2*inch') - self._test_quantity_divmod_one('10*meter', '-4.2*inch') + def _testBaseQuantity_divmod(self): + self._testBaseQuantity_divmod_one('10*meter', '4.2*inch') + self._testBaseQuantity_divmod_one('-10*meter', '4.2*inch') + self._testBaseQuantity_divmod_one('-10*meter', '-4.2*inch') + self._testBaseQuantity_divmod_one('10*meter', '-4.2*inch') - self._test_quantity_divmod_one('400*degree', '3') - self._test_quantity_divmod_one('4', '180 degree') - self._test_quantity_divmod_one(4, '180 degree') - self._test_quantity_divmod_one('20', 4) - self._test_quantity_divmod_one('300*degree', '100 degree') + self._testBaseQuantity_divmod_one('400*degree', '3') + self._testBaseQuantity_divmod_one('4', '180 degree') + self._testBaseQuantity_divmod_one(4, '180 degree') + self._testBaseQuantity_divmod_one('20', 4) + self._testBaseQuantity_divmod_one('300*degree', '100 degree') a = self.Q_('10*meter') b = self.Q_('3*second') @@ -644,14 +644,14 @@ def _test_quantity_divmod(self): self.assertRaises(DimensionalityError, divmod, a, 3) def _test_numeric(self, unit, ifunc): - self._test_quantity_add_sub(unit, self._test_not_inplace) - self._test_quantity_iadd_isub(unit, ifunc) - self._test_quantity_mul_div(unit, self._test_not_inplace) - self._test_quantity_imul_idiv(unit, ifunc) - self._test_quantity_floordiv(unit, self._test_not_inplace) - self._test_quantity_mod(unit, self._test_not_inplace) - self._test_quantity_divmod() - #self._test_quantity_ifloordiv(unit, ifunc) + self._testBaseQuantity_add_sub(unit, self._test_not_inplace) + self._testBaseQuantity_iadd_isub(unit, ifunc) + self._testBaseQuantity_mul_div(unit, self._test_not_inplace) + self._testBaseQuantity_imul_idiv(unit, ifunc) + self._testBaseQuantity_floordiv(unit, self._test_not_inplace) + self._testBaseQuantity_mod(unit, self._test_not_inplace) + self._testBaseQuantity_divmod() + #self._testBaseQuantity_ifloordiv(unit, ifunc) def test_float(self): self._test_numeric(1., self._test_not_inplace) @@ -664,7 +664,7 @@ def test_fraction(self): def test_nparray(self): self._test_numeric(np.ones((1, 3)), self._test_inplace) - def test_quantity_abs_round(self): + def testBaseQuantity_abs_round(self): x = self.Q_(-4.2, 'meter') y = self.Q_(4.2, 'meter') @@ -680,7 +680,7 @@ def test_quantity_abs_round(self): self.assertIsNot(rx, zx, 'while testing {0}'.format(fun)) self.assertIsNot(ry, zy, 'while testing {0}'.format(fun)) - def test_quantity_float_complex(self): + def testBaseQuantity_float_complex(self): x = self.Q_(-4.2, None) y = self.Q_(4.2, None) z = self.Q_(1, 'meter') diff --git a/pint/testsuite/test_util.py b/pint/testsuite/test_util.py index 915fc6648..10ee30216 100644 --- a/pint/testsuite/test_util.py +++ b/pint/testsuite/test_util.py @@ -127,7 +127,7 @@ def test_uc_conversion(self): a = UnitsContainer(m=1) self.assertIs(to_units_container(a), a) - def test_quantity_conversion(self): + def testBaseQuantity_conversion(self): from pint.registry import UnitRegistry ureg = UnitRegistry() self.assertEqual(to_units_container(ureg.Quantity(1, UnitsContainer(m=1))), diff --git a/pint/util.py b/pint/util.py index 13fc14934..cad490768 100644 --- a/pint/util.py +++ b/pint/util.py @@ -606,7 +606,7 @@ def _is_dim(name): class SharedRegistryObject(object): """Base class for object keeping a refrence to the registree. - Such object are for now _Quantity and _Unit, in a number of places it is + Such object are for now BaseQuantity and _Unit, in a number of places it is that an object from this class has a '_units' attribute. """ From f62e9467613f45cbc337b9b5db83fe8fdb215aec Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 17 Feb 2019 12:02:11 +0000 Subject: [PATCH 04/44] change np ufuncs to check x2 is a BaseQuantity --- pint/quantity.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 82f1a3220..ca0f961b7 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -106,6 +106,8 @@ def convert_to_consistent_units(*args): arg = arg.m_as(out_units) elif hasattr(arg, "__iter__") and not isinstance(arg, string_types): if isinstance(arg[0],BaseQuantity): + if not all([isinstance(item,BaseQuantity) for item in arg]): + raise TypeError("{} contains items that aren't BaseQuantity type so cannot be converted".format(arg)) arg = [item.m_as(out_units) for item in arg] new_args.append(arg) return out_units, new_args @@ -1530,12 +1532,12 @@ def _wrap_output(self, ufname, i, objs, out): if tmp == 'size': out = self.__class__(out, self._units ** self._magnitude.size) elif tmp == 'div': - units1 = objs[0]._units if isinstance(objs[0], self.__class__) else UnitsContainer() - units2 = objs[1]._units if isinstance(objs[1], self.__class__) else UnitsContainer() + units1 = objs[0]._units if isinstance(objs[0], BaseQuantity) else UnitsContainer() + units2 = objs[1]._units if isinstance(objs[1], BaseQuantity) else UnitsContainer() out = self.__class__(out, units1 / units2) elif tmp == 'mul': - units1 = objs[0]._units if isinstance(objs[0], self.__class__) else UnitsContainer() - units2 = objs[1]._units if isinstance(objs[1], self.__class__) else UnitsContainer() + units1 = objs[0]._units if isinstance(objs[0], BaseQuantity) else UnitsContainer() + units2 = objs[1]._units if isinstance(objs[1], BaseQuantity) else UnitsContainer() out = self.__class__(out, units1 * units2) else: out = self.__class__(out, self._units ** tmp) From bbb44d6304d5940693c3028dab0580a653eae02a Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 17 Feb 2019 15:30:34 +0000 Subject: [PATCH 05/44] revert changes in test_quantity --- pint/testsuite/test_quantity.py | 70 ++++++++++++++++----------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py index c5ba7fbbb..fdb246007 100644 --- a/pint/testsuite/test_quantity.py +++ b/pint/testsuite/test_quantity.py @@ -19,7 +19,7 @@ class TestQuantity(QuantityTestCase): FORCE_NDARRAY = False - def testBaseQuantity_creation(self): + def test_quantity_creation(self): for args in ((4.2, 'meter'), (4.2, UnitsContainer(meter=1)), (4.2, self.ureg.meter), @@ -44,13 +44,13 @@ def testBaseQuantity_creation(self): self.assertEqual(4.2 * self.ureg.meter, self.Q_(4.2, 2 * self.ureg.meter)) self.assertEqual(len(buffer), 1) - def testBaseQuantity_bool(self): + def test_quantity_bool(self): self.assertTrue(self.Q_(1, None)) self.assertTrue(self.Q_(1, 'meter')) self.assertFalse(self.Q_(0, None)) self.assertFalse(self.Q_(0, 'meter')) - def testBaseQuantity_comparison(self): + def test_quantity_comparison(self): x = self.Q_(4.2, 'meter') y = self.Q_(4.2, 'meter') z = self.Q_(5, 'meter') @@ -81,16 +81,16 @@ def testBaseQuantity_comparison(self): self.assertLess(self.Q_(10, 'meter'), self.Q_(5, 'kilometer')) - def testBaseQuantity_comparison_convert(self): + def test_quantity_comparison_convert(self): self.assertEqual(self.Q_(1000, 'millimeter'), self.Q_(1, 'meter')) self.assertEqual(self.Q_(1000, 'millimeter/min'), self.Q_(1000/60, 'millimeter/s')) - def testBaseQuantity_repr(self): + def test_quantity_repr(self): x = self.Q_(4.2, UnitsContainer(meter=1)) self.assertEqual(str(x), '4.2 meter') self.assertEqual(repr(x), "") - def testBaseQuantity_hash(self): + def test_quantity_hash(self): x = self.Q_(4.2, 'meter') x2 = self.Q_(4200, 'millimeter') y = self.Q_(2, 'second') @@ -106,7 +106,7 @@ def testBaseQuantity_hash(self): z2 = ureg2.Quantity(0.5, 'hertz') self.assertEqual(hash(y * z), hash(y2 * z2)) - def testBaseQuantity_format(self): + def test_quantity_format(self): x = self.Q_(4.12345678, UnitsContainer(meter=2, kilogram=1, second=-1)) for spec, result in (('{0}', str(x)), ('{0!s}', str(x)), ('{0!r}', repr(x)), ('{0.magnitude}', str(x.magnitude)), ('{0.units}', str(x.units)), @@ -511,7 +511,7 @@ def _test_not_inplace(self, operator, value1, value2, expected_result, unit=None self.assertNotEqual(id(result), id1) self.assertNotEqual(id(result), id2) - def _testBaseQuantity_add_sub(self, unit, func): + def _test_quantity_add_sub(self, unit, func): x = self.Q_(unit, 'centimeter') y = self.Q_(unit, 'inch') z = self.Q_(unit, 'second') @@ -533,7 +533,7 @@ def _testBaseQuantity_add_sub(self, unit, func): self.assertRaises(DimensionalityError, op.sub, x, 10) self.assertRaises(DimensionalityError, op.sub, x, z) - def _testBaseQuantity_iadd_isub(self, unit, func): + def _test_quantity_iadd_isub(self, unit, func): x = self.Q_(unit, 'centimeter') y = self.Q_(unit, 'inch') z = self.Q_(unit, 'second') @@ -555,7 +555,7 @@ def _testBaseQuantity_iadd_isub(self, unit, func): self.assertRaises(DimensionalityError, op.sub, x, 10) self.assertRaises(DimensionalityError, op.sub, x, z) - def _testBaseQuantity_mul_div(self, unit, func): + def _test_quantity_mul_div(self, unit, func): func(op.mul, unit * 10.0, '4.2*meter', '42*meter', unit) func(op.mul, '4.2*meter', unit * 10.0, '42*meter', unit) func(op.mul, '4.2*meter', '10*inch', '42*meter*inch', unit) @@ -563,7 +563,7 @@ def _testBaseQuantity_mul_div(self, unit, func): func(op.truediv, '4.2*meter', unit * 10.0, '0.42*meter', unit) func(op.truediv, '4.2*meter', '10*inch', '0.42*meter/inch', unit) - def _testBaseQuantity_imul_idiv(self, unit, func): + def _test_quantity_imul_idiv(self, unit, func): #func(op.imul, 10.0, '4.2*meter', '42*meter') func(op.imul, '4.2*meter', 10.0, '42*meter', unit) func(op.imul, '4.2*meter', '10*inch', '42*meter*inch', unit) @@ -571,7 +571,7 @@ def _testBaseQuantity_imul_idiv(self, unit, func): func(op.itruediv, '4.2*meter', unit * 10.0, '0.42*meter', unit) func(op.itruediv, '4.2*meter', '10*inch', '0.42*meter/inch', unit) - def _testBaseQuantity_floordiv(self, unit, func): + def _test_quantity_floordiv(self, unit, func): a = self.Q_('10*meter') b = self.Q_('3*second') self.assertRaises(DimensionalityError, op.floordiv, a, b) @@ -583,7 +583,7 @@ def _testBaseQuantity_floordiv(self, unit, func): func(op.floordiv, unit * 10.0, '4.2*meter/meter', 2, unit) func(op.floordiv, '10*meter', '4.2*inch', 93, unit) - def _testBaseQuantity_mod(self, unit, func): + def _test_quantity_mod(self, unit, func): a = self.Q_('10*meter') b = self.Q_('3*second') self.assertRaises(DimensionalityError, op.mod, a, b) @@ -594,11 +594,11 @@ def _testBaseQuantity_mod(self, unit, func): self.assertRaises(DimensionalityError, op.imod, a, 3) func(op.mod, unit * 10.0, '4.2*meter/meter', 1.6, unit) - def _testBaseQuantity_ifloordiv(self, unit, func): + def _test_quantity_ifloordiv(self, unit, func): func(op.ifloordiv, 10.0, '4.2*meter/meter', 2, unit) func(op.ifloordiv, '10*meter', '4.2*inch', 93, unit) - def _testBaseQuantity_divmod_one(self, a, b): + def _test_quantity_divmod_one(self, a, b): if isinstance(a, string_types): a = self.Q_(a) if isinstance(b, string_types): @@ -625,17 +625,17 @@ def _testBaseQuantity_divmod_one(self, a, b): copy_a //= b self.assertEqual(copy_a, q) - def _testBaseQuantity_divmod(self): - self._testBaseQuantity_divmod_one('10*meter', '4.2*inch') - self._testBaseQuantity_divmod_one('-10*meter', '4.2*inch') - self._testBaseQuantity_divmod_one('-10*meter', '-4.2*inch') - self._testBaseQuantity_divmod_one('10*meter', '-4.2*inch') + def _test_quantity_divmod(self): + self._test_quantity_divmod_one('10*meter', '4.2*inch') + self._test_quantity_divmod_one('-10*meter', '4.2*inch') + self._test_quantity_divmod_one('-10*meter', '-4.2*inch') + self._test_quantity_divmod_one('10*meter', '-4.2*inch') - self._testBaseQuantity_divmod_one('400*degree', '3') - self._testBaseQuantity_divmod_one('4', '180 degree') - self._testBaseQuantity_divmod_one(4, '180 degree') - self._testBaseQuantity_divmod_one('20', 4) - self._testBaseQuantity_divmod_one('300*degree', '100 degree') + self._test_quantity_divmod_one('400*degree', '3') + self._test_quantity_divmod_one('4', '180 degree') + self._test_quantity_divmod_one(4, '180 degree') + self._test_quantity_divmod_one('20', 4) + self._test_quantity_divmod_one('300*degree', '100 degree') a = self.Q_('10*meter') b = self.Q_('3*second') @@ -644,14 +644,14 @@ def _testBaseQuantity_divmod(self): self.assertRaises(DimensionalityError, divmod, a, 3) def _test_numeric(self, unit, ifunc): - self._testBaseQuantity_add_sub(unit, self._test_not_inplace) - self._testBaseQuantity_iadd_isub(unit, ifunc) - self._testBaseQuantity_mul_div(unit, self._test_not_inplace) - self._testBaseQuantity_imul_idiv(unit, ifunc) - self._testBaseQuantity_floordiv(unit, self._test_not_inplace) - self._testBaseQuantity_mod(unit, self._test_not_inplace) - self._testBaseQuantity_divmod() - #self._testBaseQuantity_ifloordiv(unit, ifunc) + self._test_quantity_add_sub(unit, self._test_not_inplace) + self._test_quantity_iadd_isub(unit, ifunc) + self._test_quantity_mul_div(unit, self._test_not_inplace) + self._test_quantity_imul_idiv(unit, ifunc) + self._test_quantity_floordiv(unit, self._test_not_inplace) + self._test_quantity_mod(unit, self._test_not_inplace) + self._test_quantity_divmod() + #self._test_quantity_ifloordiv(unit, ifunc) def test_float(self): self._test_numeric(1., self._test_not_inplace) @@ -664,7 +664,7 @@ def test_fraction(self): def test_nparray(self): self._test_numeric(np.ones((1, 3)), self._test_inplace) - def testBaseQuantity_abs_round(self): + def test_quantity_abs_round(self): x = self.Q_(-4.2, 'meter') y = self.Q_(4.2, 'meter') @@ -680,7 +680,7 @@ def testBaseQuantity_abs_round(self): self.assertIsNot(rx, zx, 'while testing {0}'.format(fun)) self.assertIsNot(ry, zy, 'while testing {0}'.format(fun)) - def testBaseQuantity_float_complex(self): + def test_quantity_float_complex(self): x = self.Q_(-4.2, None) y = self.Q_(4.2, None) z = self.Q_(1, 'meter') From d76171f6f32929e566e399dea54b6106cc0b839d Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 17 Feb 2019 15:33:07 +0000 Subject: [PATCH 06/44] revert some more --- pint/registry.py | 4 ++-- pint/testsuite/test_util.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pint/registry.py b/pint/registry.py index 920ef2597..32baa024a 100644 --- a/pint/registry.py +++ b/pint/registry.py @@ -119,8 +119,8 @@ def __init__(self, filename='', force_ndarray=False, on_redefinition='warn', aut from .unit import build_unit_class self.Unit = build_unit_class(self) - from .quantity import buildBaseQuantity_class - self.Quantity = buildBaseQuantity_class(self, force_ndarray) + from .quantity import build_quantity_class + self.Quantity = build_quantity_class(self, force_ndarray) from .measurement import build_measurement_class self.Measurement = build_measurement_class(self, force_ndarray) diff --git a/pint/testsuite/test_util.py b/pint/testsuite/test_util.py index 10ee30216..915fc6648 100644 --- a/pint/testsuite/test_util.py +++ b/pint/testsuite/test_util.py @@ -127,7 +127,7 @@ def test_uc_conversion(self): a = UnitsContainer(m=1) self.assertIs(to_units_container(a), a) - def testBaseQuantity_conversion(self): + def test_quantity_conversion(self): from pint.registry import UnitRegistry ureg = UnitRegistry() self.assertEqual(to_units_container(ureg.Quantity(1, UnitsContainer(m=1))), From 30d542a0c889ff9c1189d427c556b2cf75540a88 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 17 Feb 2019 15:44:19 +0000 Subject: [PATCH 07/44] some more reverts --- pint/quantity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pint/quantity.py b/pint/quantity.py index ca0f961b7..3a03796ed 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -1789,7 +1789,7 @@ def tolist(self): for value in self._magnitude.tolist()] -def buildBaseQuantity_class(registry, force_ndarray=False): +def build_quantity_class(registry, force_ndarray=False): class Quantity(BaseQuantity): def __new__(cls, value, units=None): From 5b9e473ead7ae3bb38726dfcfa61bd4da4366e76 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 17 Feb 2019 15:54:56 +0000 Subject: [PATCH 08/44] revert more --- pint/__init__.py | 2 +- pint/quantity.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pint/__init__.py b/pint/__init__.py index 2a22694ab..08ac13241 100644 --- a/pint/__init__.py +++ b/pint/__init__.py @@ -46,7 +46,7 @@ _APP_REGISTRY = _DEFAULT_REGISTRY -def _buildBaseQuantity(value, units): +def _build_quantity(value, units): """Build Quantity using the Application registry. Used only for unpickling operations. """ diff --git a/pint/quantity.py b/pint/quantity.py index 3a03796ed..86885fa37 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -159,8 +159,8 @@ def __array_function__(self, func, types, args, kwargs): default_format = '' def __reduce__(self): - from . import _buildBaseQuantity - return _buildBaseQuantity, (self.magnitude, self._units) + from . import _build_quantity + return _build_quantity, (self.magnitude, self._units) @classmethod From c21bfbd72b048b3cf4aaf7e59d7882f6a66f94bd Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Mon, 18 Feb 2019 23:04:42 +0000 Subject: [PATCH 09/44] revert getattr --- pint/quantity.py | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 86885fa37..257161437 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -1432,30 +1432,30 @@ def __numpy_method_wrap(self, func, *args, **kwargs): return value - # def __getattr__(self, item): - # # Attributes starting with `__array_` are common attributes of NumPy ndarray. - # # They are requested by numpy functions. - # if item.startswith('__array_'): - # warnings.warn("The unit of the quantity is stripped.", UnitStrippedWarning) - # if isinstance(self._magnitude, ndarray): - # return getattr(self._magnitude, item) - # else: - # # If an `__array_` attributes is requested but the magnitude is not an ndarray, - # # we convert the magnitude to a numpy ndarray. - # self._magnitude = _to_magnitude(self._magnitude, force_ndarray=True) - # return getattr(self._magnitude, item) - # elif item in self.__handled: - # if not isinstance(self._magnitude, ndarray): - # self._magnitude = _to_magnitude(self._magnitude, True) - # attr = getattr(self._magnitude, item) - # if callable(attr): - # return functools.partial(self.__numpy_method_wrap, attr) - # return attr - # try: - # return getattr(self._magnitude, item) - # except AttributeError as ex: - # raise AttributeError("Neither Quantity object nor its magnitude ({}) " - # "has attribute '{}'".format(self._magnitude, item)) + def __getattr__(self, item): + # Attributes starting with `__array_` are common attributes of NumPy ndarray. + # They are requested by numpy functions. + if item.startswith('__array_'): + warnings.warn("The unit of the quantity is stripped.", UnitStrippedWarning) + if isinstance(self._magnitude, ndarray): + return getattr(self._magnitude, item) + else: + # If an `__array_` attributes is requested but the magnitude is not an ndarray, + # we convert the magnitude to a numpy ndarray. + self._magnitude = _to_magnitude(self._magnitude, force_ndarray=True) + return getattr(self._magnitude, item) + elif item in self.__handled: + if not isinstance(self._magnitude, ndarray): + self._magnitude = _to_magnitude(self._magnitude, True) + attr = getattr(self._magnitude, item) + if callable(attr): + return functools.partial(self.__numpy_method_wrap, attr) + return attr + try: + return getattr(self._magnitude, item) + except AttributeError as ex: + raise AttributeError("Neither Quantity object nor its magnitude ({}) " + "has attribute '{}'".format(self._magnitude, item)) __array_priority__ = 17 From def8348fd5f12b8e4211eafb7a961539fc7fd86b Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Tue, 19 Feb 2019 21:56:24 +0000 Subject: [PATCH 10/44] __used -> _used --- pint/quantity.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 257161437..dbca13ff8 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -198,24 +198,24 @@ def _new(cls, value, units=None): else: raise TypeError('units must be of type str, Quantity or ' 'UnitsContainer; not {}.'.format(type(units))) - - inst.__used = False - inst.__handling = None + inst._used = False + inst._handling = None + print(inst._used) return inst @property def debug_used(self): - return self.__used + return self._used def __copy__(self): ret = self.__class__(copy.copy(self._magnitude), self._units) - ret.__used = self.__used + ret._used = self._used return ret def __deepcopy__(self, memo): ret = self.__class__(copy.deepcopy(self._magnitude, memo), copy.deepcopy(self._units, memo)) - ret.__used = self.__used + ret._used = self._used return ret def __str__(self): @@ -1578,11 +1578,11 @@ def __array_prepare__(self, obj, context=None): # Only one ufunc should be handled at a time. # If a ufunc is already being handled (and this is not another domain), # something is wrong.. - if self.__handling: + if self._handling: raise Exception('Cannot handled nested ufuncs.\n' 'Current: {}\n' - 'New: {}'.format(context, self.__handling)) - self.__handling = context + 'New: {}'.format(context, self._handling)) + self._handling = context return obj @@ -1599,15 +1599,15 @@ def __array_wrap__(self, obj, context=None): if i_out == 0: out = self._call_ufunc(uf, *objs) # If there are multiple outputs, - # store them in __handling (uf, objs, i_out, out0, out1, ...) + # store them in _handling (uf, objs, i_out, out0, out1, ...) # and return the first if uf.nout > 1: - self.__handling += out + self._handling += out out = out[0] else: # If this is not the first output, # just grab the result that was previously calculated. - out = self.__handling[3 + i_out] + out = self._handling[3 + i_out] return self._wrap_output(uf.__name__, i_out, objs, out) except (DimensionalityError, UndefinedUnitError) as ex: @@ -1620,7 +1620,7 @@ def __array_wrap__(self, obj, context=None): # If this is the last output argument for the ufunc, # we are done handling this ufunc. if uf.nout == i_out + 1: - self.__handling = None + self._handling = None return self.magnitude.__array_wrap__(obj, context) From 65905e05a02084b803bf6f0c0b3daa9594f91efe Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Tue, 19 Feb 2019 23:22:19 +0000 Subject: [PATCH 11/44] fix path without _used --- pint/quantity.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index dbca13ff8..5917a7c34 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -198,24 +198,23 @@ def _new(cls, value, units=None): else: raise TypeError('units must be of type str, Quantity or ' 'UnitsContainer; not {}.'.format(type(units))) - inst._used = False - inst._handling = None - print(inst._used) + inst.__used = False + inst.__handling = None return inst @property - def debug_used(self): - return self._used + def debug__used(self): + return self.__used def __copy__(self): ret = self.__class__(copy.copy(self._magnitude), self._units) - ret._used = self._used + ret.__used = self.__used return ret def __deepcopy__(self, memo): ret = self.__class__(copy.deepcopy(self._magnitude, memo), copy.deepcopy(self._units, memo)) - ret._used = self._used + ret.__used = self.__used return ret def __str__(self): @@ -1578,11 +1577,11 @@ def __array_prepare__(self, obj, context=None): # Only one ufunc should be handled at a time. # If a ufunc is already being handled (and this is not another domain), # something is wrong.. - if self._handling: + if self.__handling: raise Exception('Cannot handled nested ufuncs.\n' 'Current: {}\n' - 'New: {}'.format(context, self._handling)) - self._handling = context + 'New: {}'.format(context, self.__handling)) + self.__handling = context return obj @@ -1599,15 +1598,15 @@ def __array_wrap__(self, obj, context=None): if i_out == 0: out = self._call_ufunc(uf, *objs) # If there are multiple outputs, - # store them in _handling (uf, objs, i_out, out0, out1, ...) + # store them in __handling (uf, objs, i_out, out0, out1, ...) # and return the first if uf.nout > 1: - self._handling += out + self.__handling += out out = out[0] else: # If this is not the first output, # just grab the result that was previously calculated. - out = self._handling[3 + i_out] + out = self.__handling[3 + i_out] return self._wrap_output(uf.__name__, i_out, objs, out) except (DimensionalityError, UndefinedUnitError) as ex: @@ -1620,7 +1619,7 @@ def __array_wrap__(self, obj, context=None): # If this is the last output argument for the ufunc, # we are done handling this ufunc. if uf.nout == i_out + 1: - self._handling = None + self.__handling = None return self.magnitude.__array_wrap__(obj, context) @@ -1803,10 +1802,8 @@ def __new__(cls, value, units=None): elif isinstance(value, cls): inst = copy.copy(value) else: - inst = object.__new__(cls) - inst._magnitude = _to_magnitude(value, inst.force_ndarray) - inst._units = UnitsContainer() - return inst + value = _to_magnitude(value, cls.force_ndarray) + units = UnitsContainer() if hasattr(value, "__iter__"): return QuantitySequence._new(value,units) else: From 7fb3800f2fa8c63f6fc9eaacab291c5079717d38 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Wed, 20 Feb 2019 19:56:32 +0000 Subject: [PATCH 12/44] fix setitem --- pint/quantity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 5917a7c34..50c4f3239 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -1713,12 +1713,12 @@ def __setitem__(self, key, value): pass try: - if isinstance(value, self.__class__): + if isinstance(value, BaseQuantity): factor = self.__class__(value.magnitude, value._units / self._units).to_root_units() else: factor = self.__class__(value, self._units ** (-1)).to_root_units() - if isinstance(factor, self.__class__): + if isinstance(factor, BaseQuantity): if not factor.dimensionless: raise DimensionalityError(value, self.units, extra_msg='. Assign a quantity with the same dimensionality or ' From e07e23780b3e93e17658c2e928de71adae1b98f3 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Wed, 20 Feb 2019 20:02:09 +0000 Subject: [PATCH 13/44] fix put --- pint/quantity.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 50c4f3239..581cc1c24 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -1752,7 +1752,7 @@ def clip(self, first=None, second=None, out=None, **kwargs): kwargs = {'out': out} if min is not None: - if isinstance(min, self.__class__): + if isinstance(min, BaseQuantity): kwargs['min'] = min.to(self).magnitude elif self.dimensionless: kwargs['min'] = min @@ -1760,7 +1760,7 @@ def clip(self, first=None, second=None, out=None, **kwargs): raise DimensionalityError('dimensionless', self._units) if max is not None: - if isinstance(max, self.__class__): + if isinstance(max, BaseQuantity): kwargs['max'] = max.to(self).magnitude elif self.dimensionless: kwargs['max'] = max @@ -1774,7 +1774,7 @@ def fill(self, value): return self.magnitude.fill(value.magnitude) def put(self, indices, values, mode='raise'): - if isinstance(values, self.__class__): + if isinstance(values, BaseQuantity): values = values.to(self).magnitude elif self.dimensionless: values = self.__class__(values, '').to(self) From f6373fbf670565e556ba011bd1eb8f21d049a5cc Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Thu, 21 Feb 2019 22:26:51 +0000 Subject: [PATCH 14/44] some array manip tests --- pint/quantity.py | 3 ++- pint/testsuite/helpers.py | 3 ++- pint/testsuite/test_numpy.py | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 581cc1c24..8cf9c44d6 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -116,11 +116,12 @@ def implement_func(func_str): func = getattr(np,func_str) @implements(func) def _(*args): + print(func) out_units, new_args = convert_to_consistent_units(*args) Q_ = out_units._REGISTRY.Quantity return Q_(func(*new_args), out_units) -for func_str in ['linspace', 'concatenate', 'hstack', 'vstack']: +for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'broadcast_to', 'atleast_1d', 'atleast_2d', 'atleast_3d']: implement_func(func_str) diff --git a/pint/testsuite/helpers.py b/pint/testsuite/helpers.py index 1f58d212c..f290483c6 100644 --- a/pint/testsuite/helpers.py +++ b/pint/testsuite/helpers.py @@ -20,7 +20,8 @@ def requires_numpy18(): def requires_numpy_previous_than(version): if not HAS_NUMPY: return unittest.skip('Requires NumPy') - return unittest.skipUnless(StrictVersion(NUMPY_VER) < StrictVersion(version), 'Requires NumPy < %s' % version) + # return unittest.skipUnless(StrictVersion(NUMPY_VER) < StrictVersion(version), 'Requires NumPy < %s' % version) + return unittest.skipUnless(HAS_NUMPY, 'Requires NumPy') def requires_numpy(): diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 42ec259f7..4769b246c 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -26,6 +26,15 @@ def setUpClass(cls): @property def q(self): return [[1,2],[3,4]] * self.ureg.m + + def test_rollaxis(self): + self.assertQuantityEqual(np.rollaxis(self.q, 1), np.array([[1,2],[3,4]]).T * self.ureg.m) + + def test_moveaxis(self): + self.assertQuantityEqual(np.moveaxis(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) + + def test_swapaxes(self): + self.assertQuantityEqual(np.swapaxes(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) def test_tolist(self): self.assertEqual(self.q.tolist(), [[1*self.ureg.m, 2*self.ureg.m], [3*self.ureg.m, 4*self.ureg.m]]) From 0aea9bcbf5bc31b04d15181c46cc85fc602862c9 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Thu, 21 Feb 2019 22:31:37 +0000 Subject: [PATCH 15/44] remove broadcast --- pint/quantity.py | 2 +- pint/testsuite/test_numpy.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pint/quantity.py b/pint/quantity.py index 8cf9c44d6..d5cc9ae5f 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -121,7 +121,7 @@ def _(*args): Q_ = out_units._REGISTRY.Quantity return Q_(func(*new_args), out_units) -for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'broadcast_to', 'atleast_1d', 'atleast_2d', 'atleast_3d']: +for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'atleast_1d', 'atleast_2d', 'atleast_3d']: implement_func(func_str) diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 4769b246c..ad90457ac 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -27,6 +27,10 @@ def setUpClass(cls): def q(self): return [[1,2],[3,4]] * self.ureg.m + #TODO + # atleast_1d 2d 3d + # broadcast to removed? + def test_rollaxis(self): self.assertQuantityEqual(np.rollaxis(self.q, 1), np.array([[1,2],[3,4]]).T * self.ureg.m) From 077d12b90889dfb04e5d136f70b9439b95364909 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Thu, 21 Feb 2019 22:45:25 +0000 Subject: [PATCH 16/44] atleast nd --- pint/testsuite/test_numpy.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index ad90457ac..f0c69d6ff 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -28,18 +28,30 @@ def q(self): return [[1,2],[3,4]] * self.ureg.m #TODO - # atleast_1d 2d 3d + # atleast_3d # broadcast to removed? + def test_atleast_1d(self): + self.assertQuantityEqual(np.atleast_1d(self.q), self.q) + def test_atleast_2d(self): + self.assertQuantityEqual(np.atleast_2d(self.q), self.q) + + def test_atleast_3d(self): + self.assertQuantityEqual(np.atleast_3d(self.q), np.array([[[1],[2]],[[3],[4]]])* self.ureg.m) + + #################################### + # above here are __array_function tests, below may be applicable to older numpy too + #################################### + def test_rollaxis(self): self.assertQuantityEqual(np.rollaxis(self.q, 1), np.array([[1,2],[3,4]]).T * self.ureg.m) - def test_moveaxis(self): - self.assertQuantityEqual(np.moveaxis(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) + # Seems not in 1.19 + # def test_moveaxis(self): + # self.assertQuantityEqual(np.moveaxis(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) def test_swapaxes(self): self.assertQuantityEqual(np.swapaxes(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) - def test_tolist(self): self.assertEqual(self.q.tolist(), [[1*self.ureg.m, 2*self.ureg.m], [3*self.ureg.m, 4*self.ureg.m]]) From da9dcb83cf6b8ee7eef54c16e8793fbefb3797ee Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Thu, 21 Feb 2019 22:53:15 +0000 Subject: [PATCH 17/44] squuze --- pint/quantity.py | 2 +- pint/testsuite/test_numpy.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pint/quantity.py b/pint/quantity.py index d5cc9ae5f..b7f79bd8f 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -121,7 +121,7 @@ def _(*args): Q_ = out_units._REGISTRY.Quantity return Q_(func(*new_args), out_units) -for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'atleast_1d', 'atleast_2d', 'atleast_3d']: +for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze']: implement_func(func_str) diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index f0c69d6ff..2d17cfd49 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -39,12 +39,18 @@ def test_atleast_2d(self): def test_atleast_3d(self): self.assertQuantityEqual(np.atleast_3d(self.q), np.array([[[1],[2]],[[3],[4]]])* self.ureg.m) + def test_expand_dims(self): + self.assertQuantityEqual(np.expand_dims(self.q, 0), array([[[1, 2],[3, 4]]])* self.ureg.m) + #################################### # above here are __array_function tests, below may be applicable to older numpy too #################################### def test_rollaxis(self): self.assertQuantityEqual(np.rollaxis(self.q, 1), np.array([[1,2],[3,4]]).T * self.ureg.m) + + def test_squeeze(self): + self.assertQuantityEqual(np.squeeze(self.q), self.q) # Seems not in 1.19 # def test_moveaxis(self): From 2727d21a6a7fa3648148b25725ee5a1e3ce08d76 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Thu, 21 Feb 2019 23:02:02 +0000 Subject: [PATCH 18/44] uprev numpy, disable less useful versions --- .travis.yml | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index a759926d6..5c3c6fb43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,19 +9,20 @@ branches: env: # Should pandas tests be removed or replaced wih import checks? #- UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.14 PANDAS=1 - - UNCERTAINTIES="N" PYTHON="3.3" NUMPY_VERSION=1.9.2 PANDAS=0 - - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.11.2 PANDAS=0 - - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.11.2 PANDAS=0 - - UNCERTAINTIES="Y" PYTHON="3.5" NUMPY_VERSION=1.11.2 PANDAS=0 - - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.11.2 PANDAS=0 - - UNCERTAINTIES="N" PYTHON="2.7" NUMPY_VERSION=0 PANDAS=0 - - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=0 PANDAS=0 - # Test with the latest numpy version - - UNCERTAINTIES="N" PYTHON="2.7" NUMPY_VERSION=1.14 PANDAS=0 - - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.14 PANDAS=0 - - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.14 PANDAS=0 - - UNCERTAINTIES="Y" PYTHON="3.5" NUMPY_VERSION=1.14 PANDAS=0 - - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.14 PANDAS=0 + - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.17 PANDAS=0 + # - UNCERTAINTIES="N" PYTHON="3.3" NUMPY_VERSION=1.9.2 PANDAS=0 + # - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.11.2 PANDAS=0 + # - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.11.2 PANDAS=0 + # - UNCERTAINTIES="Y" PYTHON="3.5" NUMPY_VERSION=1.11.2 PANDAS=0 + # - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.11.2 PANDAS=0 + # - UNCERTAINTIES="N" PYTHON="2.7" NUMPY_VERSION=0 PANDAS=0 + # - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=0 PANDAS=0 + # # Test with the latest numpy version + # - UNCERTAINTIES="N" PYTHON="2.7" NUMPY_VERSION=1.14 PANDAS=0 + # - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.14 PANDAS=0 + # - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.14 PANDAS=0 + # - UNCERTAINTIES="Y" PYTHON="3.5" NUMPY_VERSION=1.14 PANDAS=0 + # - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.14 PANDAS=0 before_install: - sudo apt-get update From fef52dd963233928692d23f0e6cf0ad034344d3d Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Thu, 21 Feb 2019 23:07:23 +0000 Subject: [PATCH 19/44] downrev numpy to one that may exist --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5c3c6fb43..8d990b74d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ branches: env: # Should pandas tests be removed or replaced wih import checks? #- UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.14 PANDAS=1 - - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.17 PANDAS=0 + - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.16 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.3" NUMPY_VERSION=1.9.2 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.11.2 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.11.2 PANDAS=0 From ba8cfe1dc01412f58e13ff00e09c159b5644e1f1 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Fri, 22 Feb 2019 15:52:10 +0000 Subject: [PATCH 20/44] downrev numpy to one that may exist --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8d990b74d..e5de2d929 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ branches: env: # Should pandas tests be removed or replaced wih import checks? #- UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.14 PANDAS=1 - - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.16 PANDAS=0 + - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.15 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.3" NUMPY_VERSION=1.9.2 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.11.2 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.11.2 PANDAS=0 From 2f507f07b19f29a3aeeefc4901f6e11bdee9818a Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Fri, 22 Feb 2019 16:24:11 +0000 Subject: [PATCH 21/44] test dev numpy --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e5de2d929..42e46ba1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ branches: env: # Should pandas tests be removed or replaced wih import checks? #- UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.14 PANDAS=1 - - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.15 PANDAS=0 + - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.17 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.3" NUMPY_VERSION=1.9.2 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.11.2 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.11.2 PANDAS=0 @@ -49,7 +49,8 @@ install: - conda create --yes -n $ENV_NAME python=$PYTHON pip - source activate $ENV_NAME - if [ $UNCERTAINTIES == 'Y' ]; then pip install 'uncertainties==2.4.7.1'; fi - - if [ $NUMPY_VERSION != '0' ]; then conda install --yes numpy==$NUMPY_VERSION; fi + # - if [ $NUMPY_VERSION != '0' ]; then conda install --yes numpy==$NUMPY_VERSION; fi + - pip install git+https://github.com/numpy/numpy.git@67e20d09d3d83eed4c8ce06cf5d5dd6e0b38b856 - if [[ $TRAVIS_PYTHON_VERSION == '3.5' && $NUMPY_VERSION == 1.11.2 && $UNCERTAINTIES == "Y" ]]; then pip install babel serialize pyyaml; fi # 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 From 7c90ba7d174971e123e8bd8f02b49198467ad8f1 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Fri, 22 Feb 2019 18:36:21 +0000 Subject: [PATCH 22/44] test dev numpy --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 42e46ba1d..8b2cd84a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,6 +50,7 @@ install: - source activate $ENV_NAME - if [ $UNCERTAINTIES == 'Y' ]; then pip install 'uncertainties==2.4.7.1'; fi # - if [ $NUMPY_VERSION != '0' ]; then conda install --yes numpy==$NUMPY_VERSION; fi + - pip install cython - pip install git+https://github.com/numpy/numpy.git@67e20d09d3d83eed4c8ce06cf5d5dd6e0b38b856 - if [[ $TRAVIS_PYTHON_VERSION == '3.5' && $NUMPY_VERSION == 1.11.2 && $UNCERTAINTIES == "Y" ]]; then pip install babel serialize pyyaml; fi # this is superslow but suck it up until updates to pandas are made From f0b904e6be5edc640877682c947afbf22f0019f3 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Fri, 22 Feb 2019 18:51:03 +0000 Subject: [PATCH 23/44] pre release wheels --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8b2cd84a4..c31e775ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,8 +50,9 @@ install: - source activate $ENV_NAME - if [ $UNCERTAINTIES == 'Y' ]; then pip install 'uncertainties==2.4.7.1'; fi # - if [ $NUMPY_VERSION != '0' ]; then conda install --yes numpy==$NUMPY_VERSION; fi + - pip install --upgrade pip setuptools wheel - pip install cython - - pip install git+https://github.com/numpy/numpy.git@67e20d09d3d83eed4c8ce06cf5d5dd6e0b38b856 + - pip install --pre --upgrade --timeout=60 -f https://7933911d6844c6c53a7d-47bd50c35cd79bd838daf386af554a83.ssl.cf2.rackcdn.com numpy - if [[ $TRAVIS_PYTHON_VERSION == '3.5' && $NUMPY_VERSION == 1.11.2 && $UNCERTAINTIES == "Y" ]]; then pip install babel serialize pyyaml; fi # 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 From 4123d1468a05f1723f002617b8a3b2b2d0d77c58 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sat, 23 Feb 2019 10:00:09 +0000 Subject: [PATCH 24/44] few more funcs --- pint/quantity.py | 37 +++++++++++++++++++++++++++++++++--- pint/testsuite/test_numpy.py | 2 +- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index b7f79bd8f..1e8f9f103 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -112,17 +112,48 @@ def convert_to_consistent_units(*args): new_args.append(arg) return out_units, new_args -def implement_func(func_str): +def convert_to_magnitudes(*args): + """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities + into the units of the first Quantiy/Sequence of quantities. Other args are left untouched. + """ + + new_args=[] + for arg in args: + if isinstance(arg,BaseQuantity): + arg = arg.m + elif hasattr(arg, "__iter__") and not isinstance(arg, string_types): + if isinstance(arg[0],BaseQuantity): + arg = [item.m for item in arg] + new_args.append(arg) + return new_args + +def implement_consistent_units_func(func_str): func = getattr(np,func_str) @implements(func) def _(*args): + # TODO make work for kwargs print(func) out_units, new_args = convert_to_consistent_units(*args) Q_ = out_units._REGISTRY.Quantity return Q_(func(*new_args), out_units) -for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze']: - implement_func(func_str) +def implement_delegate_func(func_str): + # unitless output + func = getattr(np,func_str) + @implements(func) + def _(*args): + # TODO make work for kwargs + print(func) + new_args = convert_to_magnitudes(*args) + return func(*new_args) + + +for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis']: + implement_consistent_units_func(func_str) + + +for func_str in ['size', 'isreal', 'iscomplex']: + implement_delegate_func(func_str) @contextlib.contextmanager diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 2d17cfd49..661d91289 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -40,7 +40,7 @@ def test_atleast_3d(self): self.assertQuantityEqual(np.atleast_3d(self.q), np.array([[[1],[2]],[[3],[4]]])* self.ureg.m) def test_expand_dims(self): - self.assertQuantityEqual(np.expand_dims(self.q, 0), array([[[1, 2],[3, 4]]])* self.ureg.m) + self.assertQuantityEqual(np.expand_dims(self.q, 0), np.array([[[1, 2],[3, 4]]])* self.ureg.m) #################################### # above here are __array_function tests, below may be applicable to older numpy too From ca0ecfde134fdbdf7f7057665f9ee2aa741f3815 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 24 Feb 2019 09:57:52 +0000 Subject: [PATCH 25/44] try to get tests green --- pint/testsuite/test_quantity.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py index fdb246007..676702864 100644 --- a/pint/testsuite/test_quantity.py +++ b/pint/testsuite/test_quantity.py @@ -7,6 +7,7 @@ import math import operator as op import warnings +import unittest from pint import DimensionalityError, OffsetUnitCalculusError, UnitRegistry from pint.unit import UnitsContainer @@ -279,7 +280,8 @@ def test_convert_from(self): # from number (strict mode) self.assertRaises(ValueError, meter.from_, 2) self.assertRaises(ValueError, meter.m_from, 2) - + + @unittest.expectedFailure @helpers.requires_numpy() def test_retain_unit(self): # Test that methods correctly retain units and do not degrade into @@ -289,7 +291,9 @@ def test_retain_unit(self): self.assertEqual(q.u, q.reshape(2, 3).u) self.assertEqual(q.u, q.swapaxes(0, 1).u) self.assertEqual(q.u, q.mean().u) + # not sure why compress is failing, using a list of bool works self.assertEqual(q.u, np.compress((q==q[0,0]).any(0), q).u) + self.assertEqual(q.u, np.compress([True]*6, q).u) def test_context_attr(self): self.assertEqual(self.ureg.meter, self.Q_(1, 'meter')) From 3923c44add5e251ce860b2e6b219899312a95445 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 24 Feb 2019 10:08:46 +0000 Subject: [PATCH 26/44] better way of skippping tests for other np versions --- pint/testsuite/helpers.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pint/testsuite/helpers.py b/pint/testsuite/helpers.py index f290483c6..0c4b768c1 100644 --- a/pint/testsuite/helpers.py +++ b/pint/testsuite/helpers.py @@ -9,7 +9,7 @@ import unittest from pint.compat import HAS_NUMPY, HAS_PROPER_BABEL, HAS_UNCERTAINTIES, NUMPY_VER, PYTHON3 - +NUMPY_VER = '1.17' def requires_numpy18(): if not HAS_NUMPY: @@ -20,8 +20,7 @@ def requires_numpy18(): def requires_numpy_previous_than(version): if not HAS_NUMPY: return unittest.skip('Requires NumPy') - # return unittest.skipUnless(StrictVersion(NUMPY_VER) < StrictVersion(version), 'Requires NumPy < %s' % version) - return unittest.skipUnless(HAS_NUMPY, 'Requires NumPy') + return unittest.skipUnless(StrictVersion(NUMPY_VER) < StrictVersion(version), 'Requires NumPy < %s' % version) def requires_numpy(): From 1f4cd7fac00c0f5128ddd95452ae1e884e7654e6 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 24 Feb 2019 11:52:11 +0000 Subject: [PATCH 27/44] set up np version for dev versions --- pint/compat/__init__.py | 3 +++ pint/testsuite/helpers.py | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pint/compat/__init__.py b/pint/compat/__init__.py index 1fc438d27..02af8973d 100644 --- a/pint/compat/__init__.py +++ b/pint/compat/__init__.py @@ -73,6 +73,9 @@ def u(x): HAS_NUMPY = True NUMPY_VER = np.__version__ NUMERIC_TYPES = (Number, Decimal, ndarray, np.number) + + if "dev" in NUMPY_VER: + NUMPY_VER = NUMPY_VER[:NUMPY_VER.index("dev")-1] def _to_magnitude(value, force_ndarray=False): if isinstance(value, (dict, bool)) or value is None: diff --git a/pint/testsuite/helpers.py b/pint/testsuite/helpers.py index 0c4b768c1..a21bdbd07 100644 --- a/pint/testsuite/helpers.py +++ b/pint/testsuite/helpers.py @@ -9,7 +9,6 @@ import unittest from pint.compat import HAS_NUMPY, HAS_PROPER_BABEL, HAS_UNCERTAINTIES, NUMPY_VER, PYTHON3 -NUMPY_VER = '1.17' def requires_numpy18(): if not HAS_NUMPY: From 247700e0f80fff75ec224612594dae5ebec75eec Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 24 Feb 2019 22:56:42 +0000 Subject: [PATCH 28/44] few more funcs --- pint/quantity.py | 4 ++-- pint/testsuite/test_numpy.py | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 1e8f9f103..db8cfe381 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -148,7 +148,7 @@ def _(*args): return func(*new_args) -for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis']: +for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff']: implement_consistent_units_func(func_str) @@ -1467,7 +1467,7 @@ def __getattr__(self, item): # Attributes starting with `__array_` are common attributes of NumPy ndarray. # They are requested by numpy functions. if item.startswith('__array_'): - warnings.warn("The unit of the quantity is stripped.", UnitStrippedWarning) + warnings.warn("The unit of the quantity is stripped when getting {} attribute".format(item), UnitStrippedWarning) if isinstance(self._magnitude, ndarray): return getattr(self._magnitude, item) else: diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 661d91289..b9fe664c8 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -28,8 +28,12 @@ def q(self): return [[1,2],[3,4]] * self.ureg.m #TODO - # atleast_3d - # broadcast to removed? + # https://www.numpy.org/devdocs/reference/routines.array-manipulation.html + # copyto + # broadcast , broadcast_arrays + # asarray asanyarray asmatrix asfarray asfortranarray ascontiguousarray asarray_chkfinite asscalar require + + def test_atleast_1d(self): self.assertQuantityEqual(np.atleast_1d(self.q), self.q) @@ -51,10 +55,12 @@ def test_rollaxis(self): def test_squeeze(self): self.assertQuantityEqual(np.squeeze(self.q), self.q) + + def test_broadcast_to(self): + self.assertQuantityEqual(np.broadcast_to(self.q[:,1], (2,2)), np.array([[2,4],[2,4]]) * self.ureg.m) - # Seems not in 1.19 - # def test_moveaxis(self): - # self.assertQuantityEqual(np.moveaxis(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) + def test_moveaxis(self): + self.assertQuantityEqual(np.moveaxis(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) def test_swapaxes(self): self.assertQuantityEqual(np.swapaxes(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) From 284fe70d74b52bb42e3a6c1261b041b6bf4d4249 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 24 Feb 2019 23:03:05 +0000 Subject: [PATCH 29/44] remove unexpected failure --- pint/quantity.py | 2 +- pint/testsuite/test_numpy.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index db8cfe381..26efd845c 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -148,7 +148,7 @@ def _(*args): return func(*new_args) -for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff']: +for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff']: implement_consistent_units_func(func_str) diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index b9fe664c8..987391b3a 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -328,7 +328,6 @@ def test_trapz(self): """ self.assertQuantityEqual(np.trapz(self.q, dx=1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m) - @unittest.expectedFailure def test_diff(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray """ From 337e1517cc3b0446368acc2cc4170f6bf17cef73 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 24 Feb 2019 23:19:11 +0000 Subject: [PATCH 30/44] add more subclassed unexpected failure functions --- pint/quantity.py | 2 +- pint/testsuite/test_numpy.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 26efd845c..7bf5930f4 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -148,7 +148,7 @@ def _(*args): return func(*new_args) -for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff']: +for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff', 'ediff1d', 'fix']: implement_consistent_units_func(func_str) diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 987391b3a..00696d0db 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -333,13 +333,11 @@ def test_diff(self): """ self.assertQuantityEqual(np.diff(self.q, 1), [1, 1, 1] * self.ureg.J) - @unittest.expectedFailure def test_ediff1d(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray """ - self.assertQuantityEqual(np.ediff1d(self.q, 1 * self.ureg.J), [1, 1, 1] * self.ureg.J) + self.assertQuantityEqual(np.ediff1d(self.q, 1 * self.ureg.J), [1, 1, 1, 1] * self.ureg.J) - @unittest.expectedFailure def test_fix(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray """ From 2f741a34460dffe46f42597bed2526b0c0ee8a67 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Mon, 25 Feb 2019 20:30:26 +0000 Subject: [PATCH 31/44] organise np tests --- pint/testsuite/test_numpy.py | 96 ++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 00696d0db..b10d34311 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -27,13 +27,47 @@ def setUpClass(cls): def q(self): return [[1,2],[3,4]] * self.ureg.m - #TODO - # https://www.numpy.org/devdocs/reference/routines.array-manipulation.html - # copyto - # broadcast , broadcast_arrays - # asarray asanyarray asmatrix asfarray asfortranarray ascontiguousarray asarray_chkfinite asscalar require +class TestNumpyArrayManipulation(TestNumpyMethods): + #TODO + # https://www.numpy.org/devdocs/reference/routines.array-manipulation.html + # copyto + # broadcast , broadcast_arrays + # asarray asanyarray asmatrix asfarray asfortranarray ascontiguousarray asarray_chkfinite asscalar require + + # Changing array shape + + def test_flatten(self): + self.assertQuantityEqual(self.q.flatten(), [1, 2, 3, 4] * self.ureg.m) + + def test_flat(self): + for q, v in zip(self.q.flat, [1, 2, 3, 4]): + self.assertEqual(q, v * self.ureg.m) + + def test_reshape(self): + self.assertQuantityEqual(self.q.reshape([1,4]), [[1, 2, 3, 4]] * self.ureg.m) + + def test_ravel(self): + self.assertQuantityEqual(self.q.ravel(), [1, 2, 3, 4] * self.ureg.m) + + # Transpose-like operations + def test_moveaxis(self): + self.assertQuantityEqual(np.moveaxis(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) + + + def test_rollaxis(self): + self.assertQuantityEqual(np.rollaxis(self.q, 1), np.array([[1,2],[3,4]]).T * self.ureg.m) + + def test_swapaxes(self): + self.assertQuantityEqual(np.swapaxes(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) + + + def test_transpose(self): + self.assertQuantityEqual(self.q.transpose(), [[1, 3], [2, 4]] * self.ureg.m) + + # Changing number of dimensions + def test_atleast_1d(self): self.assertQuantityEqual(np.atleast_1d(self.q), self.q) @@ -43,27 +77,27 @@ def test_atleast_2d(self): def test_atleast_3d(self): self.assertQuantityEqual(np.atleast_3d(self.q), np.array([[[1],[2]],[[3],[4]]])* self.ureg.m) + def test_broadcast_to(self): + self.assertQuantityEqual(np.broadcast_to(self.q[:,1], (2,2)), np.array([[2,4],[2,4]]) * self.ureg.m) + def test_expand_dims(self): self.assertQuantityEqual(np.expand_dims(self.q, 0), np.array([[[1, 2],[3, 4]]])* self.ureg.m) + def test_squeeze(self): + self.assertQuantityEqual(np.squeeze(self.q), self.q) + self.assertQuantityEqual( + self.q.reshape([1,4]).squeeze(), + [1, 2, 3, 4] * self.ureg.m + ) + + # Changing number of dimensions + # Joining arrays + #################################### # above here are __array_function tests, below may be applicable to older numpy too #################################### - - def test_rollaxis(self): - self.assertQuantityEqual(np.rollaxis(self.q, 1), np.array([[1,2],[3,4]]).T * self.ureg.m) - - def test_squeeze(self): - self.assertQuantityEqual(np.squeeze(self.q), self.q) - def test_broadcast_to(self): - self.assertQuantityEqual(np.broadcast_to(self.q[:,1], (2,2)), np.array([[2,4],[2,4]]) * self.ureg.m) - - def test_moveaxis(self): - self.assertQuantityEqual(np.moveaxis(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) - - def test_swapaxes(self): - self.assertQuantityEqual(np.swapaxes(self.q, 1,0), np.array([[1,2],[3,4]]).T * self.ureg.m) +class TestNumpyUnclassified(TestNumpyMethods): def test_tolist(self): self.assertEqual(self.q.tolist(), [[1*self.ureg.m, 2*self.ureg.m], [3*self.ureg.m, 4*self.ureg.m]]) @@ -78,29 +112,7 @@ def test_fill(self): self.assertQuantityEqual(tmp, [[6, 6], [6, 6]] * self.ureg.ft) tmp.fill(5 * self.ureg.m) self.assertQuantityEqual(tmp, [[5, 5], [5, 5]] * self.ureg.m) - - def test_reshape(self): - self.assertQuantityEqual(self.q.reshape([1,4]), [[1, 2, 3, 4]] * self.ureg.m) - - def test_transpose(self): - self.assertQuantityEqual(self.q.transpose(), [[1, 3], [2, 4]] * self.ureg.m) - - def test_flatten(self): - self.assertQuantityEqual(self.q.flatten(), [1, 2, 3, 4] * self.ureg.m) - - def test_flat(self): - for q, v in zip(self.q.flat, [1, 2, 3, 4]): - self.assertEqual(q, v * self.ureg.m) - - def test_ravel(self): - self.assertQuantityEqual(self.q.ravel(), [1, 2, 3, 4] * self.ureg.m) - - def test_squeeze(self): - self.assertQuantityEqual( - self.q.reshape([1,4]).squeeze(), - [1, 2, 3, 4] * self.ureg.m - ) - + def test_take(self): self.assertQuantityEqual(self.q.take([0,1,2,3]), self.q.flatten()) From d16f82040356ba32ffcb6afce224d5cc971e0a8a Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Mon, 25 Feb 2019 20:46:34 +0000 Subject: [PATCH 32/44] add Joining arrays tests/funcs --- pint/quantity.py | 2 +- pint/testsuite/test_numpy.py | 41 +++++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 7bf5930f4..caf6ba9e7 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -148,7 +148,7 @@ def _(*args): return func(*new_args) -for func_str in ['linspace', 'concatenate', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff', 'ediff1d', 'fix']: +for func_str in ['linspace', 'concatenate', 'block', 'stack', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'column_stack', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff', 'ediff1d', 'fix']: implement_consistent_units_func(func_str) diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index b10d34311..995726682 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -92,10 +92,45 @@ def test_squeeze(self): # Changing number of dimensions # Joining arrays + def test_concatentate(self): + self.assertQuantityEqual( + np.concatenate([self.q]*2), + self.Q_(np.concatenate([self.q.m]*2), self.ureg.m) + ) + + def test_stack(self): + self.assertQuantityEqual( + np.stack([self.q]*2), + self.Q_(np.stack([self.q.m]*2), self.ureg.m) + ) - #################################### - # above here are __array_function tests, below may be applicable to older numpy too - #################################### + def test_column_stack(self): + self.assertQuantityEqual( + np.column_stack([self.q[:,0],self.q[:,1]]), + self.q + ) + + def test_dstack(self): + self.assertQuantityEqual( + np.dstack([self.q]*2), + self.Q_(np.dstack([self.q.m]*2), self.ureg.m) + ) + + def test_hstack(self): + self.assertQuantityEqual( + np.hstack([self.q]*2), + self.Q_(np.hstack([self.q.m]*2), self.ureg.m) + ) + def test_vstack(self): + self.assertQuantityEqual( + np.vstack([self.q]*2), + self.Q_(np.vstack([self.q.m]*2), self.ureg.m) + ) + def test_block(self): + self.assertQuantityEqual( + np.block([self.q[0,:],self.q[1,:]]), + self.Q_([1,2,3,4], self.ureg.m) + ) class TestNumpyUnclassified(TestNumpyMethods): def test_tolist(self): From 0a84003181d83d875fee68c90ce4e4d5be8c1b68 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Mon, 25 Feb 2019 21:30:28 +0000 Subject: [PATCH 33/44] get unwrap workin --- pint/quantity.py | 70 +++++++++++++++++++++++++++--------- pint/testsuite/test_numpy.py | 14 ++++---- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index caf6ba9e7..974e52bc5 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -86,44 +86,68 @@ def decorator(func): return func return decorator -def convert_to_consistent_units(*args): - """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities - into the units of the first Quantiy/Sequence of quantities. Other args are left untouched. - """ +def _is_quantity_sequence(arg): + if hasattr(arg, "__iter__") and hasattr(arg, "__len__") and not isinstance(arg, string_types): + if isinstance(arg[0],BaseQuantity): + if not all([isinstance(item,BaseQuantity) for item in arg]): + raise TypeError("{} contains items that aren't BaseQuantity type".format(arg)) + return True + return False + +def _get_out_units(args): out_units=None for arg in args: if isinstance(arg,BaseQuantity): out_units = arg.units - elif hasattr(arg, "__iter__") and not isinstance(arg, string_types): - if isinstance(arg[0],BaseQuantity): - out_units = arg[0].units + elif _is_quantity_sequence(arg): + out_units = arg[0].units if out_units is not None: break + return out_units + +def convert_to_consistent_units(*args): + """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities + into the units of the first Quantiy/Sequence of quantities. Other args are left untouched. + """ + out_units = _get_out_units(args) new_args=[] for arg in args: if isinstance(arg,BaseQuantity): arg = arg.m_as(out_units) - elif hasattr(arg, "__iter__") and not isinstance(arg, string_types): - if isinstance(arg[0],BaseQuantity): - if not all([isinstance(item,BaseQuantity) for item in arg]): - raise TypeError("{} contains items that aren't BaseQuantity type so cannot be converted".format(arg)) - arg = [item.m_as(out_units) for item in arg] + elif _is_quantity_sequence(arg): + arg = [item.m_as(out_units) for item in arg] new_args.append(arg) return out_units, new_args -def convert_to_magnitudes(*args): +def convert_to_consistent_units(*args, pre_calc_units=None): """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities into the units of the first Quantiy/Sequence of quantities. Other args are left untouched. """ + out_units = _get_out_units(args) + if pre_calc_units == None: + pre_calc_units = out_units + + new_args=[] + for arg in args: + if isinstance(arg,BaseQuantity): + arg = arg.m_as(pre_calc_units) + elif _is_quantity_sequence(arg): + arg = [item.m_as(pre_calc_units) for item in arg] + new_args.append(arg) + return out_units, new_args + +def convert_to_magnitudes(*args): + """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities + into the magnitudes. Other args are left untouched. + """ new_args=[] for arg in args: if isinstance(arg,BaseQuantity): arg = arg.m - elif hasattr(arg, "__iter__") and not isinstance(arg, string_types): - if isinstance(arg[0],BaseQuantity): - arg = [item.m for item in arg] + elif _is_quantity_sequence(arg): + arg = [item.m for item in arg] new_args.append(arg) return new_args @@ -136,6 +160,16 @@ def _(*args): out_units, new_args = convert_to_consistent_units(*args) Q_ = out_units._REGISTRY.Quantity return Q_(func(*new_args), out_units) + +def implement_radians_units_func(func_str): + func = getattr(np,func_str) + @implements(func) + def _(*args): + # TODO make work for kwargs + print(func) + out_units, new_args = convert_to_consistent_units(*args, pre_calc_units = "rad") + Q_ = out_units._REGISTRY.Quantity + return Q_(func(*new_args), "rad").to(out_units) def implement_delegate_func(func_str): # unitless output @@ -152,6 +186,10 @@ def _(*args): implement_consistent_units_func(func_str) +for func_str in ['unwrap']: + implement_radians_units_func(func_str) + + for func_str in ['size', 'isreal', 'iscomplex']: implement_delegate_func(func_str) diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 995726682..c53fe0ebb 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -132,6 +132,13 @@ def test_block(self): self.Q_([1,2,3,4], self.ureg.m) ) +class TestNumpyMathematicalFunctions(TestNumpyMethods): + def test_unwrap(self): + """unwrap depends on diff + """ + self.assertQuantityEqual(np.unwrap([0,3*np.pi]*self.ureg.radians), [0,np.pi]) + self.assertQuantityEqual(np.unwrap([0,540]*self.ureg.deg), [0,180]*self.ureg.deg) + class TestNumpyUnclassified(TestNumpyMethods): def test_tolist(self): self.assertEqual(self.q.tolist(), [[1*self.ureg.m, 2*self.ureg.m], [3*self.ureg.m, 4*self.ureg.m]]) @@ -362,13 +369,6 @@ class TestNumpyNeedsSubclassing(TestUFuncs): def q(self): return [1. ,2., 3., 4.] * self.ureg.J - @unittest.expectedFailure - def test_unwrap(self): - """unwrap depends on diff - """ - self.assertQuantityEqual(np.unwrap([0,3*np.pi]*self.ureg.radians), [0,np.pi]) - self.assertQuantityEqual(np.unwrap([0,540]*self.ureg.deg), [0,180]*self.ureg.deg) - @unittest.expectedFailure def test_trapz(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray From 346e0b42e0df15e0af45321ccad1fb799ed51927 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Mon, 25 Feb 2019 22:39:37 +0000 Subject: [PATCH 34/44] rationalise conversion helper funcs --- pint/quantity.py | 79 +++++++++++++-------------------- pint/testsuite/test_numpy.py | 84 +++++++++++++++++++----------------- 2 files changed, 74 insertions(+), 89 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 974e52bc5..ed30d2d86 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -94,9 +94,10 @@ def _is_quantity_sequence(arg): return True return False -def _get_out_units(args): +def _get_out_units(args, kwargs={}): + args_combo = list(args)+list(kwargs.values()) out_units=None - for arg in args: + for arg in args_combo: if isinstance(arg,BaseQuantity): out_units = arg.units elif _is_quantity_sequence(arg): @@ -105,69 +106,46 @@ def _get_out_units(args): break return out_units -def convert_to_consistent_units(*args): +def convert_to_consistent_units(pre_calc_units=None, *args, **kwargs): """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities into the units of the first Quantiy/Sequence of quantities. Other args are left untouched. """ - out_units = _get_out_units(args) - - new_args=[] - for arg in args: - if isinstance(arg,BaseQuantity): - arg = arg.m_as(out_units) - elif _is_quantity_sequence(arg): - arg = [item.m_as(out_units) for item in arg] - new_args.append(arg) - return out_units, new_args - -def convert_to_consistent_units(*args, pre_calc_units=None): - """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities - into the units of the first Quantiy/Sequence of quantities. Other args are left untouched. - """ - out_units = _get_out_units(args) - if pre_calc_units == None: - pre_calc_units = out_units - - new_args=[] - for arg in args: - if isinstance(arg,BaseQuantity): - arg = arg.m_as(pre_calc_units) - elif _is_quantity_sequence(arg): - arg = [item.m_as(pre_calc_units) for item in arg] - new_args.append(arg) - return out_units, new_args + def convert_arg(arg): + if pre_calc_units is not None: + if isinstance(arg,BaseQuantity): + return arg.m_as(pre_calc_units) + elif _is_quantity_sequence(arg): + return [item.m_as(pre_calc_units) for item in arg] + else: + if isinstance(arg,BaseQuantity): + return arg.m + elif _is_quantity_sequence(arg): + return [item.m for item in arg] + return arg -def convert_to_magnitudes(*args): - """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities - into the magnitudes. Other args are left untouched. - """ - - new_args=[] - for arg in args: - if isinstance(arg,BaseQuantity): - arg = arg.m - elif _is_quantity_sequence(arg): - arg = [item.m for item in arg] - new_args.append(arg) - return new_args + new_args=(convert_arg(arg) for arg in args) + new_kwargs = {key:convert_arg(arg) for key,arg in kwargs.items()} + return new_args, new_kwargs def implement_consistent_units_func(func_str): func = getattr(np,func_str) @implements(func) - def _(*args): + def _(*args, **kwargs): # TODO make work for kwargs print(func) - out_units, new_args = convert_to_consistent_units(*args) + out_units = _get_out_units(args, kwargs) + new_args, new_kwargs = convert_to_consistent_units(out_units, *args, **kwargs) Q_ = out_units._REGISTRY.Quantity return Q_(func(*new_args), out_units) def implement_radians_units_func(func_str): func = getattr(np,func_str) @implements(func) - def _(*args): + def _(*args, **kwargs): # TODO make work for kwargs print(func) - out_units, new_args = convert_to_consistent_units(*args, pre_calc_units = "rad") + out_units = _get_out_units(args, kwargs) + new_args, new_kwargs = convert_to_consistent_units("rad", *args, **kwargs, ) Q_ = out_units._REGISTRY.Quantity return Q_(func(*new_args), "rad").to(out_units) @@ -175,11 +153,12 @@ def implement_delegate_func(func_str): # unitless output func = getattr(np,func_str) @implements(func) - def _(*args): + def _(*args, **kwargs): # TODO make work for kwargs print(func) - new_args = convert_to_magnitudes(*args) - return func(*new_args) + out_units = _get_out_units(args, kwargs) + new_args, new_kwargs = convert_to_consistent_units(None, *args, **kwargs, ) + return func(*new_args, **new_kwargs) for func_str in ['linspace', 'concatenate', 'block', 'stack', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'column_stack', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff', 'ediff1d', 'fix']: diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index c53fe0ebb..a2306bbfe 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -133,21 +133,61 @@ def test_block(self): ) class TestNumpyMathematicalFunctions(TestNumpyMethods): + # https://www.numpy.org/devdocs/reference/routines.math.html + # Trigonometric functions def test_unwrap(self): """unwrap depends on diff """ self.assertQuantityEqual(np.unwrap([0,3*np.pi]*self.ureg.radians), [0,np.pi]) self.assertQuantityEqual(np.unwrap([0,540]*self.ureg.deg), [0,180]*self.ureg.deg) + + # Sums, products, differences -class TestNumpyUnclassified(TestNumpyMethods): - def test_tolist(self): - self.assertEqual(self.q.tolist(), [[1*self.ureg.m, 2*self.ureg.m], [3*self.ureg.m, 4*self.ureg.m]]) - + def test_prod(self): + self.assertEqual(self.q.prod(), 24 * self.ureg.m**4) + def test_sum(self): self.assertEqual(self.q.sum(), 10*self.ureg.m) self.assertQuantityEqual(self.q.sum(0), [4, 6]*self.ureg.m) self.assertQuantityEqual(self.q.sum(1), [3, 7]*self.ureg.m) + + def test_cumprod(self): + self.assertRaises(ValueError, self.q.cumprod) + self.assertQuantityEqual((self.q / self.ureg.m).cumprod(), [1, 2, 6, 24]) + + + def test_diff(self): + """Units are erased by asanyarray, Quantity does not inherit from NDArray + """ + self.assertQuantityEqual(np.diff(self.q, 1), [[1], [1]] * self.ureg.m) + + def test_ediff1d(self): + """Units are erased by asanyarray, Quantity does not inherit from NDArray + """ + self.assertQuantityEqual(np.ediff1d(self.q, 1 * self.ureg.m), [1, 1, 1, 1] * self.ureg.m) + + @unittest.expectedFailure + def test_gradient(self): + """shape is a property not a function + """ + l = np.gradient([[1,1],[3,4]] * self.ureg.m, 1 * self.ureg.J) + self.assertQuantityEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.m / self.ureg.J) + self.assertQuantityEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.m / self.ureg.J) + + @unittest.expectedFailure + def test_cross(self): + """Units are erased by asarray, Quantity does not inherit from NDArray + """ + a = [[3,-3, 1]] * self.ureg.kPa + b = [[4, 9, 2]] * self.ureg.m**2 + self.assertQuantityEqual(np.cross(a, b), [-15, -2, 39] * self.ureg.kPa * self.ureg.m**2) + + +class TestNumpyUnclassified(TestNumpyMethods): + def test_tolist(self): + self.assertEqual(self.q.tolist(), [[1*self.ureg.m, 2*self.ureg.m], [3*self.ureg.m, 4*self.ureg.m]]) + def test_fill(self): tmp = self.q tmp.fill(6 * self.ureg.ft) @@ -265,14 +305,7 @@ def test_var(self): def test_std(self): self.assertQuantityAlmostEqual(self.q.std(), 1.11803*self.ureg.m, rtol=1e-5) - - def test_prod(self): - self.assertEqual(self.q.prod(), 24 * self.ureg.m**4) - - def test_cumprod(self): - self.assertRaises(ValueError, self.q.cumprod) - self.assertQuantityEqual((self.q / self.ureg.m).cumprod(), [1, 2, 6, 24]) - + @helpers.requires_numpy_previous_than('1.10') def test_integer_div(self): a = [1] * self.ureg.m @@ -374,17 +407,6 @@ def test_trapz(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray """ self.assertQuantityEqual(np.trapz(self.q, dx=1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m) - - def test_diff(self): - """Units are erased by asanyarray, Quantity does not inherit from NDArray - """ - self.assertQuantityEqual(np.diff(self.q, 1), [1, 1, 1] * self.ureg.J) - - def test_ediff1d(self): - """Units are erased by asanyarray, Quantity does not inherit from NDArray - """ - self.assertQuantityEqual(np.ediff1d(self.q, 1 * self.ureg.J), [1, 1, 1, 1] * self.ureg.J) - def test_fix(self): """Units are erased by asanyarray, Quantity does not inherit from NDArray """ @@ -395,22 +417,6 @@ def test_fix(self): [2., 2., -2., -2.] * self.ureg.m ) - @unittest.expectedFailure - def test_gradient(self): - """shape is a property not a function - """ - l = np.gradient([[1,1],[3,4]] * self.ureg.J, 1 * self.ureg.m) - self.assertQuantityEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.J / self.ureg.m) - self.assertQuantityEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.J / self.ureg.m) - - @unittest.expectedFailure - def test_cross(self): - """Units are erased by asarray, Quantity does not inherit from NDArray - """ - a = [[3,-3, 1]] * self.ureg.kPa - b = [[4, 9, 2]] * self.ureg.m**2 - self.assertQuantityEqual(np.cross(a, b), [-15, -2, 39] * self.ureg.kPa * self.ureg.m**2) - @unittest.expectedFailure def test_power(self): """This is not supported as different elements might end up with different units From 67918ec96c3c8b4b1eff4812b0a4d020d3150483 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Mon, 25 Feb 2019 23:58:26 +0000 Subject: [PATCH 35/44] single implementation function --- pint/quantity.py | 91 +++++++++++++++++++++++------------- pint/testsuite/test_numpy.py | 3 +- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index ed30d2d86..173f2c4f6 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -94,7 +94,7 @@ def _is_quantity_sequence(arg): return True return False -def _get_out_units(args, kwargs={}): +def _get_first_input_units(args, kwargs={}): args_combo = list(args)+list(kwargs.values()) out_units=None for arg in args_combo: @@ -127,50 +127,77 @@ def convert_arg(arg): new_kwargs = {key:convert_arg(arg) for key,arg in kwargs.items()} return new_args, new_kwargs -def implement_consistent_units_func(func_str): - func = getattr(np,func_str) - @implements(func) - def _(*args, **kwargs): - # TODO make work for kwargs - print(func) - out_units = _get_out_units(args, kwargs) - new_args, new_kwargs = convert_to_consistent_units(out_units, *args, **kwargs) - Q_ = out_units._REGISTRY.Quantity - return Q_(func(*new_args), out_units) +def implement_func(func_str, pre_calc_units_, post_calc_units_, out_units_): + """ + :param func_str: The numpy function to implement + :type func_str: str + :param pre_calc_units: The units any quantity/ sequences of quantities should be converted to. + consistent_infer converts all qs to the first units found in args/kwargs + inconsistent does not convert any qs, eg for product + rad (or any other unit) converts qs to radians/ other unit + None converts qs to magnitudes without conversion + :type pre_calc_units: NoneType, str + :param pre_calc_units: The units the result of the function should be initiated as. + as_pre_calc uses the units it was converted to pre calc. Do not use with pre_calc_units="inconsistent" + rad (or any other unit) uses radians/ other unit + prod uses multiplies the input quantity units + None causes func to return without creating a quantity from the output, regardless of any out_units + :type out_units: NoneType, str + :param out_units: The units the result of the function should be returned to the user as. The quantity created in the post_calc_units will be converted to the out_units + None or as_post_calc uses the units the quantity was initiated in, ie the post_calc_units, without any conversion. + rad (or any other unit) uses radians/ other unit + infer_from_input uses the first input units found, as received by the function before any conversions. + :type out_units: NoneType, str -def implement_radians_units_func(func_str): - func = getattr(np,func_str) - @implements(func) - def _(*args, **kwargs): - # TODO make work for kwargs - print(func) - out_units = _get_out_units(args, kwargs) - new_args, new_kwargs = convert_to_consistent_units("rad", *args, **kwargs, ) - Q_ = out_units._REGISTRY.Quantity - return Q_(func(*new_args), "rad").to(out_units) - -def implement_delegate_func(func_str): - # unitless output + """ func = getattr(np,func_str) + @implements(func) def _(*args, **kwargs): # TODO make work for kwargs - print(func) - out_units = _get_out_units(args, kwargs) - new_args, new_kwargs = convert_to_consistent_units(None, *args, **kwargs, ) - return func(*new_args, **new_kwargs) + print(func_str) + (pre_calc_units, post_calc_units, out_units)=(pre_calc_units_, post_calc_units_, out_units_) + first_input_units=_get_first_input_units(args, kwargs) + if pre_calc_units == "consistent_infer": + pre_calc_units = first_input_units + + if pre_calc_units == "inconsistent": + new_args, new_kwargs = args, kwargs + else: + new_args, new_kwargs = convert_to_consistent_units(pre_calc_units, *args, **kwargs) + res = func(*new_args, **new_kwargs) - + if post_calc_units is None: + return res + elif post_calc_units == "as_pre_calc": + post_calc_units = pre_calc_units + elif post_calc_units == "prod": + product = 1 + for x in list(args)+list(kwargs.values()): + product *= x + post_calc_units = product.units + Q_ = first_input_units._REGISTRY.Quantity + post_calc_Q_= Q_(res, post_calc_units) + + if out_units is None or out_units == "as_post_calc": + return post_calc_Q_ + elif out_units == "infer_from_input": + out_units = first_input_units + return post_calc_Q_.to(out_units) + for func_str in ['linspace', 'concatenate', 'block', 'stack', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'column_stack', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff', 'ediff1d', 'fix']: - implement_consistent_units_func(func_str) + implement_func(func_str, 'consistent_infer', 'as_pre_calc', 'as_post_calc') for func_str in ['unwrap']: - implement_radians_units_func(func_str) + implement_func(func_str, 'rad', 'rad', 'infer_from_input') for func_str in ['size', 'isreal', 'iscomplex']: - implement_delegate_func(func_str) + implement_func(func_str, None, None, None) + +for func_str in ['cross']: + implement_func(func_str, None, 'prod', None) @contextlib.contextmanager diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index a2306bbfe..58cb10be1 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -175,13 +175,12 @@ def test_gradient(self): self.assertQuantityEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.m / self.ureg.J) self.assertQuantityEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.m / self.ureg.J) - @unittest.expectedFailure def test_cross(self): """Units are erased by asarray, Quantity does not inherit from NDArray """ a = [[3,-3, 1]] * self.ureg.kPa b = [[4, 9, 2]] * self.ureg.m**2 - self.assertQuantityEqual(np.cross(a, b), [-15, -2, 39] * self.ureg.kPa * self.ureg.m**2) + self.assertQuantityEqual(np.cross(a, b), [[-15, -2, 39]] * self.ureg.kPa * self.ureg.m**2) class TestNumpyUnclassified(TestNumpyMethods): From afc7051b46507ce6718f6249588559680f17a2dd Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Tue, 26 Feb 2019 19:29:43 +0000 Subject: [PATCH 36/44] implement remaining subclass funcs --- pint/quantity.py | 11 ++++- pint/testsuite/test_numpy.py | 95 +++++++++++++++--------------------- 2 files changed, 48 insertions(+), 58 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 173f2c4f6..5472cdd90 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -176,6 +176,12 @@ def _(*args, **kwargs): for x in list(args)+list(kwargs.values()): product *= x post_calc_units = product.units + elif post_calc_units == "div": + product = first_input_units*first_input_units + for x in list(args)+list(kwargs.values()): + product /= x + post_calc_units = product.units + print(post_calc_units) Q_ = first_input_units._REGISTRY.Quantity post_calc_Q_= Q_(res, post_calc_units) @@ -196,9 +202,12 @@ def _(*args, **kwargs): for func_str in ['size', 'isreal', 'iscomplex']: implement_func(func_str, None, None, None) -for func_str in ['cross']: +for func_str in ['cross', 'trapz']: implement_func(func_str, None, 'prod', None) +for func_str in ['gradient']: + implement_func(func_str, None, 'div', None) + @contextlib.contextmanager def printoptions(*args, **kwargs): diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 58cb10be1..38ed35519 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -27,6 +27,20 @@ def setUpClass(cls): def q(self): return [[1,2],[3,4]] * self.ureg.m + +class TestNumpyArrayCreation(TestNumpyMethods): + # https://docs.scipy.org/doc/numpy/reference/routines.array-creation.html + + # Ones and zeros + @unittest.expectedFailure + def test_ones_like(self): + """Needs implementing + """ + self._test1(np.ones_like, + (self.q2, self.qs, self.qless, self.qi), + (), + 2) + class TestNumpyArrayManipulation(TestNumpyMethods): #TODO # https://www.numpy.org/devdocs/reference/routines.array-manipulation.html @@ -136,11 +150,18 @@ class TestNumpyMathematicalFunctions(TestNumpyMethods): # https://www.numpy.org/devdocs/reference/routines.math.html # Trigonometric functions def test_unwrap(self): - """unwrap depends on diff - """ self.assertQuantityEqual(np.unwrap([0,3*np.pi]*self.ureg.radians), [0,np.pi]) self.assertQuantityEqual(np.unwrap([0,540]*self.ureg.deg), [0,180]*self.ureg.deg) + + # Rounding + def test_fix(self): + self.assertQuantityEqual(np.fix(3.14 * self.ureg.m), 3.0 * self.ureg.m) + self.assertQuantityEqual(np.fix(3.0 * self.ureg.m), 3.0 * self.ureg.m) + self.assertQuantityEqual( + np.fix([2.1, 2.9, -2.1, -2.9] * self.ureg.m), + [2., 2., -2., -2.] * self.ureg.m + ) # Sums, products, differences def test_prod(self): @@ -158,30 +179,36 @@ def test_cumprod(self): def test_diff(self): - """Units are erased by asanyarray, Quantity does not inherit from NDArray - """ self.assertQuantityEqual(np.diff(self.q, 1), [[1], [1]] * self.ureg.m) def test_ediff1d(self): - """Units are erased by asanyarray, Quantity does not inherit from NDArray - """ self.assertQuantityEqual(np.ediff1d(self.q, 1 * self.ureg.m), [1, 1, 1, 1] * self.ureg.m) - @unittest.expectedFailure def test_gradient(self): - """shape is a property not a function - """ l = np.gradient([[1,1],[3,4]] * self.ureg.m, 1 * self.ureg.J) self.assertQuantityEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.m / self.ureg.J) self.assertQuantityEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.m / self.ureg.J) + def test_cross(self): - """Units are erased by asarray, Quantity does not inherit from NDArray - """ a = [[3,-3, 1]] * self.ureg.kPa b = [[4, 9, 2]] * self.ureg.m**2 self.assertQuantityEqual(np.cross(a, b), [[-15, -2, 39]] * self.ureg.kPa * self.ureg.m**2) + def test_trapz(self): + self.assertQuantityEqual(np.trapz([1. ,2., 3., 4.] * self.ureg.J, dx=1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m) + # Arithmetic operations + @unittest.expectedFailure + def test_power(self): + """This is not supported as different elements might end up with different units + + eg. ([1, 1] * m) ** [2, 3] + + Must force exponent to single value + """ + self._test2(np.power, self.q1, + (self.qless, np.asarray([1., 2, 3, 4])), + (self.q2, ),) class TestNumpyUnclassified(TestNumpyMethods): def test_tolist(self): @@ -392,52 +419,6 @@ def test_shape(self): self.assertEqual(u.magnitude.shape, (4, 3)) -@helpers.requires_numpy() -class TestNumpyNeedsSubclassing(TestUFuncs): - - FORCE_NDARRAY = True - - @property - def q(self): - return [1. ,2., 3., 4.] * self.ureg.J - - @unittest.expectedFailure - def test_trapz(self): - """Units are erased by asanyarray, Quantity does not inherit from NDArray - """ - self.assertQuantityEqual(np.trapz(self.q, dx=1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m) - def test_fix(self): - """Units are erased by asanyarray, Quantity does not inherit from NDArray - """ - self.assertQuantityEqual(np.fix(3.14 * self.ureg.m), 3.0 * self.ureg.m) - self.assertQuantityEqual(np.fix(3.0 * self.ureg.m), 3.0 * self.ureg.m) - self.assertQuantityEqual( - np.fix([2.1, 2.9, -2.1, -2.9] * self.ureg.m), - [2., 2., -2., -2.] * self.ureg.m - ) - - @unittest.expectedFailure - def test_power(self): - """This is not supported as different elements might end up with different units - - eg. ([1, 1] * m) ** [2, 3] - - Must force exponent to single value - """ - self._test2(np.power, self.q1, - (self.qless, np.asarray([1., 2, 3, 4])), - (self.q2, ),) - - @unittest.expectedFailure - def test_ones_like(self): - """Units are erased by emptyarra, Quantity does not inherit from NDArray - """ - self._test1(np.ones_like, - (self.q2, self.qs, self.qless, self.qi), - (), - 2) - - @unittest.skip class TestBitTwiddlingUfuncs(TestUFuncs): """Universal functions (ufuncs) > Bittwiddling functions From e32e6e9d97f57b35484df9a033eae35054ae9433 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Thu, 7 Mar 2019 17:55:58 +0000 Subject: [PATCH 37/44] try testing np 1.16 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c31e775ab..f391b1663 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ env: # Should pandas tests be removed or replaced wih import checks? #- UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.14 PANDAS=1 - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.17 PANDAS=0 + - UNCERTAINTIES="N" PYTHON="3.6" NUMPY_VERSION=1.16 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.3" NUMPY_VERSION=1.9.2 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.4" NUMPY_VERSION=1.11.2 PANDAS=0 # - UNCERTAINTIES="N" PYTHON="3.5" NUMPY_VERSION=1.11.2 PANDAS=0 From 4dcbe78766269f985d1898601d8f5384a1511f2c Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Mon, 18 Mar 2019 23:36:33 +0000 Subject: [PATCH 38/44] . --- pint/quantity.py | 9 ++++- pint/testsuite/test_numpy.py | 73 ++++++++++++++---------------------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/pint/quantity.py b/pint/quantity.py index 5472cdd90..0a0ea60b3 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -151,11 +151,12 @@ def implement_func(func_str, pre_calc_units_, post_calc_units_, out_units_): """ func = getattr(np,func_str) + print(func_str) @implements(func) def _(*args, **kwargs): # TODO make work for kwargs - print(func_str) + print("_",func_str) (pre_calc_units, post_calc_units, out_units)=(pre_calc_units_, post_calc_units_, out_units_) first_input_units=_get_first_input_units(args, kwargs) if pre_calc_units == "consistent_infer": @@ -190,7 +191,10 @@ def _(*args, **kwargs): elif out_units == "infer_from_input": out_units = first_input_units return post_calc_Q_.to(out_units) - +@implements(np.power) +def _power(*args, **kwargs): + print(args) + pass for func_str in ['linspace', 'concatenate', 'block', 'stack', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'column_stack', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff', 'ediff1d', 'fix']: implement_func(func_str, 'consistent_infer', 'as_pre_calc', 'as_post_calc') @@ -234,6 +238,7 @@ class BaseQuantity(PrettyIPython, SharedRegistryObject): :type units: UnitsContainer, str or Quantity. """ def __array_function__(self, func, types, args, kwargs): + print("__array_function__", func) if func not in HANDLED_FUNCTIONS: return NotImplemented if not all(issubclass(t, BaseQuantity) for t in types): diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 38ed35519..b3218a15d 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -198,17 +198,38 @@ def test_cross(self): def test_trapz(self): self.assertQuantityEqual(np.trapz([1. ,2., 3., 4.] * self.ureg.J, dx=1*self.ureg.m), 7.5 * self.ureg.J*self.ureg.m) # Arithmetic operations - @unittest.expectedFailure + def test_power(self): - """This is not supported as different elements might end up with different units + arr = np.array(range(3), dtype=np.float) + q = self.Q_(arr, 'meter') - eg. ([1, 1] * m) ** [2, 3] + for op_ in [op.pow, op.ipow, np.power]: + q_cp = copy.copy(q) + self.assertRaises(DimensionalityError, op_, 2., q_cp) + arr_cp = copy.copy(arr) + arr_cp = copy.copy(arr) + q_cp = copy.copy(q) + self.assertRaises(DimensionalityError, op_, q_cp, arr_cp) + q_cp = copy.copy(q) + q2_cp = copy.copy(q) + self.assertRaises(DimensionalityError, op_, q_cp, q2_cp) - Must force exponent to single value - """ - self._test2(np.power, self.q1, - (self.qless, np.asarray([1., 2, 3, 4])), - (self.q2, ),) + @unittest.expectedFailure + @helpers.requires_numpy() + def test_exponentiation_array_exp_2(self): + arr = np.array(range(3), dtype=np.float) + #q = self.Q_(copy.copy(arr), None) + q = self.Q_(copy.copy(arr), 'meter') + arr_cp = copy.copy(arr) + q_cp = copy.copy(q) + # this fails as expected since numpy 1.8.0 but... + self.assertRaises(DimensionalityError, op.pow, arr_cp, q_cp) + # ..not for op.ipow ! + # q_cp is treated as if it is an array. The units are ignored. + # BaseQuantity.__ipow__ is never called + arr_cp = copy.copy(arr) + q_cp = copy.copy(q) + self.assertRaises(DimensionalityError, op.ipow, arr_cp, q_cp) class TestNumpyUnclassified(TestNumpyMethods): def test_tolist(self): @@ -493,39 +514,3 @@ def test_right_shift(self): (self.qless, 2), (self.q1, self.q2, self.qs, ), 'same') - - -class TestNDArrayQuantityMath(QuantityTestCase): - - @helpers.requires_numpy() - def test_exponentiation_array_exp(self): - arr = np.array(range(3), dtype=np.float) - q = self.Q_(arr, 'meter') - - for op_ in [op.pow, op.ipow]: - q_cp = copy.copy(q) - self.assertRaises(DimensionalityError, op_, 2., q_cp) - arr_cp = copy.copy(arr) - arr_cp = copy.copy(arr) - q_cp = copy.copy(q) - self.assertRaises(DimensionalityError, op_, q_cp, arr_cp) - q_cp = copy.copy(q) - q2_cp = copy.copy(q) - self.assertRaises(DimensionalityError, op_, q_cp, q2_cp) - - @unittest.expectedFailure - @helpers.requires_numpy() - def test_exponentiation_array_exp_2(self): - arr = np.array(range(3), dtype=np.float) - #q = self.Q_(copy.copy(arr), None) - q = self.Q_(copy.copy(arr), 'meter') - arr_cp = copy.copy(arr) - q_cp = copy.copy(q) - # this fails as expected since numpy 1.8.0 but... - self.assertRaises(DimensionalityError, op.pow, arr_cp, q_cp) - # ..not for op.ipow ! - # q_cp is treated as if it is an array. The units are ignored. - # BaseQuantity.__ipow__ is never called - arr_cp = copy.copy(arr) - q_cp = copy.copy(q) - self.assertRaises(DimensionalityError, op.ipow, arr_cp, q_cp) From 6c62a05ef650c43598fe16bb82219ffd4ec1408b Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 28 Jul 2019 19:16:44 +0100 Subject: [PATCH 39/44] offset units --- pint/compat/__init__.py | 2 +- pint/quantity.py | 26 ++++++++++++++++++++------ pint/testsuite/test_numpy.py | 11 ++++++++++- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/pint/compat/__init__.py b/pint/compat/__init__.py index 02af8973d..2234d7ba0 100644 --- a/pint/compat/__init__.py +++ b/pint/compat/__init__.py @@ -131,7 +131,7 @@ def _to_magnitude(value, force_ndarray=False): import pandas as pd HAS_PANDAS = True # pin Pandas version for now - HAS_PROPER_PANDAS = pd.__version__.startswith("0.24.0.dev0+625.gbdb7a16") + HAS_PROPER_PANDAS = pd.__version__.startswith("0.25") except ImportError: HAS_PROPER_PANDAS = HAS_PANDAS = False diff --git a/pint/quantity.py b/pint/quantity.py index 0a0ea60b3..f093c25e9 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -110,6 +110,7 @@ def convert_to_consistent_units(pre_calc_units=None, *args, **kwargs): """Takes the args for a numpy function and converts any Quantity or Sequence of Quantities into the units of the first Quantiy/Sequence of quantities. Other args are left untouched. """ + print(args,kwargs) def convert_arg(arg): if pre_calc_units is not None: if isinstance(arg,BaseQuantity): @@ -123,8 +124,9 @@ def convert_arg(arg): return [item.m for item in arg] return arg - new_args=(convert_arg(arg) for arg in args) + new_args=tuple(convert_arg(arg) for arg in args) new_kwargs = {key:convert_arg(arg) for key,arg in kwargs.items()} + print( new_args, new_kwargs) return new_args, new_kwargs def implement_func(func_str, pre_calc_units_, post_calc_units_, out_units_): @@ -157,6 +159,8 @@ def implement_func(func_str, pre_calc_units_, post_calc_units_, out_units_): def _(*args, **kwargs): # TODO make work for kwargs print("_",func_str) + args_and_kwargs = list(args)+list(kwargs.values()) + (pre_calc_units, post_calc_units, out_units)=(pre_calc_units_, post_calc_units_, out_units_) first_input_units=_get_first_input_units(args, kwargs) if pre_calc_units == "consistent_infer": @@ -174,12 +178,19 @@ def _(*args, **kwargs): post_calc_units = pre_calc_units elif post_calc_units == "prod": product = 1 - for x in list(args)+list(kwargs.values()): + for x in args_and_kwargs: product *= x post_calc_units = product.units elif post_calc_units == "div": product = first_input_units*first_input_units - for x in list(args)+list(kwargs.values()): + for x in args_and_kwargs: + product /= x + post_calc_units = product.units + elif post_calc_units == "delta": + post_calc_units = (1*first_input_units-1*first_input_units).units + elif post_calc_units == "delta,div": + product=(1*first_input_units-1*first_input_units).units + for x in args_and_kwargs[1:]: product /= x post_calc_units = product.units print(post_calc_units) @@ -195,7 +206,7 @@ def _(*args, **kwargs): def _power(*args, **kwargs): print(args) pass -for func_str in ['linspace', 'concatenate', 'block', 'stack', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'column_stack', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'diff', 'ediff1d', 'fix']: +for func_str in ['linspace', 'concatenate', 'block', 'stack', 'hstack', 'vstack', 'dstack', 'atleast_1d', 'column_stack', 'atleast_2d', 'atleast_3d', 'expand_dims','squeeze', 'swapaxes', 'compress', 'searchsorted' ,'rollaxis', 'broadcast_to', 'moveaxis', 'fix']: implement_func(func_str, 'consistent_infer', 'as_pre_calc', 'as_post_calc') @@ -209,8 +220,11 @@ def _power(*args, **kwargs): for func_str in ['cross', 'trapz']: implement_func(func_str, None, 'prod', None) -for func_str in ['gradient']: - implement_func(func_str, None, 'div', None) +for func_str in ['diff', 'ediff1d',]: + implement_func(func_str, None, 'delta', None) + +for func_str in ['gradient', ]: + implement_func(func_str, None, 'delta,div', None) @contextlib.contextmanager diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index b3218a15d..6764c6391 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -26,6 +26,9 @@ def setUpClass(cls): @property def q(self): return [[1,2],[3,4]] * self.ureg.m + @property + def q_temperature(self): + return self.Q_([[1,2],[3,4]] , self.ureg.degC) class TestNumpyArrayCreation(TestNumpyMethods): @@ -180,15 +183,21 @@ def test_cumprod(self): def test_diff(self): self.assertQuantityEqual(np.diff(self.q, 1), [[1], [1]] * self.ureg.m) + self.assertQuantityEqual(np.diff(self.q_temperature, 1), [[1], [1]] * self.ureg.delta_degC) def test_ediff1d(self): - self.assertQuantityEqual(np.ediff1d(self.q, 1 * self.ureg.m), [1, 1, 1, 1] * self.ureg.m) + self.assertQuantityEqual(np.ediff1d(self.q), [1, 1, 1] * self.ureg.m) + self.assertQuantityEqual(np.ediff1d(self.q_temperature), [1, 1, 1] * self.ureg.delta_degC) def test_gradient(self): l = np.gradient([[1,1],[3,4]] * self.ureg.m, 1 * self.ureg.J) self.assertQuantityEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.m / self.ureg.J) self.assertQuantityEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.m / self.ureg.J) + l = np.gradient(self.Q_([[1,1],[3,4]] , self.ureg.degC), 1 * self.ureg.J) + self.assertQuantityEqual(l[0], [[2., 3.], [2., 3.]] * self.ureg.delta_degC / self.ureg.J) + self.assertQuantityEqual(l[1], [[0., 0.], [1., 1.]] * self.ureg.delta_degC / self.ureg.J) + def test_cross(self): a = [[3,-3, 1]] * self.ureg.kPa From b57f012aea5640af4df67ce76ef55e17736c1577 Mon Sep 17 00:00:00 2001 From: Andrew Savage Date: Sat, 17 Aug 2019 21:17:12 +0900 Subject: [PATCH 40/44] add warning --- pint/__init__.py | 2 +- pint/compat/__init__.py | 24 ++++++++++++++++++++++++ pint/errors.py | 1 + 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pint/__init__.py b/pint/__init__.py index 08ac13241..f47b863ff 100644 --- a/pint/__init__.py +++ b/pint/__init__.py @@ -23,6 +23,7 @@ from .context import Context +import warnings import sys try: from pintpandas import PintType, PintArray @@ -38,7 +39,6 @@ # so the reported version will be unknown __version__ = "unknown" - #: A Registry with the default units and constants. _DEFAULT_REGISTRY = LazyRegistry() diff --git a/pint/compat/__init__.py b/pint/compat/__init__.py index 2234d7ba0..7867e4244 100644 --- a/pint/compat/__init__.py +++ b/pint/compat/__init__.py @@ -13,6 +13,7 @@ import sys +import warnings from io import BytesIO from numbers import Number from decimal import Decimal @@ -66,6 +67,25 @@ def u(x): except ImportError: from itertools import izip_longest as zip_longest +# TODO: remove this warning after v0.10 +class BehaviorChangeWarning(UserWarning): + pass +_msg = ('The way pint handles numpy operations has changed. ' +'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/764 . ' +'To hide this warning use the following code to import pint:' +""" + +import warnings +with warnings.catch_warnings(): + warnings.simplefilter("ignore") + import pint +--- +""") + try: import numpy as np from numpy import ndarray @@ -87,6 +107,10 @@ def _to_magnitude(value, force_ndarray=False): if force_ndarray: return np.asarray(value) return value + + warnings.warn(_msg, BehaviorChangeWarning) + + except ImportError: diff --git a/pint/errors.py b/pint/errors.py index 65ad56b79..dfbdcdb1c 100644 --- a/pint/errors.py +++ b/pint/errors.py @@ -113,5 +113,6 @@ def __str__(self): return msg.format(self.units1, self.units2) + class UnitStrippedWarning(UserWarning): pass From d3195d1fdb3073e772b12d4dd96a11facc718353 Mon Sep 17 00:00:00 2001 From: Andrew Savage Date: Sat, 17 Aug 2019 21:21:56 +0900 Subject: [PATCH 41/44] add warning --- pint/errors.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pint/errors.py b/pint/errors.py index dfbdcdb1c..65ad56b79 100644 --- a/pint/errors.py +++ b/pint/errors.py @@ -113,6 +113,5 @@ def __str__(self): return msg.format(self.units1, self.units2) - class UnitStrippedWarning(UserWarning): pass From d9ef27d547ea20c50ff5d98a270d9734cd84c697 Mon Sep 17 00:00:00 2001 From: Andrew Savage Date: Sat, 17 Aug 2019 21:22:22 +0900 Subject: [PATCH 42/44] add warning --- pint/compat/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pint/compat/__init__.py b/pint/compat/__init__.py index 7867e4244..17fa6f5ae 100644 --- a/pint/compat/__init__.py +++ b/pint/compat/__init__.py @@ -13,7 +13,6 @@ import sys -import warnings from io import BytesIO from numbers import Number from decimal import Decimal From ea6bebe05857306bde16a384b541d90ddfb2c9b0 Mon Sep 17 00:00:00 2001 From: Andrew Savage Date: Sat, 17 Aug 2019 21:22:59 +0900 Subject: [PATCH 43/44] add warning --- pint/__init__.py | 1 - pint/compat/__init__.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pint/__init__.py b/pint/__init__.py index f47b863ff..3b9c91e5e 100644 --- a/pint/__init__.py +++ b/pint/__init__.py @@ -23,7 +23,6 @@ from .context import Context -import warnings import sys try: from pintpandas import PintType, PintArray diff --git a/pint/compat/__init__.py b/pint/compat/__init__.py index 17fa6f5ae..7867e4244 100644 --- a/pint/compat/__init__.py +++ b/pint/compat/__init__.py @@ -13,6 +13,7 @@ import sys +import warnings from io import BytesIO from numbers import Number from decimal import Decimal From aa66a079188f32e730bf126db1b493286eebd6e7 Mon Sep 17 00:00:00 2001 From: Andrew Savage Date: Sat, 17 Aug 2019 21:35:34 +0900 Subject: [PATCH 44/44] add warning --- pint/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pint/__init__.py b/pint/__init__.py index 3b9c91e5e..08ac13241 100644 --- a/pint/__init__.py +++ b/pint/__init__.py @@ -38,6 +38,7 @@ # so the reported version will be unknown __version__ = "unknown" + #: A Registry with the default units and constants. _DEFAULT_REGISTRY = LazyRegistry()