-
Notifications
You must be signed in to change notification settings - Fork 23
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
Improve handling of forward references #364
Conversation
This comment has been minimized.
This comment has been minimized.
Interesting 🤔 I'm getting these running on
I'll check why the CI is failing tomorrow.. |
Oh, we recently switched off F821 for the whole typeshed repo: https://github.com/python/typeshed/pull/9976/files It was causing too many false positives on That means that the approach in this PR probably won't be viable, unfortunately 😔 |
Hmm that's unfortunate.. Maybe something like this could work ? This should in theory ignore all def handleNodeLoad(self, node, *args, **kwargs):
super().handleNodeLoad(node, *args, **kwargs)
parent = self.getParent(node)
# Remove error message if it's F821 and we're in ClassDef
if self.messages and isinstance(self.messages[-1], UndefinedName) and not isinstance(parent, ast.ClassDef):
self.messages.pop() |
That looks like it might still emit false positives for assignment-via-annotations in the global scope, e.g.: INSTANCE: str
upper = INSTANCE.upper But, I'm not really too familiar with the In general, my approach to this area of the codebase has been "I hate that we monkey-patch pyflakes at all and I'm scared to change this code that I inherited and don't really understand fully" 😬 But, maybe it's time we bit the bullet... |
Though technically allowed, this enforces that subclasses are defined after the classes they inherit from which improves readability.
By default pyflakes requires a future import for annotations in order to process forward references in annotations. This makes it so that the explicit import is not needed, which gets rid of a lot of F821 errors.
e2175e3
to
9eeb64a
Compare
This comment has been minimized.
This comment has been minimized.
OK so looking at the
Most of these seem to be forward refs of classes which we want to forbid anyway. (btw I still don't understand this |
Yeah that's really weird. Probably a bug in our CI setup somewhere. I'll look into it when I have the time. It should be ignored for all .pyi files in this repo: Line 33 in 95d65fe
|
This comment has been minimized.
This comment has been minimized.
Must be something in the test suite triggering it. When I run |
Ok, figured it out, the problem is that in some files we use |
Came up in [#364](#364 (comment)) Plain `--ignore` resets the list of ignores which means that the config file is not taken into account.
I'm open to dropping support for running the plugin with flake8 v4 if it's necessary for this PR. |
This comment has been minimized.
This comment has been minimized.
I'll have a look later to see why exactly it's failing for v4. If it's an easy fix, I'll try to make the PR work for v4 as well. |
My guess is that it's because flake8 v4 pins an old version of pyflakes that doesn't yet know that forward references in annotations are okay sometimes. But that's just a guess, haven't looked into it at all. |
Anyway flake8 v4 is very old at this point, and only really relevant for Python 3.7 (which is nearly end-of-life). So tbh if supporting flake8 v4 involves complicating our code in any way, I'd prefer for us just to drop support for v4 :) |
This hit is problematic. Here's a simplified version of what we're doing in typeshed: class TokenList(list[TokenList]): ... It seems like the monkey-patching we do means that pyflakes-monkeypatched-by-flake8-pyi currently allows recursive class definitions like this. It would be a shame if we regressed on that, as recursive class definitions are needed surprisingly often when writing stubs, and they're a useful feature. So, to be clear, ideally we'd disallow this: class Foo(Bar): ...
class Bar: ... But we'd continue to allow this: class Foo(list[Bar]): ...
class Bar: ... |
I can see how having recursive definitions like class TokenList(list[TokenList]): ... would be be useful but this class Foo(list[Bar]): ...
class Bar: ... just seems like this with extra steps class Foo(Bar): ...
class Bar: ... If we agree that the last one breaks reading order, so does the previous one and I'd disallow both of them.. though maybe I'm missing some context here? |
The issue is that you could come up with cases like this, and I think it would be pretty hard to be confident that we'd secured ourselves against any chance of emitting false-positive errors: class Foo(list[Bar]): ...
class Bar(Foo): ... |
class Foo(list[Bar]): ...
class Bar(Foo): ... What would be the ideal solution in this case? Emitting |
It should be allowed without errors. |
Remember that stub files are never actually executed at runtime; they serve only as "data" for static type checkers and IDEs. As such, forward references are never an error in a In a case like this, there's no way of avoiding some kind of forward reference, and it's a perfectly valid construct in the context of a stub file, so we should ideally make sure that we don't emit any error for that code. |
Thanks for the review! I applied your suggestions and added a changelog entry. Let me know if it's ok :) |
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.
Thank you! Looks like this closes 3 flake8-pyi issues all at once, which is great! (I've updated the PR description.)
This comment was marked as outdated.
This comment was marked as outdated.
@JelleZijlstra or @Akuli, would one of you be able to take a quick look as well? The
|
Also FYI @nipunn1313 -- looks like we might have to update some autogenerated stubs made using mypy-protobuf if this is merged (but then, you did ask for a change like this in #278 😉). Will mypy-protobuf itself need to be updated in order to produce stubs compatible with flake8-pyi following this change, or does it add the |
⚠ Flake8 diff showing the effect of this PR on typeshed: > ./stubs/protobuf/google/protobuf/compiler/plugin_pb2.pyi:128:155: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:255:148: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:336:150: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:657:155: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:919:142: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:936:144: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:1170:165: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/type_pb2.pyi:125:133: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/type_pb2.pyi:212:147: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/service/hlo_pb2.pyi:806:156: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/service/hlo_pb2.pyi:1079:156: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/xla_data_pb2.pyi:851:155: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/xla_data_pb2.pyi:1310:160: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/xla_data_pb2.pyi:1418:138: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/xla_data_pb2.pyi:1576:153: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/framework/api_def_pb2.pyi:50:146: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/framework/dataset_options_pb2.pyi:135:162: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/framework/graph_transfer_info_pb2.pyi:196:159: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/config_pb2.pyi:348:146: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/config_pb2.pyi:375:164: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/config_pb2.pyi:686:182: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/config_pb2.pyi:1086:150: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/core_platform_payloads_pb2.pyi:32:158: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/data_service_pb2.pyi:59:165: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/data_service_pb2.pyi:163:161: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/rewriter_config_pb2.pyi:70:146: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/rewriter_config_pb2.pyi:118:152: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/rewriter_config_pb2.pyi:135:168: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/rewriter_config_pb2.pyi:154:154: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/saved_object_graph_pb2.pyi:475:152: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/saver_pb2.pyi:29:174: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/struct_pb2.pyi:334:159: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tensor_bundle_pb2.pyi:44:157: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/compilation_result_pb2.pyi:36:160: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/optimization_parameters_pb2.pyi:738:158: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/optimization_parameters_pb2.pyi:805:159: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/optimization_parameters_pb2.pyi:846:161: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/topology_pb2.pyi:31:170: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/tpu_embedding_configuration_pb2.pyi:30:153: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/tpu_embedding_configuration_pb2.pyi:51:177: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/verifier_config_pb2.pyi:29:146: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/util/event_pb2.pyi:151:140: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/util/event_pb2.pyi:202:156: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/util/test_log_pb2.pyi:486:157: NQA102 "# noqa: F821" has no matching violations |
Following python/typeshed#10123 and python/typeshed#10124, these are now the only hits I get if I run this PR's version of flake8-pyi on typeshed, after removing the F821 ignores from typeshed's |
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.
It looks good. Just one nit.
Co-authored-by: Akuli <[email protected]>
⚠ Flake8 diff showing the effect of this PR on typeshed: > ./stubs/protobuf/google/protobuf/compiler/plugin_pb2.pyi:128:155: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:255:148: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:336:150: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:657:155: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:919:142: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:936:144: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/descriptor_pb2.pyi:1170:165: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/type_pb2.pyi:125:133: NQA102 "# noqa: F821" has no matching violations
> ./stubs/protobuf/google/protobuf/type_pb2.pyi:212:147: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/service/hlo_pb2.pyi:806:156: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/service/hlo_pb2.pyi:1079:156: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/xla_data_pb2.pyi:851:155: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/xla_data_pb2.pyi:1310:160: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/xla_data_pb2.pyi:1418:138: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/compiler/xla/xla_data_pb2.pyi:1576:153: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/framework/api_def_pb2.pyi:50:146: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/framework/dataset_options_pb2.pyi:135:162: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/framework/graph_transfer_info_pb2.pyi:196:159: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/config_pb2.pyi:348:146: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/config_pb2.pyi:375:164: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/config_pb2.pyi:686:182: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/config_pb2.pyi:1086:150: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/core_platform_payloads_pb2.pyi:32:158: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/data_service_pb2.pyi:59:165: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/data_service_pb2.pyi:163:161: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/rewriter_config_pb2.pyi:70:146: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/rewriter_config_pb2.pyi:118:152: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/rewriter_config_pb2.pyi:135:168: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/rewriter_config_pb2.pyi:154:154: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/saved_object_graph_pb2.pyi:475:152: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/saver_pb2.pyi:29:174: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/struct_pb2.pyi:334:159: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tensor_bundle_pb2.pyi:44:157: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/compilation_result_pb2.pyi:36:160: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/optimization_parameters_pb2.pyi:738:158: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/optimization_parameters_pb2.pyi:805:159: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/optimization_parameters_pb2.pyi:846:161: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/topology_pb2.pyi:31:170: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/tpu_embedding_configuration_pb2.pyi:30:153: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/tpu/tpu_embedding_configuration_pb2.pyi:51:177: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/protobuf/verifier_config_pb2.pyi:29:146: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/util/event_pb2.pyi:151:140: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/util/event_pb2.pyi:202:156: NQA102 "# noqa: F821" has no matching violations
> ./stubs/tensorflow/tensorflow/core/util/test_log_pb2.pyi:486:157: NQA102 "# noqa: F821" has no matching violations |
I don't totally understand the question nor what's going on this diff. Feel free to do whatever it takes to get it to land - and I can come play with it later. mypy-protobuf is used to generate some files in typeshed - so it's possible there might be changes required - but I haven't dug in to verify yet. I don't know how it interacts with flake8 off the top of my head. |
Thanks @nipunn1313! Sorry my message above was unclear. I'll dig in in a bit, and file an issue over at mypy-protobuf if it's required 👍 |
Thanks again @tomasr8 for tackling this! It's a great improvement, I think. |
Thanks to you @AlexWaygood , @JelleZijlstra and @Akuli for all the reviews, suggestions and explanations! :) |
#364 means that pyflakes now doesn't emit nearly the same number of F822 false positives on stub files as it used too. However, the fix was made 'by accident', so we didn't document the change or add tests.
As discussed here
I've checked manually and it finds a couple of issues in typeshed, but I didn't have the time to go through them and verify them.
Fixes #276
Fixes #317
Fixes #278