Giter Club home page Giter Club logo

Comments (8)

aulemahal avatar aulemahal commented on July 17, 2024

I think I found the error source, but I don't know how to fix.

The custom formatter is called by NewFormatter, a class declared in the wrapper itself. This class has the format_unit method which prepares the unit with pint.delegates.formatter._compound_unit_helpers.prepare_compount_unit. In the case of a dimensionless quantity, that last function has a conditional I don't understand:

if "~" in spec:
return ([], [])
else:
return ([("dimensionless", 1)], [])

So with a spec without a "~", the first output of prepare_compount_unit is [("dimensionless", 1)]. NewFormatter goes on and wraps this into a UnitsContainer :

units = self._registry.UnitsContainer(numerator)

The resulting container prints (repr) as <UnitsContainer({'dimensionless': 1})>. However, if we go back to the initial unit, its container rather prints as repr(u._units) <UnitsContainer({})>. I thus assume the first UnitsContainer, the one sent by NewFormatter to the custom function, is wrong.

from pint.

andrewgsavage avatar andrewgsavage commented on July 17, 2024

Adding return ([("dimensionless", 1)], []) was the most obvious way I saw to get the formatters to return "dimensionless" as the longform. I'm not sure what the previous behaviour was.

Why do you need to create a unit inside your custom formatter?

from pint.

aulemahal avatar aulemahal commented on July 17, 2024

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 formatter call from the default formatter instead of actually calling it. Hopefully, that solution would work with previous pint versions as well.

from pint.

hgrecco avatar hgrecco commented on July 17, 2024

@aulemahal Can you provide a little bit more in detail you use case? I do not understand it too well.

from pint.

aulemahal avatar aulemahal commented on July 17, 2024

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.

from pint.

hgrecco avatar hgrecco commented on July 17, 2024

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.

see here

from pint.

hgrecco avatar hgrecco commented on July 17, 2024

By the way, I want to change the prepare_compount_unit function to stop accepting spec. The only spec that is used now is ~. So I would rather have a boolean flag about using the short version.

from pint.

aulemahal avatar aulemahal commented on July 17, 2024

Closing this as it makes more sense to fix the issue in cf-xarray.

from pint.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.