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

TYP: setup basic type checking and fix existing type errors #3546

Merged
merged 14 commits into from
Nov 22, 2021

Conversation

neutrinoceros
Copy link
Member

@neutrinoceros neutrinoceros commented Oct 7, 2021

PR Summary

This is a work in progress in support to the ongoing effort to add more type hints to the code base.
Goals:

  • enable continuous type checking with mypy
  • fix any existing errors (type inconsistencies) with minimal changes
  • add optional yt dependencies to Mypy workflow env and repeat (this may be left to a follow up PR)

@neutrinoceros neutrinoceros force-pushed the mypy-setup branch 2 times, most recently from 3eecf38 to 5ef7902 Compare October 7, 2021 09:37
@neutrinoceros neutrinoceros added infrastructure Related to CI, versioning, websites, organizational issues, etc enhancement Making something better labels Oct 7, 2021
@neutrinoceros neutrinoceros force-pushed the mypy-setup branch 7 times, most recently from 76d7437 to b738ccc Compare October 8, 2021 09:30
Copy link
Member

@matthewturk matthewturk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't make it all the way through. I do notice there are a bunch of changes I'm not sure really fit, like some changes to the volume rendering camera, that I think I need to understand why they're bundled here.

pyproject.toml Outdated
@@ -76,3 +76,15 @@ addopts = '''
--ignore='yt/frontends/owls_subfind/tests/test_outputs.py'
--ignore='yt/frontends/ramses/tests/test_outputs.py'
'''

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I can necessarily review these, but they seem okay-ish.

@@ -220,7 +220,7 @@ def _repr_json_(self):


if not os.path.exists(_global_config_file):
cfg = {"yt": {}}
cfg = {"yt": {}} # type: ignore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a sneaking suspicion type: ignore is going to get pretty frustrating...

import weakref
from collections import defaultdict
from contextlib import contextmanager
from typing import List, Tuple, Union
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When are we able to use list and the like with just a __future__ import?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we drop Python 3.6 later this month. And it will come basically free thanks to pyupgrade + pre-commit

@@ -469,9 +470,6 @@ def write_out(self, filename, fields=None, format="%0.16e"):
if fields is None:
fields = sorted(self.field_data.keys())

if self._key_fields is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we getting rid of this? Does adding the type check create a default value?

Copy link
Member Author

@neutrinoceros neutrinoceros Oct 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it doesn't. So here's my reasoning: the YTDataContainer class was never meant for instantiation, but it's useful to inherit from, so it is essentially an abstract class. Many of the class attributes are defined there with placeholder values. Now, if the _key_fields attribute needs to be defined in subclasses, as the check I removed here seems to imply, then it doesn't make much sense to give it Optional[List[str]] as a type when really it needs to be List[str]. Does this make sense ?

@@ -38,7 +39,7 @@ class OctreeSubset(YTSelectionContainer):
_num_ghost_zones = 0
_type_name = "octree_subset"
_skip_add = True
_con_args = ("base_region", "domain", "ds")
_con_args: Tuple[str, ...] = ("base_region", "domain", "ds")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I reading it right that we need to re-type-hint things in subclasses?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For tuples of arbitrary length in classes that have children of their own, yes.
Mypy is pretty strict in type inference so it will consider that tuples of different lengths are incompatible types unless we explicitly mark the length as arbitrary in the parent class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, if these attributes were lists (mutable) instead of tuples (immutable), we could define their types as list[str] once and for all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They aren't mutable, though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which is why mypy is a little harder to satisfy, but I actually prefer keeping it the way it is now too

domain_context_registry = {}


class DomainContext:
class DomainContext(abc.ABC):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What benefit does making this an ABC without defining any required characteristics give us?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see my reply about YTDataContainer for my global reasoning. Here it doesn't seem necessary, but it also doesn't break any test so I don't think it hurts to explicitly declare that this class isn't meant to be instantiated. Should I revert it ?

attrs = None # The attributes of the header
ftype: Optional[str] = None # The name to give to the field type
fname: Optional[str] = None # The name of the file(s)
attrs: Optional[
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to think carefully about this specific type.

action="store_true",
help="Store the configuration in the global configuration file.",
),
_global_local_args = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did "exclusive" go?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a mistake, thanks for catching this !

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted the "fix" now that I saw it breaking CI https://github.com/yt-project/yt/actions/runs/1320143467
I don't understand where this string was supposed to be used. If you know it is actually useful I may need to revert the change here and modify corresponding hints accordingly.

@@ -20,8 +21,8 @@ def _make_io_key(args, *_args, **kwargs):


class BaseIOHandler:
_vector_fields = ()
_dataset_type = None
_vector_fields: Tuple[str, ...] = ()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this always be just a str, or could it contain a ftype, fname tuple?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dunno. Started with the strictest type that I knew was possible and stopped there because I saw no further conflicts reported. It is absolutely fine to extend this definition later if needed.

@neutrinoceros
Copy link
Member Author

You mean the properties ? I think the way it was implemented 7yrs was likely a hack to get Python2 and Python3 compat, and mypy doesn't like it so I simply modernized it.

@neutrinoceros neutrinoceros force-pushed the mypy-setup branch 2 times, most recently from 0f5e3ab to 7c974c7 Compare October 8, 2021 15:49
@neutrinoceros
Copy link
Member Author

neutrinoceros commented Oct 9, 2021

Something happened in the last couple hours that apparently broke the py38 Jenkins job (2739 errors, not just this PR !).
edit: there's the issue to keep track of upstream: sympy/sympy#22241

So I think it's preferable to keep this as a draft for now, because I'd like to collapse my ~30 commits down to 2 or 3 commits via rebasing, but I don't feel comfortable doing it while CI is red, since I'll loose the granularity I need to bisect bugs easily.

@neutrinoceros neutrinoceros force-pushed the mypy-setup branch 2 times, most recently from 84b06a9 to 1a3baf6 Compare October 11, 2021 07:48
@neutrinoceros
Copy link
Member Author

@yt-fido test this please

@neutrinoceros
Copy link
Member Author

yay, this is back to only typing failures (as opposed to red CI), should be able to rebase and open this soon

Copy link
Member

@cphyc cphyc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that's getting me worried is all the # type: ignore in the imports.
Could you elaborate why they are required?

yt/data_objects/static_output.py Outdated Show resolved Hide resolved
yt/frontends/gadget/data_structures.py Outdated Show resolved Hide resolved
yt/frontends/ramses/field_handlers.py Outdated Show resolved Hide resolved
yt/frontends/rockstar/definitions.py Show resolved Hide resolved
yt/frontends/ramses/particle_handlers.py Show resolved Hide resolved
yt/frontends/ramses/field_handlers.py Show resolved Hide resolved
yt/sample_data/api.py Show resolved Hide resolved
yt/utilities/object_registries.py Show resolved Hide resolved
yt/visualization/volume_rendering/camera.py Show resolved Hide resolved
yt/visualization/volume_rendering/scene.py Show resolved Hide resolved
@neutrinoceros
Copy link
Member Author

One thing that's getting me worried is all the # type: ignore in the imports.
Could you elaborate why they are required?

sure. So the reason I'm adding a lot of such comments in places where yt.units is imported from is that mypy complains along the lines of

yt.units has no attribute "..."

and this is inherently because yt.units acts as a wrapper around unyt using from ... import *, which is something that mypy can't resolve easily. Now I think #2597 would have helped with this, but it only ever raised concerns and I was never able to convince anyone else to consider it for inclusion, hence # type: ignore 🤷🏻‍♂️

@neutrinoceros neutrinoceros force-pushed the mypy-setup branch 2 times, most recently from 2e7eeb0 to 96d9135 Compare October 12, 2021 17:14
@neutrinoceros
Copy link
Member Author

almost there. Now praying I didn't break anything in my rebasing. If all goes well I'll remove the draft status later today.

@neutrinoceros neutrinoceros marked this pull request as ready for review October 12, 2021 19:55
@neutrinoceros neutrinoceros marked this pull request as ready for review November 11, 2021 21:50
@neutrinoceros
Copy link
Member Author

Noe to reviewers: I got this mergeable again, but needed to rebase to resolve some conflicts. Everything that's changed since the last time you reviewed is found in small, isolated commits, so I hope it's still manageable.
Also note that, as most sweeping changes, this has the potential of becoming unmanageable on my end very quickly, so I'd appreciate if we can consider moving this forward as soon as possible. Thanks

@matthewturk
Copy link
Member

matthewturk commented Nov 12, 2021 via email

@neutrinoceros
Copy link
Member Author

There are some tiny functional changes though, almost exclusively in input validation routines, where I raised some type degeneracies detected by mypy. Other than that, there should be none

@neutrinoceros
Copy link
Member Author

forced-pushed again to resolve conflicts. Again, the bulk of the diff is contained in the unchanged (conflicts aside) first commit. I didn't need any new commit this time, phew

@matthewturk
Copy link
Member

I personally think that this is good to go.

@neutrinoceros
Copy link
Member Author

@cphyc, sorry I know you're busy, but could you give this the final review since you've already done most of it ?

@cphyc cphyc merged commit f137e1d into yt-project:main Nov 22, 2021
@neutrinoceros neutrinoceros deleted the mypy-setup branch November 22, 2021 14:45
neutrinoceros added a commit to neutrinoceros/yt that referenced this pull request Nov 22, 2021
matthewturk added a commit that referenced this pull request Nov 22, 2021
BUG: hotfix for a functional breakage introduced in #3546
@neutrinoceros neutrinoceros mentioned this pull request Jan 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Making something better infrastructure Related to CI, versioning, websites, organizational issues, etc
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants