Skip to content
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

No doc for type checker usage (e.g. with mypy) #1577

Open
conversy opened this issue Sep 8, 2022 · 4 comments
Open

No doc for type checker usage (e.g. with mypy) #1577

conversy opened this issue Sep 8, 2022 · 4 comments

Comments

@conversy
Copy link

conversy commented Sep 8, 2022

Hi,

following this issue and this PR on typing/annotation, I could not find a documentation or a tutorial that would explain how to use pint with mypyor other Python checkers, together with the limitations.

Would it be possible to discuss here a few examples of code and usage, before creating a doc entry PR for this topic?
That would be very useful!

Best,

Stéphane

PS: I first asked a similar question in the PR discussion, but I figured it would be best to submit a documentation issue.

@conversy
Copy link
Author

conversy commented Sep 15, 2022

after some investigations, I ended up with this example of how to best use Pint with mypy:

import pint
ureg = pint.UnitRegistry()

class Human:
    height: pint.Quantity = 0 * ureg.centimeter                        # initialize to a preferred unit/dimensionality
    def __init__(self, height: pint.Quantity = 0 * ureg.centimeter):   # idem for default value
        height.check('[length]')                                       # check dimensionality
        # assert(height.dimensionality == self.height.dimensionality)  # check dimensionality variant
        self.height = height.to(ureg.centimeter)                       # optional call to .to, keep the field as centimeters

h1 = Human(180 * ureg.centimeter)  # mypy ok, assertions ok
h2 = Human(6 * ureg.feet)          # mypy ok, assertions still ok
print(h2.height)
h3 = Human(180)                    # mypy error: not a pint.Quantity
h4 = Human(180 * ureg.second)      # mypy ok, assertions error: cannot convert units

rationale:

  • AFAIU mypycannot check more that a parameter is at least a pint.Quantity, hence its use in the method signature
  • I want to ensure that the dimensionality of a parameter is correct, hence the two assertions. The first one is the documented one. The variant checks that the initial dimensionality is respected before an assignment to a field. Conversely to the first assertion, the variant stays the same if one decides to change the dimensionality of a field. It's also simpler if the dimensionality is complex.
  • to keep the same unit throughout the lifetime of the object, use pint.Quantity.to
  • the assertions are unnecessary if one uses pint.Quantity.to, as it will raise an error if the quantities are not compatible.

I hope this is helpful, I may be completely wrong though...

@conversy
Copy link
Author

conversy commented Sep 27, 2022

import pint
ureg = pint.UnitRegistry()

# raise DimensionalityError if not compatible
def assert_compatible_with(a,b):
    b.to(a.units)

class Human:
    def __init__(self, height: pint.Quantity = 0 * ureg.cm):   # default value in a preferred unit/dimensionality
        height.check('[length]')                               # check dimensionality
        assert_compatible_with(height, 0 * ureg.cm)            # variant
        self.height = height.to(ureg.centimeter)               # optional call to .to, keep the field as centimeters

h1 = Human(180 * ureg.centimeter)  # mypy ok, assertions ok
h2 = Human(6 * ureg.feet)          # mypy ok, assertions still ok
h3 = Human(180)                    # mypy error: not a pint.Quantity
h4 = Human(180 * ureg.second)      # mypy ok, assertions error: cannot convert units

In this version, I use an assert that raises the corresponding exceptions with a useful message.
Is there such an assert in the lib? I could not find one... Mine is costly since it converts values, and we only want to check dimensionality...

@jules-ch jules-ch added the docs label Oct 20, 2022
@arkanoid87
Copy link

any news on this? I'm playing around trying to make mypy happy with my pint'ed code that works at runtime, but fails in mypy

@conversy
Copy link
Author

Might be another instance of 'read the doc, Luke', but I overlooked wrapping:

class Human:
    @ureg.check(None, '[length]')
    def __init__(self, height: pint.Quantity):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants