-
Notifications
You must be signed in to change notification settings - Fork 478
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom formatter can't reformat dimensionless unit #2024
Comments
I think I found the error source, but I don't know how to fix. The custom formatter is called by pint/pint/delegates/formatter/_compound_unit_helpers.py Lines 268 to 271 in ad02b87
So with a spec without a "~", the first output of pint/pint/delegates/formatter/_to_register.py Line 102 in ad02b87
The resulting container prints (repr) as |
Adding Why do you need to create a unit inside your custom formatter? |
Because the custom formatter builds on the default formatter. For calling the default one it needs a Unit object. But I guess one solution would be to copy the |
@aulemahal Can you provide a little bit more in detail you use case? I do not understand it too well. |
My usecase is this : https://github.com/xarray-contrib/cf-xarray/blob/c511cc5624ef0a217e83cb0f820a7acd16b877ae/cf_xarray/units.py#L19 I myself didn't write this function, but I think the code comes from a time where this was done outside of pint, not as a custom formatter, which might explain why it looks weird. To make things simple we begin by format the units using the short default format ("~D"). Then, using regex we split the result into its components to remove the division and exponentiation signs. Then remove the multiplication signs, replace "Δ" with "delta" and "percent" with "%". I had not re-read the exact way this was written and I now realize this is much more complex than re-writing the units directly from the container. The only issue I foresee is that we would want the "cf" formatter to always act as a short one, as if "~cf" was written. |
If you are sure that the input is unit, then recreating is not necessary. You can also subclass the existing formatter, this would be a direct replacement to your (I haven't tried yet) class MyFormatter(DefaultFormatter):
def format_unit(
self,
unit: PlainUnit | Iterable[tuple[str, Any]],
uspec: str = "",
sort_func: SortFunc | None = None,
**babel_kwds: Unpack[BabelKwds],
) -> str:
"""Format a unit (can be compound) into string
given a string formatting specification and locale related arguments.
"""
if "~" not in uspec:
uspec = "~" + uspec
s = super().format_unit(unit, uspec, sort_func, **babal_kwds)
# Search and replace patterns
pat = r"(?P<inverse>(?:1 )?/ )?(?P<unit>\w+)(?: \*\* (?P<pow>\d))?"
def repl(m):
i, u, p = m.groups()
p = p or (1 if i else "")
neg = "-" if i else ""
return f"{u}{neg}{p}"
out, n = re.subn(pat, repl, s)
# Remove multiplications
out = out.replace(" * ", " ")
# Delta degrees:
out = out.replace("Δ°", "delta_deg")
return out.replace("percent", "%")
# we need a better way here
ureg.formatter._formatters["cf"] = MyFormatter and then if you look into the DefaultFormatter you can actually directly copy the parse_unit and avoid calling the super function. |
By the way, I want to change the |
Closing this as it makes more sense to fix the issue in |
MWE:
raises: "KeyError: 'dimensionless'"
Full traceback:
To help the debug:
prints
.
The text was updated successfully, but these errors were encountered: