diff --git a/physical_quantities/CHANGELOG.md b/physical_quantities/CHANGELOG.md index 601f98d..19cc309 100644 --- a/physical_quantities/CHANGELOG.md +++ b/physical_quantities/CHANGELOG.md @@ -41,6 +41,7 @@ The first public release. This is a further tidy-up after the 0.2.0 extraction: ### Fixed +- A value that is a list, with a unit, is converted value by value: `{1, 2} km`, `1,2~\mathrm{km}` (LaTeX), `plus_minus 2 km` and `\pm 2~\mathrm{km}` give `standard_values` of 1000 and 2000 (or −2000 and 2000). Before, the unit factor multiplied the list as a whole, and `parse_quantity` raised `QuantityParseError`. - The roentgen was converted as kelvin per kilogram; it is 2.58×10⁻⁴ C/kg (ampere·second per kilogram). - The imperial pint was 0.00056826 m³; it is 0.00056826125 m³, so that 8 pints make a gallon. - `nautical_miles` is recognised as the plural of `nautical_mile`. Before, only `nauticalmiles` was, and in natural mode `2 nautical_miles` parsed as 2 nautical_mile·second. diff --git a/physical_quantities/src/lambdafeedback/physical_quantities/_conversion.py b/physical_quantities/src/lambdafeedback/physical_quantities/_conversion.py index d69d173..c890afd 100644 --- a/physical_quantities/src/lambdafeedback/physical_quantities/_conversion.py +++ b/physical_quantities/src/lambdafeedback/physical_quantities/_conversion.py @@ -9,6 +9,7 @@ from lambdafeedback.expression_parsing import ( SympyParsingConfig, + create_expression_set, parse_expression, parse_expressions, parse_latex, @@ -85,6 +86,7 @@ def sympy_forms( value_text: Any = value.content_string() if value is not None else None if value_text is not None and params.is_latex: value_text = parse_latex(value_text, params.symbols, params.simplify) + standard_values: tuple[Basic, ...] | None = None standard_unit: Any = None expanded_unit: Any = None dimension: Basic = Integer(1) @@ -99,10 +101,17 @@ def sympy_forms( base_symbols = {symbol: 1 for symbol in standard_unit.free_symbols if str(symbol) in _BASE_UNIT_NAMES} unit_factor = standard_unit.subs(base_symbols).simplify() standard_unit = (standard_unit / unit_factor).simplify(rational=True) - value_text = f"({value_text})*({unit_factor})" + if len(create_expression_set(value_text, config.substitution_params)) > 1: + # A list ({1, 2}, 1,2 in LaTeX, or ±): the factor applies to each value, not to the list. + factored_values = tuple(value * unit_factor for value in parse_expressions(value_text, config)) + standard_values = tuple(dict.fromkeys(factored_values)) + else: + value_text = f"({value_text})*({unit_factor})" dimension = parse_expression(substitute(expanded_unit_string, _BASE_UNIT_DIMENSIONS), config) + if standard_values is None and value_text is not None: + standard_values = parse_expressions(value_text, config) return SympyForms( - standard_values=None if value_text is None else parse_expressions(value_text, config), + standard_values=standard_values, standard_unit=standard_unit, expanded_unit=expanded_unit, dimension=dimension, diff --git a/physical_quantities/tests/test_regressions.py b/physical_quantities/tests/test_regressions.py index 53e4ae4..c9a0cf2 100644 --- a/physical_quantities/tests/test_regressions.py +++ b/physical_quantities/tests/test_regressions.py @@ -117,6 +117,21 @@ def test_latex_spacing_and_upright_units(self, expr, value, unit): quantity = parse_quantity(expr, QuantityParams(is_latex=True)) assert (quantity.value.content_string(), quantity.unit.content_string()) == (value, unit) + @pytest.mark.parametrize( + "expr, is_latex, values", + [ + ("{1, 2} km", False, {1000, 2000}), + ("plus_minus 2 km", False, {-2000, 2000}), + (r"1,2~\mathrm{km}", True, {1000, 2000}), + (r"\pm 2~\mathrm{km}", True, {-2000, 2000}), + ], + ) + def test_unit_factor_applies_to_each_value_of_a_list(self, expr, is_latex, values): + # The factor multiplied the list as a whole, "((1, 2))*(1000)", which raised. + quantity = parse_quantity(expr, QuantityParams(is_latex=is_latex)) + assert set(quantity.standard_values) == values + assert str(quantity.standard_unit) == "metre" + @pytest.mark.parametrize("strictness", ["strict", "natural"]) @pytest.mark.parametrize( "expr, unit",