Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion astropy/io/votable/tests/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ def test_write_jybeam_unit(tmp_path, recwarn):
t = Table(
{
"flux": [5 * (u.Jy / u.beam)],
"foo": [0 * u.Unit("Crab", format="ogip")],
"foo": [0 * u.Unit("Crab", format="ogip", parse_strict="warn")],
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not obvious to me why a test about writing a VOTable should assert that parsing the string "Crab" with the "ogip" format emits a warning, but that is what the test does.

"bar": [1 * u.def_unit("my_unit")],
}
)
Expand Down
8 changes: 5 additions & 3 deletions astropy/units/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2033,11 +2033,13 @@ def __call__(
if isinstance(s, bytes):
s = s.decode("ascii")

try:
return f._validate_unit(s, detailed_exception=False) # Try a shortcut
try: # Try a shortcut
return f._validate_unit(
s, detailed_exception=False, deprecations="raise"
)
except (AttributeError, ValueError):
# No `f._validate_unit()` (AttributeError)
# or `s` was a composite unit (ValueError).
# or `s` was a composite unit or deprecated (ValueError).
pass

try:
Expand Down
18 changes: 16 additions & 2 deletions astropy/units/format/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import numpy as np

from astropy.units.core import CompositeUnit, NamedUnit, Unit, get_current_unit_registry
from astropy.units.errors import UnitsWarning
from astropy.units.errors import UnitParserWarning, UnitsWarning
from astropy.units.utils import maybe_simple_fraction
from astropy.utils.misc import did_you_mean

Expand Down Expand Up @@ -253,14 +253,28 @@ def _did_you_mean_units(cls, unit: str) -> str:
return did_you_mean(unit, cls._units, fix=cls._fix_deprecated)

@classmethod
def _validate_unit(cls, unit: str, detailed_exception: bool = True) -> UnitBase:
def _validate_unit(
cls,
unit: str,
detailed_exception: bool = True,
deprecations: Literal["silent", "warn", "raise"] = "warn",
Copy link
Member Author

@eerovaher eerovaher Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect the new parameter will be helpful for #18304.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be, and perhaps the best way forward would be to try to solve #18304 first (which can be done for 7.2). Though one of the options there has to be "convert".

) -> UnitBase:
if unit in cls._deprecated_units:
if deprecations == "warn":
warnings.warn(cls._deprecated_unit_message(unit), UnitParserWarning)
elif deprecations == "raise":
raise ValueError()
try:
return cls._units[unit]
except KeyError:
if detailed_exception:
raise ValueError(cls._invalid_unit_error_message(unit)) from None
raise ValueError() from None

@classmethod
def _deprecated_unit_message(cls, unit: str) -> str:
return f"The unit '{unit}' has been deprecated in the {cls.__name__} standard."

@classmethod
def _invalid_unit_error_message(cls, unit: str) -> str:
return (
Expand Down
4 changes: 3 additions & 1 deletion astropy/units/format/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,9 @@ class Generic(Base, _GenericParserMixin):
"""

@classmethod
def _validate_unit(cls, s: str, detailed_exception: bool = True) -> UnitBase:
def _validate_unit(
cls, s: str, detailed_exception: bool = True, deprecations: str = "warn"
) -> UnitBase:
registry = get_current_unit_registry().registry
if s in cls._unit_symbols:
s = cls._unit_symbols[s]
Expand Down
9 changes: 0 additions & 9 deletions astropy/units/format/ogip.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,12 +362,3 @@ def format_exponential_notation(
cls, val: UnitScale | np.number, format_spec: str = "g"
) -> str:
return format(val, format_spec)

@classmethod
def _validate_unit(cls, unit: str, detailed_exception: bool = True) -> UnitBase:
if unit in cls._deprecated_units:
warnings.warn(
f"The unit '{unit}' has been deprecated in the OGIP standard.",
UnitsWarning,
)
return super()._validate_unit(unit, detailed_exception)
21 changes: 6 additions & 15 deletions astropy/units/format/vounit.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@
dimensionless_unscaled,
si_prefixes,
)
from astropy.units.errors import (
UnitParserWarning,
UnitScaleError,
UnitsError,
UnitsWarning,
)
from astropy.units.errors import UnitParserWarning, UnitScaleError, UnitsError
from astropy.utils import classproperty

from . import Base, utils
Expand Down Expand Up @@ -222,12 +217,8 @@ def _fix_deprecated(cls, x: str) -> list[str]:
)

@classmethod
def _validate_unit(cls, unit: str, detailed_exception: bool = True) -> UnitBase:
if unit in cls._deprecated_units:
warnings.warn(
UnitsWarning(
f"The unit '{unit}' has been deprecated in the VOUnit standard."
f" Suggested: {cls.to_string(cls._units[unit]._represents)}."
)
)
return super()._validate_unit(unit, detailed_exception)
def _deprecated_unit_message(cls, unit: str) -> str:
return (
super()._deprecated_unit_message(unit)
+ f" Suggested: {cls.to_string(cls._units[unit]._represents)}."
)
14 changes: 8 additions & 6 deletions astropy/units/tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,12 @@ def check_roundtrip(self, unit, output_format=None):
s = unit.to_string(output_format)

if s in self.format_._deprecated_units:
with pytest.warns(UnitsWarning, match="deprecated") as w:
a = Unit(s, format=self.format_)
with pytest.raises(ValueError, match="deprecated"):
Unit(s, format=self.format_)
with pytest.warns(UnitParserWarning, match="deprecated") as w:
a = Unit(s, format=self.format_, parse_strict="warn")
assert len(w) == 1
assert Unit(s, format=self.format_, parse_strict="silent") == a
else:
a = Unit(s, format=self.format_) # No warning

Expand All @@ -373,7 +376,7 @@ def check_roundtrip_decompose(self, unit):
ud = unit.decompose()
s = ud.to_string(self.format_)
assert " " not in s
a = Unit(s, format=self.format_)
a = Unit(s, format=self.format_, parse_strict="warn")
assert_allclose(a.decompose().scale, ud.scale, rtol=1e-5)


Expand Down Expand Up @@ -464,7 +467,7 @@ def test_roundtrip(self, unit):
# as a deprecated unit.
with pytest.warns(UnitsWarning):
s = unit.to_string(self.format_)
a = Unit(s, format=self.format_)
a = Unit(s, format=self.format_, parse_strict="warn")
assert_allclose(a.decompose().scale, unit.decompose().scale, rtol=1e-9)
else:
self.check_roundtrip(unit)
Expand Down Expand Up @@ -735,9 +738,8 @@ def test_deprecated_did_you_mean_units():
):
u.Unit("ANGSTROM", format="vounit")

with pytest.warns(UnitsWarning, match=r".* 0\.1nm\.") as w:
with pytest.raises(ValueError, match=r".* 0\.1nm\."):
u.Unit("angstrom", format="vounit")
assert len(w) == 1


@pytest.mark.parametrize("string", ["mag(ct/s)", "dB(mW)", "dex(cm s**-2)"])
Expand Down
7 changes: 7 additions & 0 deletions docs/changes/units/18536.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Parsing deprecated units (e.g. ``u.Unit("erg", format="vounit",
parse_strict=...)``) now obeys the ``parse_strict`` argument, which has so far
been ignored in that context.
Because by default ``parse_strict="raise"`` then parsing deprecated units can
now cause an error where it previously succeeded with a warning, but the
previous behaviour (for deprecated units) can be ensured in a
backwards-compatible way by setting ``parse_strict="warn"`` explicitly.
Loading