-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
GH-119668: expose importlib.machinery.NamespacePath #119669
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
Changes from 12 commits
3c5af73
668b8ff
02ceeb5
67cfa22
29df08f
fb73b9f
0dd0520
06b8906
cd506a4
6f441c4
137652a
ab0930f
d779a7f
be415ab
bb16ae1
9f52661
0494d7b
8ccb7ba
8bc2d6c
6668168
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1009,6 +1009,17 @@ find and load modules. | |
| :exc:`ImportError` is raised. | ||
|
|
||
|
|
||
| .. class:: NamespacePath(name, path, path_finder) | ||
|
|
||
| Represents a namespace package's path. | ||
|
|
||
| It uses the module *name* to find its parent module, and from there it looks | ||
| up the parent's :attr:`module.__path__`. When this changes, the module's own | ||
| path is recomputed, using *path_finder*. The initial value is set to *path*. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we perhaps mention the type of the path finder? (namely using some class Sphinx directive). Btw, when you say that the initial value is set to path it seems that you are talking about the initial value of the path finder. In addition, maybe mention that invalidating the caches forces the path to be recomputed (it might not necessarily be the case that the module parent's path has changed). Finally, could you document the public methods of this new class? For instance there is no insert() method nor remove() as for lists. I think an example of how to use it could be useful though I don't know whether we want to burden the docs even more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I've rewritten the docs to include this.
Using The main improvement I want to get from the PR is the ability to
Similarly, I don't really think that is necessary given the advanced nature of the topic. |
||
|
|
||
| For top-level modules, the parent module's path is :data:`sys.path`. | ||
|
|
||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The attribute is set on |
||
| .. class:: SourceFileLoader(fullname, path) | ||
|
|
||
| A concrete implementation of :class:`importlib.abc.SourceLoader` by | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1086,12 +1086,15 @@ def get_filename(self, fullname): | |
| return self.path | ||
|
|
||
|
|
||
| class _NamespacePath: | ||
| """Represents a namespace package's path. It uses the module name | ||
| to find its parent module, and from there it looks up the parent's | ||
| __path__. When this changes, the module's own path is recomputed, | ||
| using path_finder. For top-level modules, the parent module's path | ||
| is sys.path.""" | ||
| class NamespacePath: | ||
| """Represents a namespace package's path. | ||
|
|
||
| It uses the module *name* to find its parent module, and from there it looks | ||
| up the parent's __path__. When this changes, the module's own path is | ||
| recomputed, using *path_finder*. The initial value is set to *path*. | ||
|
|
||
| For top-level modules, the parent module's path is sys.path. | ||
| """ | ||
|
|
||
| # When invalidate_caches() is called, this epoch is incremented | ||
| # https://bugs.python.org/issue45703 | ||
|
|
@@ -1145,7 +1148,7 @@ def __len__(self): | |
| return len(self._recalculate()) | ||
|
|
||
| def __repr__(self): | ||
| return f'_NamespacePath({self._path!r})' | ||
| return f'NamespacePath({self._path!r})' | ||
|
|
||
| def __contains__(self, item): | ||
| return item in self._recalculate() | ||
|
|
@@ -1154,12 +1157,15 @@ def append(self, item): | |
| self._path.append(item) | ||
|
|
||
|
|
||
| _NamespacePath = NamespacePath # for backwards compatibility | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we make it a deprecated alias? (and emit a warning if we were to access the private name? that could help transition from the private to the public name).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Who's the backwards-compatibility for? People poking at private attributes in private modules?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it hurts, but I also don't think it's necessary.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well... considering it has been designed to support backwards compatibility, I thought it was worth the deprecation warning so that we can eventually remove it. But that could make the code a bit more complex (as we would need some logic in the module's
FFY00 marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| # This class is actually exposed publicly in a namespace package's __loader__ | ||
| # attribute, so it should be available through a non-private name. | ||
| # https://github.com/python/cpython/issues/92054 | ||
| class NamespaceLoader: | ||
| def __init__(self, name, path, path_finder): | ||
| self._path = _NamespacePath(name, path, path_finder) | ||
| self._path = NamespacePath(name, path, path_finder) | ||
|
|
||
| def is_package(self, fullname): | ||
| return True | ||
|
|
@@ -1214,9 +1220,9 @@ def invalidate_caches(): | |
| del sys.path_importer_cache[name] | ||
| elif hasattr(finder, 'invalidate_caches'): | ||
| finder.invalidate_caches() | ||
| # Also invalidate the caches of _NamespacePaths | ||
| # Also invalidate the caches of NamespacePaths | ||
| # https://bugs.python.org/issue45703 | ||
| _NamespacePath._epoch += 1 | ||
| NamespacePath._epoch += 1 | ||
|
|
||
| from importlib.metadata import MetadataPathFinder | ||
| MetadataPathFinder.invalidate_caches() | ||
|
|
@@ -1302,7 +1308,7 @@ def find_spec(cls, fullname, path=None, target=None): | |
| # We found at least one namespace path. Return a spec which | ||
| # can create the namespace package. | ||
| spec.origin = None | ||
| spec.submodule_search_locations = _NamespacePath(fullname, namespace_path, cls._get_spec) | ||
| spec.submodule_search_locations = NamespacePath(fullname, namespace_path, cls._get_spec) | ||
| return spec | ||
| else: | ||
| return None | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Expose :class:`importlib.machinery.NamespacePath`. | ||
|
FFY00 marked this conversation as resolved.
Outdated
|
||
Uh oh!
There was an error while loading. Please reload this page.