In a previous post in the context of PEP 660 (editable installs), @pf_moore and I were discussing that currently it is difficult to handle namespace packages using a MetaPathFinder
, the main challenge being dynamic path computation.
After testing some PoCs during the weekend, I think I manage to find 2 possible implementations that would work for this scenario:
-
(A) Consider
_NamespacePath
to be an implementation detail and re-implement another “path class” that emulates the behaviour described in 5. The import system — Python 3.12.1 documentation :Namespace packages do not use an ordinary list for their path attribute. They instead use a custom iterable type which will automatically perform a new search for package portions on the next import attempt within that package if the path of their parent package (or sys.path for a top level package) changes.
Comments:
- This approach requires re-implementing a lot of stuff. Even when relying on
pkgutil.extend_path
(as I did in my PoC), a lot of code is still needed to support path entry hooks. - It also requires the
MetaPathFinder
in the beginning ofsys.meta_path
, which is something I would avoid if possible.
- This approach requires re-implementing a lot of stuff. Even when relying on
-
(B) Instead of using a
MetaPathFinder
, create a customPathEntryFinder
and add a “bogus” entry tosys.path
just to trigger it. This way the existingimportlib.machinery.PathFinder
will take care of creating the_NamespacePath
under the hood.
Comments:- This approach seems more straightforward than (A), but it still requires a “bogus” entry to
sys.path
. It would be nice to be able to implement finders for namespaces without this correspondence, to align with the definition in Python docs:Namespace packages may or may not correspond directly to objects on the file system; they may be virtual modules that have no concrete representation.
- This approach seems more straightforward than (A), but it still requires a “bogus” entry to
The code for both PoCs is available at How to implement Namespace packages via `MetaPathFinder`? · GitHub.
I would like to use this discussion thread to collect some feedback and ask the following questions:
- The assumption in (A) is that the expression “a custom iterable type” to refer to any iterable type that fulfils the requirements, not a specific class of the stdlib. Is this a fair assumption or is there any risks that internally Python checks for
_NamespacePath
? - Is
pkgutil
(and particularlypkgutil.extend_path
) still considered first class citizen of the standard library or is it better to avoid it when writing new code? - Is the behaviour observed in (B), by any chance, accidental? For a PEP 660 implementation it would be nice if this behaviour could be considered stable.
- Is there any conceptual problems with approaches (A) and (B)?
- Is there any recommended/other way of implementing this feature? (The main use case I am interested is PEP 660).