Re: Round 2 for "A ModuleSpec Type for the Import System"

Eric Snow <[email protected]>
Newsgroups gmane.comp.python.import
Message-ID <CALFfu7Cq9LX6Y7wRjhewRvGeLd1z7AznwpAGq5=z8L1R_uCspQ@mail.gmail.com>
On Wed, Aug 14, 2013 at 6:27 PM, PJ Eby <[email protected]> wrote:

> On Fri, Aug 9, 2013 at 6:58 PM, Eric Snow <[email protected]>
> wrote:
> > A High-Level View
> > -----------------
> >
> > ...
>
> It would be really helpful if that high-level view were actually
> included, as I'm having a lot of trouble wrapping my head around the
> rest of the spec.  For that matter, some introductory examples to
> contrast "before" and "after" for something that this changes would be
> really nice at about this point.
>

Sounds good.  As to examples, do you mean how you would replace an
implementation of load_module() with one of exec_module()?


> > Path-based attributes:
> >
> > If any of these is set, it indicates that the module is path-based.  For
> > reference, a path entry is a string for a location where the import
> > system will look for modules, e.g. the path entries in ``sys.path`` or a
> > package's ``__path__``).
> >
>
> What does "path-based" actually mean here?  On the one hand, you're
> saying that a path entry is on sys.path or a __path__, but then we're
> using an attribute called "filename".  Shouldn't it be called
> path_entry or subpath, or location or something, if it's not required
> to be a filename?  The overlap between path = sys.path and path =
> filesystem path is way too confusing here.
>

This is a really good point.  I'll clean it up.  I've already changed
"path" to "path_entries" and dropped "filename" in favor of "set_fileattr".
 Furthermore, "file location" is a good substitute for "path" when talking
about files.


> > .. XXX Would a different name be better?  ``path_location``?
>
> Yeah, definitely something other than filename.  ;-)
>
> It might also help to explain that some modules can be loaded by
> reference to a location, e.g. a filesystem path or a URL or something
> of the sort -- having the location lets you load the module, but in
> theory you could load that module under various names.  In contrast,
> non-located modules can't be loaded in this fashion: modules created
> by a meta path loader (such as builtins), or modules dynamically
> created in code.  For these, the name is the only way to access them,
> so they have an "origin" but not a "location".
>

Right.  That's the point of "origin".  It will be up to the loader whether
or not to use "origin" to determine a location, if any.

Also, bear in mind that it's not just exotic locations like URLs that
> aren't filenames.  zipimport uses pseudo-filenames that pretend a
> zipfile is a directory, by prepending the zipfile's filename to a path
> that's within the zipfile.  So, calling this "filename" is *really* a
> bad idea; it's not always a filename for even stdlib importers, let
> alone anything third-party!
>

Yeah, that has always bugged me about "__file__".  The upcoming revision of
the PEP uses the combo of "origin" and "set_fileattr" (a bool) instead of
"filename".


> > ``path``
> >
> > The list of path entries in which to search for submodules if this
> > module is a package.  Otherwise it is ``None``.
>
> This should probably be called submodule_path or
> submodule_search_locations or something, to avoid even *more*
> overloading of the word "path".  ;-)
>

I came to the same conclusion and was planning on using "path_entries".
 However perhaps something even more explicit, like
"submodule_search_locations", would be better. :)

>    If not passed in, ``path`` is set to an empty list if
>
>    ``is_package`` is true.  Then the directory from ``filename`` is
> >    appended to it, if possible.  If ``is_package`` is false, ``path``
> >    stays unset.
>
> How does this interact with namespace packages?  Does it?
>

Namespace packages won't use this method, so nothing will be populated
dynamically.


> > ``load(module=None, *, is_reload=False)``
> >
> > This method captures the current functionality of and requirements on
> > ``Loader.load_module()`` without any semantic changes, except one.
> > Reloading a module when ``exec_module()`` is available actually uses
> > ``module`` rather than ignoring it in favor of the one in
> > ``sys.modules``, as ``Loader.load_module()`` does.
>
> Interesting -- this could possibly be leveraged to implement
> multi-version imports.
>

