-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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: support namespace packages when passing files #9742
Conversation
Just so that the logic mirrors closely
b16a68c
to
896709e
Compare
e8ba3f1
to
c3cfef2
Compare
Diff from mypy_primer, showing the effect of this PR on open source code: + poetry/__init__.py:4: error: Cannot determine type of '__path__'
- poetry/__init__.py:4: error: Cannot determine type of '__path__'
+ zilencer/migrations/0001_initial.py: error: Duplicate module named '0001_initial' (also at 'zerver/migrations/0001_initial.py')
- zerver/lib/sqlalchemy_utils.py:20: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:21: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:22: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:23: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:25: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:26: error: unused 'type: ignore' comment
- zerver/views/message_fetch.py:111: error: unused 'type: ignore' comment
- zerver/views/message_fetch.py:704: error: unused 'type: ignore' comment
+ paasta_tools/kubernetes/__init__.py: error: Source file found twice under different module names: 'paasta_tools.kubernetes' and 'kubernetes'
- paasta_tools/iptables.py:23: error: First argument to namedtuple() should be '_RuleBase', not 'Rule'
- paasta_tools/metrics/metastatus_lib.py:650: error: Argument "key" to "sorted" has incompatible type "Callable[[Any], Sequence[Tuple[str, str]]]"; expected "Callable[[Any], SupportsLessThan]"
- paasta_tools/metrics/metastatus_lib.py:650: error: Argument "key" to "sorted" has incompatible type "Callable[[Any], Sequence[Tuple[str, str]]]"; expected "Callable[[_SlaveT], SupportsLessThan]"
- lib/streamlit/__init__.py:52:1: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
+ lib/streamlit/components/v1/components.py:30:1: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
+ src/graphql/__init__.py:280: error: Name 'specified_rules' already defined (by an import)
+ src/graphql/__init__.py:280: error: Name 'validate' already defined (by an import)
+ tests/benchmarks/test_validate_invalid_gql.py:23: error: Module not callable
+ tests/benchmarks/test_validate_gql.py:10: error: Module not callable
+ isort/output.py:596: error: "_LineWithComments" has no attribute "comments"
- isort/output.py:596: error: "_LineWithComments" has no attribute "comments"
- torchvision/__init__.py:13: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
+ torchvision/datasets/samplers/clip_sampler.py:2: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
+ sympy/__init__.py:69: error: Name 'ask' already defined (by an import)
+ sympy/__init__.py:69: error: Name 'refine' already defined (by an import)
+ sympy/__init__.py:105: error: Name 'approximants' already defined (by an import)
+ sympy/__init__.py:105: error: Name 'gruntz' already defined (by an import)
+ sympy/__init__.py:105: error: Name 'series' already defined (by an import)
+ sympy/__init__.py:133: error: Name 'continued_fraction' already defined (by an import)
+ sympy/__init__.py:133: error: Name 'egyptian_fraction' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'combsimp' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'fu' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'gammasimp' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'hyperexpand' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'powsimp' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'radsimp' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'ratsimp' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'simplify' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'sqrtdenest' already defined (by an import)
+ sympy/__init__.py:155: error: Name 'trigsimp' already defined (by an import)
+ sympy/__init__.py:168: error: Name 'decompogen' already defined (by an import)
+ sympy/__init__.py:168: error: Name 'diophantine' already defined (by an import)
+ sympy/__init__.py:168: error: Name 'solveset' already defined (by an import)
+ sympy/__init__.py:226: error: Name 'singularities' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'ccode' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'cxxcode' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'fcode' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'jscode' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'latex' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'mathml' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'pretty' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'preview' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'pycode' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'python' already defined (by an import)
+ sympy/__init__.py:234: error: Name 'rcode' already defined (by an import)
+ sympy/__init__.py:251: error: Name 'plot' already defined (by an import)
+ sympy/__init__.py:251: error: Name 'plot_implicit' already defined (by an import)
+ sympy/__init__.py:251: error: Name 'textplot' already defined (by an import)
+ sympy/solvers/__init__.py:17: error: Name 'diophantine' already defined (by an import)
+ sympy/printing/__init__.py:3: error: Name 'pretty' already defined (by an import)
+ sympy/matrices/__init__.py:24: error: Name 'trace' already defined (by an import)
+ sympy/physics/units/__init__.py:47: error: Cannot determine type of 'velocity'
+ sympy/simplify/hyperexpand_doc.py:14: error: Module not callable
+ sympy/integrals/meijerint_doc.py:19: error: Module not callable
+ sympy/integrals/meijerint_doc.py:30: error: Module not callable
+ sympy/integrals/meijerint_doc.py:31: error: Module not callable
+ src/prefect/__init__.py:16: error: Name 'case' already defined (by an import)
|
Apparently this is something mypy is sensitive to. Thanks mypy primer!
7b3d52a
to
efbb522
Compare
This means we could accidentally fallback to calling a FileSystemCache method if stuff gets moved around.
Diff from mypy_primer, showing the effect of this PR on open source code: + paasta_tools/kubernetes/__init__.py: error: Source file found twice under different module names: 'paasta_tools.kubernetes' and 'kubernetes'
- paasta_tools/iptables.py:23: error: First argument to namedtuple() should be '_RuleBase', not 'Rule'
- paasta_tools/metrics/metastatus_lib.py:650: error: Argument "key" to "sorted" has incompatible type "Callable[[Any], Sequence[Tuple[str, str]]]"; expected "Callable[[Any], SupportsLessThan]"
- paasta_tools/metrics/metastatus_lib.py:650: error: Argument "key" to "sorted" has incompatible type "Callable[[Any], Sequence[Tuple[str, str]]]"; expected "Callable[[_SlaveT], SupportsLessThan]"
+ zilencer/migrations/0001_initial.py: error: Duplicate module named '0001_initial' (also at 'zerver/migrations/0001_initial.py')
- zerver/lib/sqlalchemy_utils.py:20: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:21: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:22: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:23: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:25: error: unused 'type: ignore' comment
- zerver/lib/sqlalchemy_utils.py:26: error: unused 'type: ignore' comment
- zerver/views/message_fetch.py:111: error: unused 'type: ignore' comment
- zerver/views/message_fetch.py:704: error: unused 'type: ignore' comment
|
mypy_primer points out two undesirable effects: 1) scripts causing search path confusion 2) scripts with the same names causing issues (e.g. migrations in zulip)
efbb522
to
739f067
Compare
Summary of the (now fixed) mypy_primer issues:
|
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 looks awesome! I especially like the unit tests -- they both test the behavior in lot of detail and in a clean way, and also act as a nice specification. They are also written very clearly. I really enjoyed reviewing this.
Left some minor comments. The only thing that may be significant is the behavior with packages spanning multiple directories. Feel free to merge once you've addressed all the comments.
mypy/find_sources.py
Outdated
"""Determines sort order for directory listing. | ||
|
||
The desirable property is foo < foo.pyi < foo.py. | ||
The desirable propertes are: |
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.
Typo: propertes -> properties
return root | ||
|
||
|
||
def get_explicit_package_bases(options: Options) -> Optional[List[str]]: |
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.
Add a short summary about the purpose of this.
mypy/test/test_find_sources.py
Outdated
|
||
def normalise_build_source_list(sources: List[BuildSource]) -> List[Tuple[str, Optional[str]]]: | ||
return sorted( | ||
(s.module, normalise_path(s.base_dir) if s.base_dir is not None else None) |
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.
Add parentheses around the second tuple item to make precedence explicit?
@@ -88,6 +88,7 @@ def __init__(self) -> None: | |||
self.follow_imports_for_stubs = False | |||
# PEP 420 namespace packages | |||
self.namespace_packages = False | |||
self.explicit_package_bases = False |
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.
Add comment that explains in some detail what this means.
@@ -88,6 +88,7 @@ def __init__(self) -> None: | |||
self.follow_imports_for_stubs = False | |||
# PEP 420 namespace packages | |||
self.namespace_packages = False |
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.
Maybe add more detail to the comment. At least mention that __init__.py
files are no longer necessary to define a package, and package can span multiple directories.
|
||
def test_crawl_namespace(self) -> None: | ||
options = Options() | ||
options.namespace_packages = True |
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.
What about testing namespace packages that span multiple directories (submodules in multiple MYPYPATH entries)?
Thanks for the review! Implemented all the suggestions. I still owe documentation updates, but I'll do that in a separate PR. |
This should cover the current state on master, as implemented across python#9742, python#9683, python#9632, python#9616, python#9614, etc. This will need to be changed if we can make `--namespace-packages` the default (python#9636).
This should cover the current state on master, as previously discussed / implemented across python#9742, python#9683, python#9632, python#9616, python#9614, etc. This will need to be changed if we can make `--namespace-packages` the default (python#9636). I haven't documented some of the finer points of the changes, since it felt like an inappropriate level of detail (e.g. using absolute paths when crawling, how directories with invalid package names affect crawling, etc)
This should cover the current state on master, as previously discussed / implemented across python#9742, python#9683, python#9632, python#9616, python#9614, etc. This will need to be changed if we can make `--namespace-packages` the default (python#9636). I haven't documented some of the finer points of the changes, since it felt like an inappropriate level of detail (e.g. using absolute paths when crawling, how directories with invalid package names affect crawling, etc)
This should cover the current state on master, as previously discussed / implemented across #9742, #9683, #9632, #9616, #9614, etc. This will need to be changed if we can make `--namespace-packages` the default (#9636). I haven't documented some of the finer points of the changes, since it felt like an inappropriate level of detail (e.g. using absolute paths when crawling, how directories with invalid package names affect crawling, etc) Co-authored-by: hauntsaninja <>
This should cover the current state on master, as previously discussed / implemented across #9742, #9683, #9632, #9616, #9614, etc. This will need to be changed if we can make `--namespace-packages` the default (#9636). I haven't documented some of the finer points of the changes, since it felt like an inappropriate level of detail (e.g. using absolute paths when crawling, how directories with invalid package names affect crawling, etc) Co-authored-by: hauntsaninja <>
@hauntsaninja
So that means that in a scenario where there is a namespace package at the root, to be able to correctly run Why was |
You might be confusing the directory the package is in with the package directory? Also mypy automatically adds the current directory as an explicit package base, which might be another piece you're missing. Automatically treating cwd as a base is why the common single top level namespace package scenario works. Please read https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules which has examples. The behaviour was previously completely broken when passing If you're still stuck, I recommend posting on gitter.im or making an issue. |
@hauntsaninja thanks for the response. I am not stuck. It just seems that there is no way out of needing to define the base folder for all projects in the repo, which are using the same mypy.ini. I don't think I am confusing directory and package. Yes, there is a directory that includes the packages and other files like setup.py. This is the base directory. And, because the top level package is a namespace package, the first folder inside it doesn't have Yes, maybe it was working before because the cwd was always the directory containing the package. But now, that is not true, especially since we mostly run mypy from the Python extension in VSCode and the cloned repository has several other python projects. Anyways, I wonder, for instance, how pylint discover the correct base folder and the fully qualified name for the packages when checking a single file and if mypy could do the same. |
From what you're describing, it sounds like using The situation in which you'd need to list things is if you had a layout with something like:
|
That is pretty much what we have. When I run Do you think this is a bug worthy of an issue or just expected behavior? |
I recommend creating an issue that has a |
@niander have you resolved your issue? I have exactly the same problem in monorepo setup when using
will fail due to @hauntsaninja |
As I said in my previous comment, this PR is not the place to discuss issues or for user support. I'll be locking this thread.
|
This is the successor to #9632. Things should basically be as discussed in that PR. Since #9616 is merged, this should now resolve #5759.
We leave the Bazel integration with
--package-root
almost entirely untouched, save for a) one change that's a bugfix / doesn't affect the core of what--package-root
is doing, b) another drive by bugfix that's not related to this PR.Change a) fixes the package root
__init__.py
hackery when passed absolute paths. Change b) fixes the validation logic for package roots above the current directory; it was broken if you passed..
as a package rootSince we're leaving
--package-root
alone for now, I named the new flag--explicit-package-base
to try and avoid confusion. Doing so also matches the language used by BuildSource a little better.The new logic is summarised in the docstring of
SourceFinder.crawl_up
.Some commentary:
find_sources_in_dir
to callcrawl_up
directly to construct the BuildSource. This helps codify the fact that files discovered will use the same module names as if you passed them directly.__init__.py
to preserve current unit tests where we raise InvalidSourceList.cc @JukkaL