-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Sort modules in dependency cycles based on dependencies #1530
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
Comments
Test case (reduced from a real-world example): File x.py: class Base:
pass
def foo() -> int:
import y
return y.Sub.attr File y.py: import x
class Sub(x.Base):
attr = 0 This passes when run as
|
FWIW, that test case is not easily solved with the suggested heuristic. The heuristic suggests processing x.py first, y.py second (so that the base class is processed before the subclass), but if you do that it will always give that error. :-( |
Yeah, it wouldn't help here. However, the order of processing would be more stable so at least the error would be less likely to appear after an unrelated change. There are additional things we could do instead, but it's not clear whether they are worth the extra complexity. One approach that I think we've discussed before is to run full type checking first on module top levels and Another idea it to have another light-weight attribute type inference pass that runs before main type inference that can only deal with simple cases, such as these:
Some potential heuristics we might use in the light-weight pass:
The motivation is that if this would infer types for, say, 2/3 of attributes (not a proper estimate) before type checking, the issue would be much less severe and the cases where a type annotation would be required would often be non-trivial so arguably the annotations may improve clarity. |
OK, then I'll go ahead and submit a PR for what I have. The rules I've implemented so far:
I don't yet use information about how the imported variable gets used yet; I'm not sure that information is easily available before the semantic analysis starts. I need a new example though -- something that actually fails due to an import cycle and where the proposed algorithm improves things. |
I ran this over our internal server repo and had to silence a few errors. A light-weight type checker for class attributes would definitely have helped -- all errors were about class attributes with a simple int or str literal as value. So I think I'd like to tackle that next. (The other repo was a red herring.) |
This thwarts certain kinds of errors due to circular dependencies (see #1530).
I'm working on some code to infer types from simple literal assignments here: https://github.com/python/mypy/tree/class_attrs @rwbarton you might want to follow along. There are some tests failing; so far I've identified some failures in the weak-mode tests and some others due to tests for other features relying on undefined types. I've shut up the weak-mode test failures by simply skipping this feature when any weak options are set. I'll shut up the others by making the expressions slightly more complex. We'll see what else fails. UPDATE: More failures due to the rendering of variables with known types being different (no '*' following the name). |
Yes, this looks right to me. |
…forms (#1736) This partially addresses #1530 (but does not fully fix it). The new algorithm always processes x before y, while the old one gives an error when y is processed before x: ``` x.py:4: note: In module imported here: y.py: note: In class "Sub": y.py:3: error: Cannot determine type of 'attr' ``` I am working on additional heuristics in https://github.com/python/mypy/tree/class_attrs.
This thwarts certain kinds of errors due to circular dependencies (see #1530).
This thwarts certain kinds of errors due to circular dependencies (see #1530).
Set the type of `x = 0` and similar in the semantic analyzer. This thwarts certain kinds of errors due to circular dependencies (see #1530). Skip this business if weak_opts is non-empty. Disable lightweight type check if build target < TYPE_CHECK. Only do the lightweight type inferencing thing outside functions.
Topo sort modules within dependency cycles to get a more consistent build ordering and fewer bogus errors due to ordering issues. Specifically, each import would have a priority (high/low).
Topo sort modules in a cycle by considering high-priority dependencies first. We can use low-priority dependencies as secondary sort criteria.
We'd assign a high priority in these cases:
from m import ...
at module top level (not within a function) so that one of the imported names is not a moduleExceptions:
if False:
is ignored and can't affect ordering.Intuitively, we want to make build ordering approximate the order in which modules are initialized at runtime.
Example:
Expected benefits:
A base class will usually be type checked before a derived class (if defined in different modules), so attributes and decorated methods in the base class would have inferred types when type checking the derived class.
Less likely to access an imported type alias that hasn't been processed yet, but only if type alias is imported using
from m import ...
.Order of checking modules within a cycle would be more predictable. This is useful, as ordering changes may result in "Cannot determine type" errors that seem to come out of nowhere.
Cycles caused by mypy-only imports within
if False:
blocks won't affect the ordering of a build.This would partially solve #481. The rules above still don't cover all interesting problems but they should give a clear improvement for a lot of code.
This could perhaps be implemented as a new pass in
process_stale_scc
, where we'd sort the strongly connected component before the main semantic analysis pass.The text was updated successfully, but these errors were encountered: