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

pint typing #1259

Merged
merged 1 commit into from
Aug 1, 2021
Merged

pint typing #1259

merged 1 commit into from
Aug 1, 2021

Conversation

jules-ch
Copy link
Collaborator

@jules-ch jules-ch commented Mar 7, 2021

Following the discussion started here #1166

  • Executed pre-commit run --all-files with no errors
  • The change is fully covered by automated unit tests
  • Documented in docs/ as appropriate
  • Added an entry to the CHANGES file

This PR introduces Type annotation support for Quantity.

Quantity is a Generic (https://docs.python.org/3/library/typing.html#user-defined-generic-types).

Magnitude type changes based on input type when constructing Quantity.
This will ease type checking for libraries & application using types and developers who wish to statically type check their code.

  • Quantity as Generic Type
  • Properly type annotate widely used functions from public API (Registry, Unit, Quantity)

If readability is becoming an issue we can export type annotations to stub files.

After the merge of this PR we should comply to PEP561.
We can also introduce Annotated Quantity types with dimensionality & unit.

After running mypy, we will be able to track some bugs since we have a lot of type checking with isinstance, switches etc.

@jules-ch jules-ch force-pushed the pint-typing branch 2 times, most recently from f388c7a to b6cad1d Compare March 14, 2021 22:20
@jules-ch jules-ch force-pushed the pint-typing branch 5 times, most recently from b08333a to abc29be Compare May 22, 2021 21:03
@jules-ch jules-ch added this to the 0.18 milestone May 24, 2021
@jules-ch
Copy link
Collaborator Author

jules-ch commented May 25, 2021

PR is almost ready.

from pint import UnitRegistry
import numpy as np

ureg = UnitRegistry()

float_quantity = ureg.Quantity(150.0, "m**2")
int_quantity = ureg.Quantity(150, "m**2")
array_quantity = ureg.Quantity(np.array([1, 2, 3, 4]), "m**2")
list_quantity = ureg.Quantity((1, 2, 3, 4), "m**2")
str_quantity = ureg.Quantity("50*2 m")

sequence = [ureg.Quantity(2, "m"), ureg.Quantity(3, "m")]

array_quantity_2 = ureg.Quantity.from_list(sequence)

reveal_type(float_quantity)
reveal_type(int_quantity)
reveal_type(float_quantity.m)
reveal_type(float_quantity.to("km**2"))
reveal_type(array_quantity)

reveal_type(array_quantity_2)
reveal_type(str_quantity)
reveal_type(list_quantity)

With mypy :

note: Revealed type is 'pint.quantity.Quantity[builtins.float*]'
note: Revealed type is 'pint.quantity.Quantity[builtins.int*]'
note: Revealed type is 'builtins.float*'
note: Revealed type is 'pint.quantity.Quantity[builtins.float*]'
Revealed type is 'pint.quantity.Quantity[numpy.ndarray*]'
Revealed type is 'pint.quantity.Quantity[numpy.ndarray]'
Revealed type is 'pint.quantity.Quantity[Any]'
Revealed type is 'pint.quantity.Quantity[numpy.ndarray]'

@jules-ch
Copy link
Collaborator Author

jules-ch commented May 31, 2021

If some people can test this in their IDE & with mypy. I'd be grateful.

I had trouble working with dynamically generated classes (like quantity is), it seems to work with another TypeVar in the overloaded __new__ constructor, I'll probably file issues in the mypy tracker.

For further steps I'll file an issue where we can discuss further steps.

  • I'll link this also to the issue where we discussed Array sublassing (it can ease type hints)
  • Annotated Types with PEP593
  • Better mypy support will come with a plugin I think or with dedicated types just like numpy does with DTypes
    It might be really difficult to implement, so I don't know if we want to go into this rabbithole.

@hgrecco as I said if type hints is becoming a burden in the code, we can extract them to stub files (*.pyi) if needed

@hgrecco
Copy link
Owner

hgrecco commented May 31, 2021

Cool. What do you think about adding from __future__ import annotations to have a uniform typing experience among all python versions. This is not supported in Python 3.6 but I think we can drop it as it is not supported by current NumPy version, right?

@keewis
Copy link
Contributor

keewis commented Jun 1, 2021

right, but that's not what pint has to look out for. Rather, NEP29 allowed dropping support for py36 about a year ago so this is definitely okay.

@jules-ch
Copy link
Collaborator Author

jules-ch commented Jun 1, 2021

I agree with @keewis , we can drop support for python 3.6 according to NEP 29.
Python 3.6 is going to the End of Life phase at the end of this year.
We can bump minimum numpy version aswell.

According to pypistats.org, python 3.6 represents roughly 10% of the download.

We can pin an issue announcing last compatible version & that version 0.18 will drop python 3.6 support. See if this raises any flags.

@hgrecco
Copy link
Owner

hgrecco commented Jun 1, 2021

@keewis You are right. I actually looked at NEP29.

@jules-ch I agree with the protocol you have proposed.

@jules-ch jules-ch force-pushed the pint-typing branch 3 times, most recently from 46120b9 to e4711ea Compare June 12, 2021 21:08
@jules-ch jules-ch force-pushed the pint-typing branch 2 times, most recently from 3ad490a to 727fc87 Compare July 25, 2021 21:04
@jules-ch jules-ch changed the title WIP : pint typing pint typing Jul 25, 2021
@jules-ch
Copy link
Collaborator Author

I'm fixing CI with #1356, after that I think we can merge this.

Type hints will be experimental, I made typing module private.
I haven't had a proper answer to the issue I raised in mypy python/mypy#10611, the workaround is working to have type inference on the constructor.

@jules-ch jules-ch marked this pull request as ready for review July 25, 2021 21:25
- Quantity as Generic class
- Add overloaded signature for __new__ Quantity
- Add typing module as private
- Add py.typed for PEP561 supports
- Add overloaded signature for __new__ Quantity
- Quantity as Generic class
- Add type hints throughout the project
- Add py.typed in package data in setup.cfg
- Add type hints for decorators
- Add type hints for public API of registry.py
- Add type hints for units.py
@jules-ch
Copy link
Collaborator Author

jules-ch commented Aug 1, 2021

@hgrecco It is ready for review

@hgrecco
Copy link
Owner

hgrecco commented Aug 1, 2021

This is awesome. Excelent job. My only question is the purpose of this

if TYPE_CHECKING:
    from .quantity import Quantity
    from .registry import UnitRegistry
    from .util import UnitsContainer

in files like context.py My understanding is that when annotations is imported from future everyhing is treated as an string and therefore this should not be necessary. But my knowledge of typing is not so great. Can you comment on this?

@jules-ch
Copy link
Collaborator Author

jules-ch commented Aug 1, 2021

It is used to avoid circular imports.
Even with __future__.annotations import statements are still there & therefore circular imports can still throw errors

@hgrecco
Copy link
Owner

hgrecco commented Aug 1, 2021

I get tge circular imports, and I get that these imports are only used when typechecking. I was wondering if there was no other place to put it (i.e. a typing imports file which make somethings available). In any case, I really like this and I will be happy to merge it

@hgrecco hgrecco merged commit 82a42e4 into hgrecco:master Aug 1, 2021
@conversy
Copy link

conversy commented Sep 7, 2022

Hi,

sorry for what might appear as a very dumb question, but I couldn't find any doc on how to use this.
Could you provide me with some examples of annotations that could be verified with mypy please?
That would be very helpful, thanks in advance!

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

Successfully merging this pull request may close these issues.

4 participants