-
-
Notifications
You must be signed in to change notification settings - Fork 407
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
Fix mypy type errors by inlining stale stub file and fixing ABC contract implementations. #1491
Fix mypy type errors by inlining stale stub file and fixing ABC contract implementations. #1491
Conversation
It appears that this PR is caught in a bit of a catch-22 situation at the moment. The problematic line is
I'm going to assume that a |
Ah, the catch-22 is somewhat worse than I feared, and is not resolved by a
Would it be okay to add |
fdc79e6
to
4e03986
Compare
Codecov Report
@@ Coverage Diff @@
## master #1491 +/- ##
==========================================
- Coverage 91.97% 91.83% -0.15%
==========================================
Files 105 105
Lines 11239 11300 +61
==========================================
+ Hits 10337 10377 +40
- Misses 902 923 +21
Continue to review full report at Codecov.
|
At the moment it seems that My instinct would be to add |
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.
I don't really know much about typing but it looks good.
My guess is that mean
, sum
... are not recognized because they are implemented in ImplementsDatasetReduce and inherited by the Dataset class which is probably too convoluted for mypy
|
||
@staticmethod | ||
def from_netcdf(filename): | ||
def from_netcdf(filename: str) -> "InferenceData": |
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 sometimes strings are used for the types and sometimes the raw type? Trying to understand how typing works
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.
That's a good question. Type hints with string literals like "InferenceData"
here represent so-called forward references: https://www.python.org/dev/peps/pep-0484/#forward-references
Forward references allow us to refer to types and values that are not currently in scope. On this particular line, the from_netcdf
function is being defined within the InferenceData
class, which means that the InferenceData
class is not fully constructed yet i.e. the InferenceData
name does not yet refer to anything in the current scope. If we attempted to use InferenceData
here, it would cause a NameError
because the symbol is not defined yet. Instead, if we use a forward reference, we signal to mypy that it should look up what that name refers to when type-checking rather than when defining the class, which avoids the problem since by the time type-checking starts, the class has already been fully defined and its name can be resolved just fine.
This is why on line 1334 we can use InferenceData
without the quotes: that line is located outside and after the InferenceData
class definition, so that symbol is defined and present in the scope at the point where it is being used.
For "Literal[True]"
and similar cases, we use quotes for a related but slightly different reason: the import of Literal
on line 42 is only performed if the typing.TYPE_CHECKING
special value is true. However, type hints that are not forward references are always executed -- even when not performing type-checking. This means that if we had used Literal[True]
instead of using the string literal equivalent, executing the Literal[True]
expression in a non-type-checking run would cause a NameError
since Literal
is not imported in that situation.
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.
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.
Thanks for the review @OriolAbril! Do you have any thoughts on including typing_extensions
as a package dependency in a future PR? That would ensure that users of this package are able to type-check with it correctly, instead of needing to make typing_extensions
available in their environment by installing it separately from this package. If you think that's okay, I'm happy to open a PR for it.
My guess is that
mean
,sum
... are not recognized because they are implemented in ImplementsDatasetReduce and inherited by the Dataset class which is probably too convoluted for mypy
Ah yes, I see the problem: this line causes those methods to be defined on Dataset
, but the dynamic method definitions are not visible to mypy
. This is not an actionable mypy error for this package (it's solvable on the xarray
side but not here), and the best approach for this package is to suppress those issues with # type: ignore
for now. I'll open a PR for that as soon as this PR is merged.
|
||
@staticmethod | ||
def from_netcdf(filename): | ||
def from_netcdf(filename: str) -> "InferenceData": |
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.
That's a good question. Type hints with string literals like "InferenceData"
here represent so-called forward references: https://www.python.org/dev/peps/pep-0484/#forward-references
Forward references allow us to refer to types and values that are not currently in scope. On this particular line, the from_netcdf
function is being defined within the InferenceData
class, which means that the InferenceData
class is not fully constructed yet i.e. the InferenceData
name does not yet refer to anything in the current scope. If we attempted to use InferenceData
here, it would cause a NameError
because the symbol is not defined yet. Instead, if we use a forward reference, we signal to mypy that it should look up what that name refers to when type-checking rather than when defining the class, which avoids the problem since by the time type-checking starts, the class has already been fully defined and its name can be resolved just fine.
This is why on line 1334 we can use InferenceData
without the quotes: that line is located outside and after the InferenceData
class definition, so that symbol is defined and present in the scope at the point where it is being used.
For "Literal[True]"
and similar cases, we use quotes for a related but slightly different reason: the import of Literal
on line 42 is only performed if the typing.TYPE_CHECKING
special value is true. However, type hints that are not forward references are always executed -- even when not performing type-checking. This means that if we had used Literal[True]
instead of using the string literal equivalent, executing the Literal[True]
expression in a non-type-checking run would cause a NameError
since Literal
is not imported in that situation.
I did not know about the package until today so everything I know about it comes from a quick search about it. Is this package needed for compatibility with python 3.6? Or also for experimental typing features not yet present in 3.7-8? I would have no problem adding the dependency, but I'm not sure it's worth the effort if next release drops python 3.6 Also, to double check. We can merge this as is without having to wait for typing_extensions dependency or xarray changes right? |
It's not just for 3.6 — in general, any typing-related improvements are added to This PR should be okay to merge as-is (since type-checking with |
Description
This PR is only about improving type hint coverage and quality. It contains no new features and makes no substantive changes to existing functionality, so it should be fully covered by existing tests. This is similar in spirit to #1397 and #1398.
I noticed that a few type errors pointed out by
mypy
(see below) are because of the fact that theinference_data.py
file has an associated type stub fileinference_data.pyi
and unfortunately the two files have drifted from each other significantly. As a result,mypy
was using the type hints from the (outdated) type stub file and complaining about what it was seeing. Sincearviz
is Python 3+ only, I addressed this problem by merging the stub file into the source file itself, preserving all the type hints while deleting the stub file.In addition, the
InferenceData
object only partially implemented theMapping
ABC contract, whichmypy
also did not appreciate. The main issues were thatInferenceData
implemented only a subset of theMapping
methods and had incorrect type signatures for the view-based ones (returning iterables instead of the required view objects).All in all, this PR resolves the following
mypy
errors:Unfortunately, since
mypy
now no longer ignores theinference_data.py
file altogether (as the previously-preferredinference_data.pyi
no longer exists), the followingmypy
errors emerge and exist in code I did not touch. I would love some guidance on this from someone more experienced with thexarray.Dataset
type, since only some of the method accesses on the neighboring lines raisemypy
complaints like this.If anyone has any ideas why
mypy
is able to determine that e.g.xr.Dataset.set_coords
exists, butxr.Dataset.sum
does not, I'd love to hear them and apply the corresponding fixes to this PR or a future one.In case anyone is curious (maybe @ColCarroll?), after merging this PR,
arviz
would be 29 mypy errors away from being able to usetyping_copilot
's ratcheting mechanism for ensuring type coverage remains consistent and continues to improve over time. A number of those issues are solvable simply by applying# type: ignore
-- wherever I am able to determine that it is safe to do so, I intend to open a PR applying the suppression. I could definitely use some help if anyone more knowledgeable about this library is interested in taking a look at some of the trickier cases!Checklist