Skip to content

Commit

Permalink
Merge pull request #3778 from embray/units/quantity-array-repr
Browse files Browse the repository at this point in the history
Allow object arrays of mixed unit Quantities to be printed
  • Loading branch information
embray committed May 29, 2015
2 parents 77440e1 + 584a304 commit 346e455
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 16 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Expand Up @@ -285,6 +285,12 @@ New Features

- ``astropy.units``

- Fixed printing of object ndarrays containing multiple Quantity
objects with differing / incompatible units. Note: Unit conversion errors
now cause a ``UnitConversionError`` exception to be raised. However, this
is a subclass of the ``UnitsError`` exception used previously, so existing
code that catches ``UnitsError`` should still work. [#3778]

- ``astropy.utils``

- ``astropy.vo``
Expand Down
8 changes: 4 additions & 4 deletions astropy/nddata/compat.py
Expand Up @@ -6,7 +6,7 @@

import numpy as np

from ..units import UnitsError, Unit
from ..units import UnitsError, UnitConversionError, Unit
from .. import log

from .nddata import NDData
Expand Down Expand Up @@ -115,9 +115,9 @@ def uncertainty(self, value):
try:
scaling = (1 * value._unit).to(self.unit)
except UnitsError:
raise UnitsError('Cannot convert unit of uncertainty '
'to unit of '
'{0} object.'.format(class_name))
raise UnitConversionError(
'Cannot convert unit of uncertainty to unit of '
'{0} object.'.format(class_name))
value.array *= scaling
elif not self.unit and value._unit:
# Raise an error if uncertainty has unit and data does not
Expand Down
3 changes: 3 additions & 0 deletions astropy/time/core.py
Expand Up @@ -20,6 +20,7 @@

from .. import units as u
from .. import _erfa as erfa
from ..units import UnitConversionError
from ..utils.compat.odict import OrderedDict
from ..utils.compat.misc import override__dir__
from ..extern import six
Expand Down Expand Up @@ -282,6 +283,8 @@ def _get_time_fmt(self, val, val2, format, scale):
try:
return FormatClass(val, val2, scale, self.precision,
self.in_subfmt, self.out_subfmt)
except UnitConversionError:
raise
except (ValueError, TypeError):
pass
else:
Expand Down
17 changes: 12 additions & 5 deletions astropy/units/core.py
Expand Up @@ -27,8 +27,8 @@
# TODO: Support functional units, e.g. log(x), ln(x)

__all__ = [
'UnitsError', 'UnitsWarning', 'UnitBase', 'NamedUnit',
'IrreducibleUnit', 'Unit', 'def_unit', 'CompositeUnit',
'UnitsError', 'UnitsWarning', 'UnitConversionError', 'UnitBase',
'NamedUnit', 'IrreducibleUnit', 'Unit', 'def_unit', 'CompositeUnit',
'PrefixUnit', 'UnrecognizedUnit', 'get_current_unit_registry',
'set_enabled_units', 'add_enabled_units',
'set_enabled_equivalencies', 'add_enabled_equivalencies',
Expand Down Expand Up @@ -451,6 +451,13 @@ class UnitScaleError(UnitsError, ValueError):
pass


class UnitConversionError(UnitsError, ValueError):
"""
Used specifically for errors related to converting between units or
interpreting units in terms of other units.
"""


# Maintain error in old location for backward compatibility
from .format import fits as _fits
_fits.UnitScaleError = UnitScaleError
Expand Down Expand Up @@ -837,7 +844,7 @@ def get_err_str(unit):
unit_str = get_err_str(orig_unit)
other_str = get_err_str(orig_other)

raise UnitsError(
raise UnitConversionError(
"{0} and {1} are not convertible".format(
unit_str, other_str))

Expand Down Expand Up @@ -910,7 +917,7 @@ def _to(self, other):
in zip(self_decomposed.bases, other_decomposed.bases))):
return self_decomposed.scale / other_decomposed.scale

raise UnitsError(
raise UnitConversionError(
"'{0!r}' is not a scaled version of '{1!r}'".format(self, other))

def to(self, other, value=1.0, equivalencies=[]):
Expand Down Expand Up @@ -1636,7 +1643,7 @@ def decompose(self, bases=set()):
return CompositeUnit(scale, [base], [1],
_error_check=False)

raise UnitsError(
raise UnitConversionError(
"Unit {0} can not be decomposed into the requested "
"bases".format(self))

Expand Down
4 changes: 2 additions & 2 deletions astropy/units/quantity_helper.py
Expand Up @@ -2,7 +2,7 @@
# quantities (http://pythonhosted.org/quantities/) package.

import numpy as np
from .core import (UnitsError, dimensionless_unscaled,
from .core import (UnitsError, UnitConversionError, dimensionless_unscaled,
get_current_unit_registry)
from ..utils.compat.fractions import Fraction

Expand Down Expand Up @@ -279,7 +279,7 @@ def get_converters_and_unit(f, *units):
converters[changeable] = get_converter(units[changeable],
units[fixed])
except UnitsError:
raise UnitsError(
raise UnitConversionError(
"Can only apply '{0}' function to quantities "
"with compatible dimensions"
.format(f.__name__))
Expand Down
20 changes: 20 additions & 0 deletions astropy/units/tests/test_quantity.py
Expand Up @@ -1155,3 +1155,23 @@ def test_insert():
q2 = q.insert(1, 10 * u.m, axis=1)
assert np.all(q2.value == [[ 1, 10, 2],
[ 3, 10, 4]])


def test_repr_array_of_quantity():
"""
Test print/repr of object arrays of Quantity objects with different
units.
Regression test for the issue first reported in
https://github.com/astropy/astropy/issues/3777
"""

a = np.array([1 * u.m, 2 * u.s], dtype=object)
if NUMPY_LT_1_7:
# Numpy 1.6.x has some different defaults for how to display object
# arrays (it uses the str() of the objects instead of the repr()
assert repr(a) == 'array([1.0 m, 2.0 s], dtype=object)'
assert str(a) == '[1.0 m 2.0 s]'
else:
assert repr(a) == 'array([<Quantity 1.0 m>, <Quantity 2.0 s>], dtype=object)'
assert str(a) == '[<Quantity 1.0 m> <Quantity 2.0 s>]'
2 changes: 1 addition & 1 deletion docs/units/conversion.rst
Expand Up @@ -34,7 +34,7 @@ If you attempt to convert to a incompatible unit, an exception will result:
>>> cms.to(u.km)
Traceback (most recent call last):
...
UnitsError: 'cm / s' (speed) and 'km' (length) are not convertible
UnitConversionError: 'cm / s' (speed) and 'km' (length) are not convertible

You can check whether a particular conversion is possible using the
`~astropy.units.core.UnitBase.is_equivalent` method::
Expand Down
6 changes: 3 additions & 3 deletions docs/units/equivalencies.rst
Expand Up @@ -38,7 +38,7 @@ Length and angles are not normally convertible, so
>>> (8.0 * u.arcsec).to(u.parsec)
Traceback (most recent call last):
...
UnitsError: 'arcsec' (angle) and 'pc' (length) are not convertible
UnitConversionError: 'arcsec' (angle) and 'pc' (length) are not convertible

However, when passing the result of
:func:`~astropy.units.equivalencies.parallax` as the third argument to the
Expand Down Expand Up @@ -68,11 +68,11 @@ dimensionless). For instance, normally the following raise exceptions::
>>> u.degree.to('')
Traceback (most recent call last):
...
UnitsError: 'deg' (angle) and '' (dimensionless) are not convertible
UnitConversionError: 'deg' (angle) and '' (dimensionless) are not convertible
>>> (u.kg * u.m**2 * (u.cycle / u.s)**2).to(u.J)
Traceback (most recent call last):
...
UnitsError: 'cycle2 kg m2 / s2' and 'J' (energy) are not convertible
UnitConversionError: 'cycle2 kg m2 / s2' and 'J' (energy) are not convertible

But when passing we pass the proper conversion function,
:func:`~astropy.units.equivalencies.dimensionless_angles`, it works.
Expand Down
2 changes: 1 addition & 1 deletion docs/units/index.rst
Expand Up @@ -104,7 +104,7 @@ conversion from wavelength to frequency doesn't normally work:
>>> (1000 * u.nm).to(u.Hz)
Traceback (most recent call last):
...
UnitsError: 'nm' (length) and 'Hz' (frequency) are not convertible
UnitConversionError: 'nm' (length) and 'Hz' (frequency) are not convertible

but by passing an equivalency list, in this case ``spectral()``, it does:

Expand Down

0 comments on commit 346e455

Please sign in to comment.