diff --git a/pint/formatting.py b/pint/formatting.py index b00b771c7..d259ab615 100644 --- a/pint/formatting.py +++ b/pint/formatting.py @@ -197,6 +197,7 @@ def format_pretty(unit: UnitsContainer, registry: UnitRegistry, **options) -> st power_fmt="{}{}", parentheses_fmt="({})", exp_call=_pretty_fmt_exponent, + registry=registry, **options, ) @@ -228,6 +229,7 @@ def format_latex(unit: UnitsContainer, registry: UnitRegistry, **options) -> str division_fmt=r"\frac[{}][{}]", power_fmt="{}^[{}]", parentheses_fmt=r"\left({}\right)", + registry=registry, **options, ) return formatted.replace("[", "{").replace("]", "}") @@ -259,6 +261,7 @@ def format_html(unit: UnitsContainer, registry: UnitRegistry, **options) -> str: division_fmt=r"{}/{}", power_fmt=r"{}{}", parentheses_fmt=r"({})", + registry=registry, **options, ) @@ -273,6 +276,7 @@ def format_default(unit: UnitsContainer, registry: UnitRegistry, **options) -> s division_fmt=" / ", power_fmt="{} ** {}", parentheses_fmt=r"({})", + registry=registry, **options, ) @@ -287,10 +291,53 @@ def format_compact(unit: UnitsContainer, registry: UnitRegistry, **options) -> s division_fmt="/", power_fmt="{}**{}", parentheses_fmt=r"({})", + registry=registry, **options, ) +dim_order = [ '[substance]', '[mass]', '[current]', '[luminosity]', '[length]', '[time]', '[temperature]' ] +def dim_sort(units: Iterable[list[str]], registry: UnitRegistry): + """Sort a list of units by dimensional order. + + Parameters + ---------- + units : list + a list of unit names (without values). + registry : UnitRegistry + the registry to use for looking up the dimensions of each unit. + + Returns + ------- + list + the list of units sorted by most significant dimension first. + + Raises + ------ + KeyError + If unit cannot be found in the registry. + """ + ret_dict = dict() + many = len(units) > 1 + for name in units: + cname = registry.get_name(name) + if not cname: + continue + dim_types = iter(dim_order) + while True: + try: + dim = next(dim_types) + if dim in registry.get_dimensionality(cname): + if dim not in ret_dict: + ret_dict[dim] = list() + ret_dict[dim].append(cname) + break + except StopIteration: + raise KeyError(f"Unit {cname} has no recognized dimensions") + + ret = sum([ret_dict[dim] for dim in dim_order if dim in ret_dict], []) + return ret + def formatter( items: Iterable[tuple[str, Number]], as_ratio: bool = True, @@ -304,6 +351,8 @@ def formatter( babel_length: str = "long", babel_plural_form: str = "one", sort: bool = True, + sort_dims: bool = False, + registry: Optional[UnitRegistry] = None, ) -> str: """Format a list of (name, exponent) pairs. @@ -334,6 +383,12 @@ def formatter( (Default value = lambda x: f"{x:n}") sort : bool, optional True to sort the formatted units alphabetically (Default value = True) + sort_dims : bool, optional + True to sort the units dimentionally (Default value = False). + When dimensions have multiple units, sort by "most significant dimension" the unit contains + When both `sort` and `sort_dims` are True, sort alphabetically within sorted dimensions + registry : UnitRegistry, optional + The registry to use if `sort_dims` is True Returns ------- @@ -393,6 +448,12 @@ def formatter( else: neg_terms.append(power_fmt.format(key, fun(value))) + if sort_dims: + if len(pos_terms)>1: + pos_terms = dim_sort(pos_terms, registry) + if len(neg_terms)>1: + neg_terms = dim_sort(neg_terms, registry) + if not as_ratio: # Show as Product: positive * negative terms ** -1 return _join(product_fmt, pos_terms + neg_terms) diff --git a/pint/testsuite/test_issues.py b/pint/testsuite/test_issues.py index c98ac61bf..c4bdd2b5d 100644 --- a/pint/testsuite/test_issues.py +++ b/pint/testsuite/test_issues.py @@ -1150,3 +1150,20 @@ def test_issues_1505(): assert isinstance( ur.Quantity("m/s").magnitude, decimal.Decimal ) # unexpected fail (magnitude should be a decimal) + + +def test_issues_1841(): + import pint + + # sets compact display mode + ur = UnitRegistry() + ur.default_format = '~P' + + # creates quantity + q = ur.Quantity("1 kW * 1 h") + + assert pint.formatting.format_unit(q.u._units, spec='', registry=ur, sort_dims=True) == 'kilowatt * hour' + assert pint.formatting.format_unit(q.u._units, spec='', registry=ur, sort_dims=False) == 'hour * kilowatt' + + # this prints "1 h·kW", not "1 kW·h" unless sort_dims is True + # print(q)