-
Notifications
You must be signed in to change notification settings - Fork 70
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
MyPy, lots of MyPy #123
MyPy, lots of MyPy #123
Conversation
This PR makes Elemental pass MyPy. I've done what I can to make not use the `type: ignore` comment best I can, but Pydantic and MyPy don't seem to play nice in many cases. Outstanding issues I have no good solution to: * Typed Dict not available until 3.8 * `lambda` functions with kwargs that have defaults can't be inffered and Callable does not work. * MyPy does not like the `constr` from Pydantic * Not sure how to fix the Array type acceptance, if I try to make it have a `Generic[T]` type, it complains about metaclass types * Not sure if the `Array` Generic type is really a good idea? * `compare_X` functions cannot import ProtoModel since it creates a recursive import as other things import from testing.
This pull request introduces 2 alerts when merging efe3e95 into 711f6fe - view on LGTM.com new alerts:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great! (though you're stealing my favorite test bed for learning typing :-) )
periodictable = periodic_table.periodictable | ||
PhysicalConstantsContext = physical_constants.PhysicalConstantsContext | ||
constants = physical_constants.constants | ||
CovalentRadii = covalent_radii.CovalentRadii |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need CovaletRadii
(the class)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This __init__
preserves all the same imports as before, just in a way which makes MyPy happy. Now would be a good time to revisit the imports of singletons while we're in here tinkering if something does not belong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like the class is exposed for the tests and docs. and it's exposed so ppl can create a Context, whereas the physconst context gets its own class b/c it has extra pint intricacies.
so this is fine. future vdwRadii and similar can have their contexts exposed through class while physconst stays with own class. so long as predictable.
cgmp_rules.append('4-' + str(ifr)) | ||
|
||
# * (R5) require total parity consistent among neutral_electrons, chg, and mult | ||
cgmp_range.append(lambda c, fc, m, fm: _parity_ok(zel, c, m)) | ||
cgmp_rules.append('5') | ||
for ifr in range(nfr): | ||
cgmp_range.append(lambda c, fc, m, fm, ifr=ifr: _parity_ok(fzel[ifr], fc[ifr], fm[ifr])) | ||
cgmp_range.append(lambda c, fc, m, fm, ifr=ifr: _parity_ok(fzel[ifr], fc[ifr], fm[ifr])) # type: ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ifr=ifr
and chg=chg
are less a "default" argument than a "capture" argument for lambda instantiation time. But until one can declare for ifr: int in range(nfr):
, I can see where typing would have a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason mypy
has issues with any of the lambda
functions which have a ifr=ifr
like construct in its args. It was able to understand the rest of them. I tried doing a Callable[..., bool]
and Callable[[float, float, float, float], bool]
but it did not like that, nor does the Callable
support kwargs in anyway, so capturing the ifr
and chg
kwargs is impossible with that (and MyPy complains about the signature if you try, and then still cant parse the lambda
).
So the "for now" fix was to ignore it until we we engineer it differently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
your sol'n is fine by me. I'm glad to be aware of the problem.
def to_dict(self) -> Dict: | ||
return {attr: getattr(self, attr) for attr in self._dict_attrs} | ||
|
||
data = Data() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh, that works, too. too bad can't tie back into models.Molecule
, whose fields these are. thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple ways I found to do this, one is wait until 3.8 and we get the TypedDict
class, other places this was a thing I just declared the variable a x: Dict = ...
and moved on. This one I tried the dataclass-like construct here since it was fixed keys and entirely internal as it was an ensured way of enforcing the typing.
Since this I did make this a Class, even internal, if its undesired, I can roll it back to just being a declared Dict[Any, Any]
and having MyPy effectively ignore it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope, it's fine. my comment was conversational.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not too terrible to do overall?
@@ -7,6 +7,8 @@ | |||
from functools import lru_cache | |||
from typing import Union | |||
|
|||
from pint import quantity, UnitRegistry |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cannot import pint here. Far too slow and must be lazily imported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -379,7 +380,7 @@ def test_psi4_qm_3(): | |||
'fragment_separators': [], | |||
} | |||
|
|||
fullans5b = {'efp': {}} | |||
fullans5b: Dict = {'efp': {}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to type tests? Seems annoying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not, these were quick though and I was just ticking items off the list. I can mark mypy to ignore all the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not too terrible to do overall?
The end result is. The amount of tinkering I had to do to learn the oddities (such as just x: Dict =...
a variable) took a long time since there is no real solution there, its more of a " just ignore this, MyPy" order. I also spent a good long while looking at the Array[some_type]
constructs and in the end could not figure out how to tell MyPy that Array
can take a type argument. I can go into more details about what I tried if you want more details though.
One thing I found was that the I could also just leave it and rely on forward ref type hints for free. |
I'm not keen on emigration from |
Eh... maybe? It would take a bit of engineering. I'm fine leaving it as is and suspected the |
This pull request introduces 3 alerts when merging 0b1fc37 into 711f6fe - view on LGTM.com new alerts:
|
This pull request introduces 2 alerts when merging a4750a9 into 711f6fe - view on LGTM.com new alerts:
|
This PR makes Elemental pass MyPy. I've done what I can to make not
use the
type: ignore
comment best I can, but Pydantic and MyPy don'tseem to play nice in many cases.
Outstanding issues I have no good solution to:
lambda
functions with kwargs that have defaults can't be inffered and Callable does not work.constr
from PydanticGeneric[T]
type, it complains about metaclass typesArray
Generic type is really a good idea?compare_X
functions cannot import ProtoModel since it creates a recursive import as other things import from testing.Next major step would be to include the
.pyi
stub files so things like Engine can recognize the typed functions