I'm planning on splitting reload() out from load() so those semantics would
go away.  However, there may be room to still provide the same
functionality.  What would be needed for multi-version imports? (Is that
question opening a can of worms? <wink>)


> > This means that
> > ``is_reload`` could be dropped as a parameter.  However, doing so would
> > mean we could not use ``None`` to indicate that the module should be
> > pulled from ``sys.modules``.
>
> Wait, what?  That doesn't seem true to me: why not just use the module
> or pull one according to whether it's None or not?  What actual
> difference does is_reload really make here?
>

With a separate reload() this point is moot.


> > Regarding the first part of ``load()``, the following describes what
> > happens.
>
> I'm thinking maybe this should be parameterized to allow passing in a
> 'modules' dictionary other than sys.modules.  This would make
> multi-version imports or other "isolated environment" imports more
> viable, and factor out another global element of the import system.
> That way, if you implement an isolated module system, you don't have
> to duplicate or subclass ModuleSpec to perform the same loading
> functionality.
>

Cool idea, but couldn't this wait.  I could totally see this as part of PEP
406 (import engine).


> > Unfortunately, the ability to proxy does not extend to ``id()``
> > comparisons and ``isinstance()`` tests.
>
> Who does id() tests on loaders?


Which is why I'm not going to worry about it too much. :)


>  isinstance() fudging, OTOH, is quite
> doable.  See the ProxyTypes library on PyPI for an example; it's
> 2.x-only but I believe somebody has done a proof-of-concept port (due
> to some __special__ methods being different or missing in 3.x)
>

The current plan is to simply implement __subclasshook__() on the various
importlib ABCs, and perhaps other loaders, to check for methods.  Some of
the ABCs in collections.abc (like Iterator) do this.

> Finders
> > -------
> >
> > Finders will now return ModuleSpec objects when ``find_module()`` is
> > called rather than loaders.  For backward compatility, ``Modulespec``
> > objects proxy the attributes of their ``loader`` attribute.
>
> Has anybody looked at how this change affects pkgutil's (and
> setuptools') generic function-based extensions to PEP 302?  Currently,
> you can register specific loader types with these guys, but that'll
> likely break if importlib is going to start wrapping loaders without
> those tools' knowledge.
>

Good point.  I'll look into this.


>
> May I suggest adding a new finder method, find_module_spec() instead?
> Then, implement it for finders that don't support it by calling
> find_module() and wrapping the loader with a ModuleSpec.  This
> approach would be less disruptive to code that already uses
> find_module and inspects loader types to add extension protocols.
>

I consider this a last resort--i.e. if we can't find a way to make
find_module() work for us in a simple enough way.  I just cringe at the
idea of bolting on another backward-compatibility-induced method,
particularly when it's the OOTDI and the existing name is better fit for
the new functionality than old and re-purposing find_module() is within
reach.

> Adding another similar method to avoid backward-compatibility issues
> > is undersireable if avoidable.  The import APIs have suffered enough,
> > especially considering ``PathEntryFinder.find_loader()`` was just
> > added in Python 3.3.  The approach taken by this PEP should be
> > sufficient to address backward-compatibility issues for
> > ``find_module()``.
>
> I'm not sure I'm following here: are you saying that all PEP 302
> finders implemented by anyone, anywhere, must be changed *in order to
> work at all*, when this lands in a *minor version change*?
>

Existing finders and loaders will continue working as-is.  I've already got
this working in a rough implementation, so it's not that big a stretch.


> > Other Changes
> > -------------
>
> This section doesn't address impact on pkgutil, which makes
> significant use of the PEP 302 API.
>

I'll add that in.  Thanks for bringing it up.  My draft implementation is
passing all the pkgutil tests, but I wouldn't be surprised if I've missed
something here.

Anyway, thanks for the feedback.  I'll post an update to the PEP in the
next day or two.

-eric

_______________________________________________
Import-SIG mailing list
[email protected]
http://mail.python.org/mailman/listinfo/import-sig
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.