<div dir="ltr"><br><div class="gmail_extra"><br><br><div class="gmail_quote">On Fri, Jul 19, 2013 at 8:51 AM, Brett Cannon <span dir="ltr"><<a href="mailto:brett@python.org" target="_blank">brett@python.org</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr">If this can lead to the deprecation of .pth files then I support the idea, but I think there are technical issues in terms of implementation that have not been throught through yet. This is going to require an implementation (even if it isn't in importlib._bootstrap but as a subclass of importlib.machinery.FileFinder or something) to see how you plan to make all of this work before this PEP can move beyond this SIG.</div>
</blockquote><div><br></div><div>I'll address any outstanding concerns separately and update the PEP pursuant to outstanding recommendations.  In the meantime, below is a rough draft of the implementation.  We can factor out any artificial complexity I've introduced <wink/>, but it should reflect the approach that came to mind first for me.  You'll notice that I call _find_module() directly (for sys.meta_path traversal).</div>
<div><br></div><div>As already noted, the whole issue of how to populate __indirect__ is tricky, both in how to feed it to the loader and how it should look for namespace packages.  I just stuck a placeholder in there for the moment.  The rest of the implementation is pretty trivial in comparison.  However it does reflect my approach to handling cycles and to aggregating the list for __indirect__.</div>
<div><br></div><div>I'll make it more clear in the PEP that refpath resolution is depth-first--a consequence of doing normal loader lookup.  This means that in the face of a cycle the normal package/module/ns package handling happens rather than acting like there was an empty .ref file (but only if no other path entries in the .ref file pan out first).  Would it be better to treat this case the same as an empty .ref file?</div>
<div><br></div><div>-eric</div><div><br></div><div><br></div><div>diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py</div><div>--- a/Lib/importlib/_bootstrap.py</div><div>+++ b/Lib/importlib/_bootstrap.py</div>
<div>@@ -1338,7 +1338,9 @@</div><div>         sys.path_importer_cache."""</div><div>         if path is None:</div><div>             path = sys.path</div><div>-        loader, namespace_path = cls._get_loader(fullname, path)</div>
<div>+        loader, namespace_path, indirect = cls._get_loader(fullname, path)</div><div>+        # XXX What to do with indirect?</div><div>+        # XXX How to handle __indirect__ for namespace packages?</div><div>         if loader is not None:</div>
<div>             return loader</div><div>         else:</div><div>@@ -1372,6 +1374,7 @@</div><div>         self._path_mtime = -1</div><div>         self._path_cache = set()</div><div>         self._relaxed_path_cache = set()</div>
<div>+        self._visited_refnames = {}</div><div><br></div><div>     def invalidate_caches(self):</div><div>         """Invalidate the directory mtime."""</div><div>@@ -1379,6 +1382,25 @@</div>
<div><br></div><div>     find_module = _find_module_shim</div><div><br></div><div>+    def process_ref_file(self, fullname, refname):</div><div>+        """Return the path specified in a .ref file."""</div>
<div>+        path = []</div><div>+        with open(refname, encoding='UTF-8') as reffile:</div><div>+            for line in reffile:</div><div>+                # XXX Be more lenient on leading/trailing whitespace?</div>
<div>+                line = line.strip()</div><div>+                # ignore comments and blank lines</div><div>+                if line.startswith("#"):</div><div>+                    continue</div><div>+                if not line:</div>
<div>+                    continue</div><div>+                # resolve the target</div><div>+                if not line.startswith("/"):</div><div>+                    line = self.path + "/" + line</div>
<div>+                target = os.path.join(*line.split("/"))</div><div>+                path.append(target)</div><div>+        return path</div><div>+</div><div>     def find_loader(self, fullname):</div><div>         """Try to find a loader for the specified module, or the namespace</div>
<div>         package portions. Returns (loader, list-of-portions)."""</div><div>@@ -1398,6 +1420,29 @@</div><div>         else:</div><div>             cache = self._path_cache</div><div>             cache_module = tail_module</div>
<div>+        # Handle indirection files first.</div><div>+        if fullname not in self._visited_refnames:</div><div>+            indirect = []</div><div>+            visited = set()</div><div>+            self._visited_refnames[fullname] = (indirect, visited)</div>
<div>+        else:</div><div>+            indirect, visited = self._visited_refnames[fullname]</div><div>+        refname = _path_join(self.path, tail_module) + '.ref'</div><div>+        if refname not in visited and refname in cache:</div>
<div>+            visited.add(refname)</div><div>+            indirect.append(refname)</div><div>+            refpath = self.process_ref_file(fullname, refname)</div><div>+            if not refpath:</div><div>+                # An empty ref file is a marker for skipping this path entry.</div>
<div>+                indirect.pop()</div><div>+                return (None, [])</div><div>+            loader = _find_module(fullname, refpath)</div><div>+            if isinstance(loader, NamespaceLoader):</div><div>+                return (None, loader._path, indirect)</div>
<div>+            else:</div><div>+                return (loader, [], indirect)</div><div>+        if indirect and indirect[0] == refname:</div><div>+            del self._visited_refnames[fullname]</div><div>         # Check if the module is the name of a directory (and thus a package).</div>
<div>         if cache_module in cache:</div><div>             base_path = _path_join(self.path, tail_module) </div></div></div></div>