From 3596c7fc72c1a2027167c517eb23c0f40ee2529d Mon Sep 17 00:00:00 2001 From: Ken Kroenlein <51962276+kroenlein@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:11:51 -0700 Subject: [PATCH] Prevent count, radian, bit and dimensionless interconversion (#209) --- gemd/__version__.py | 2 +- gemd/units/impl.py | 16 ++++++++++++++-- tests/units/test_parser.py | 10 ++++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/gemd/__version__.py b/gemd/__version__.py index 4bd6f7be..14e8848a 100644 --- a/gemd/__version__.py +++ b/gemd/__version__.py @@ -1 +1 @@ -__version__ = "1.18.2" +__version__ = "1.18.3" diff --git a/gemd/units/impl.py b/gemd/units/impl.py index 47f1f534..78b38c0b 100644 --- a/gemd/units/impl.py +++ b/gemd/units/impl.py @@ -227,8 +227,20 @@ def convert_units(value: float, starting_unit: str, final_unit: str) -> float: if starting_unit == final_unit: return value # skip computation else: - resolved_final_unit = _REGISTRY.parse_units(final_unit) # `to` bypasses preparser - return _REGISTRY.Quantity(value, starting_unit).to(resolved_final_unit).magnitude + resolved_value = _REGISTRY.Quantity(value, starting_unit) + resolved_final_unit = _REGISTRY.parse_units(final_unit) + # Make sure count, radian, bit, and non-dimensional don't accidentally interconvert + # https://pint.readthedocs.io/en/0.23/user/angular_frequency.html + root1 = _REGISTRY.get_root_units(resolved_value)[1] + root2 = _REGISTRY.get_root_units(resolved_final_unit)[1] + if root1 != root2: + raise IncompatibleUnitsError( + units1=resolved_value.units, + dim1=_REGISTRY.get_dimensionality(resolved_final_unit), + units2=final_unit, + dim2=_REGISTRY.get_dimensionality(resolved_final_unit) + ) + return resolved_value.to(resolved_final_unit).magnitude @register_unit_format("clean") diff --git a/tests/units/test_parser.py b/tests/units/test_parser.py index 19bb9031..88c7e33a 100644 --- a/tests/units/test_parser.py +++ b/tests/units/test_parser.py @@ -5,8 +5,7 @@ import pytest from gemd.units import parse_units, convert_units, get_base_units, change_definitions_file, \ - UndefinedUnitError, DefinitionSyntaxError -from gemd.units.impl import DEFAULT_FILE + UndefinedUnitError, DefinitionSyntaxError, IncompatibleUnitsError @pytest.mark.parametrize("return_unit", [True, False]) @@ -144,6 +143,13 @@ def test_conversion(): assert -1e-8 < convert_units(100, 'g / 100 mL', 'g/cc') - 1 < 1e-8 assert -1e-8 < convert_units(1, "g / 2.5 cm", "g / 25 mm") - 1 < 1e-8 + # Verify that convert_units throws exceptions + with pytest.raises(IncompatibleUnitsError): + convert_units(1, 'mL', 'g') + with pytest.raises(IncompatibleUnitsError): + # https://pint.readthedocs.io/en/0.23/user/angular_frequency.html + convert_units(1, 'Hz', 'rpm') + def test_get_base_units(): """Test that base units & conversions make sense."""