<div class="gmail_quote">On Sun, Apr 22, 2012 at 1:26 AM, Nick Coghlan <span dir="ltr">&lt;<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="im">On Sun, Apr 22, 2012 at 11:06 AM, Eric V. Smith &lt;<a href="mailto:eric@trueblade.com">eric@trueblade.com</a>&gt; wrote:<br>
&gt; Furthermore, given how __path__ is built, by one-at-a-time remembering<br>
&gt; the path entries that have a foo directory but no foo/__init__.py, I&#39;m<br>
&gt; not sure how you&#39;d turn that into some auto-updating iterable.<br>
<br>
</div>You just have to remember all your namespace packages somewhere and<br>
then use a list subclass that triggers a rescan whenever the contents<br>
change.<br></blockquote><div><br></div><div>Not necessary if you set __path__ to an iterable that caches a tuple of its parent package __path__ (or sys.path), and compares that against the current value before iterating.  If it&#39;s changed, you walk the parent and rescan, otherwise iterate over your cached value.  I posted a sketch to Python-Dev the first time 402 discussion happened there.</div>
<div><br></div><div>The consequences of making namespace package __path__ iterable are less problematic, I believe, than changing the type of sys.path: almost no code manipulates __path__ as anything but an iterable, and code that does is broken for namespace packages anyway, because accessing specific offsets won&#39;t give you what you think you&#39;re looking for.  So you get noisy breakage instead of quiet breakage in such cases (as would happen with using lists for __path__).</div>
<div><br></div><div>If for some reason you want to explicitly change a namespace package&#39;s __path__, you could just reset __path__ to list(__path__), and proceed from there -- which is the recommended idiom for using extend_path, anyway.</div>
<div> </div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Personally, I&#39;m happier with the basic behaviour being that<br>
dynamically updating sys.path while the program is running can be a<br>
bit hit-or-miss in terms of what recognises the change.<br></blockquote><div><br></div><div>pkg_resources supports dynamic updating today, so the idea here was to make it possible to do away with that.  (It only supports updating if it&#39;s the one doing the sys.path manipulation, however.)</div>
<div><br></div><div>I think there should be *some* blessed API(s) to force the updating, though, even if it&#39;s not automatic or dynamic.  extend_path() really isn&#39;t the right tool for the job.</div><div><br></div><div>
The main argument in favor of automatic updating is that it more closely matches naive expectations of users coming from other languages.  (Although to be honest I&#39;m not 100% certain that those other languages actually do change their lookups that dynamically.)</div>
<div><br></div><div>Anyway, the sketch (using PEP 402&#39;s importer protocol; not updated for 420) was something like:</div><div><br></div><div><div><font face="&#39;courier new&#39;, monospace">class VirtualPath:</font></div>
<div><font face="&#39;courier new&#39;, monospace">    __slots__ = (&#39;__name__&#39;, &#39;_parent&#39;, &#39;_last_seen&#39;, &#39;_path&#39;)</font></div><div><font face="&#39;courier new&#39;, monospace"><br></font></div>
<div><font face="&#39;courier new&#39;, monospace">    def __init__(self, name, parent_path):</font></div><div><font face="&#39;courier new&#39;, monospace">        self.__name__ = name</font></div><div><font face="&#39;courier new&#39;, monospace">        self._parent = parent_path</font></div>
<div><font face="&#39;courier new&#39;, monospace">        self._path = self._last_seen = ()</font></div><div><font face="&#39;courier new&#39;, monospace"><br></font></div><div><font face="&#39;courier new&#39;, monospace">    def _fail(self, *args, **kw):</font></div>
<div><font face="&#39;courier new&#39;, monospace">        raise TypeError(self.__name__+&quot; is a virtual package&quot;)</font></div><div><font face="&#39;courier new&#39;, monospace"><br></font></div><div><font face="&#39;courier new&#39;, monospace">    __getitem__ = __setitem__ = __delitem__ = append = extend = insert = _fail</font></div>
<div><font face="&#39;courier new&#39;, monospace"><br></font></div><div><font face="&#39;courier new&#39;, monospace">    def _calculate(self):</font></div><div><font face="&#39;courier new&#39;, monospace">        with _ImportLockContext():</font></div>
<div><font face="&#39;courier new&#39;, monospace">            parent = tuple(self._parent)</font></div><div><font face="&#39;courier new&#39;, monospace">            if parent != self._last_seen:</font></div><div><font face="&#39;courier new&#39;, monospace">                items = []</font></div>
<div><font face="&#39;courier new&#39;, monospace">                name = self.__name__</font></div><div><font face="&#39;courier new&#39;, monospace">                for entry in parent:</font></div><div><font face="&#39;courier new&#39;, monospace">                    importer = get_importer(entry)</font></div>
<div><font face="&#39;courier new&#39;, monospace">                    if hasattr(importer, &#39;get_subpath&#39;):</font></div><div><font face="&#39;courier new&#39;, monospace">                        item = importer.get_subpath(name)</font></div>
<div><font face="&#39;courier new&#39;, monospace">                        if item is not None:</font></div><div><font face="&#39;courier new&#39;, monospace">                            items.append(item)</font></div><div>
<font face="&#39;courier new&#39;, monospace">                self._last_seen = parent</font></div><div><font face="&#39;courier new&#39;, monospace">                self._path = tuple(items)</font></div><div><font face="&#39;courier new&#39;, monospace">            return self._path</font></div>
<div><font face="&#39;courier new&#39;, monospace"><br></font></div><div><font face="&#39;courier new&#39;, monospace">    def __iter__(self):</font></div><div><font face="&#39;courier new&#39;, monospace">        return iter(self._calculate())</font></div>
<div><font face="&#39;courier new&#39;, monospace"><br></font></div><div><font face="&#39;courier new&#39;, monospace">    def __len__(self):</font></div><div><font face="&#39;courier new&#39;, monospace">        return len(self._calculate())</font></div>
<div><font face="&#39;courier new&#39;, monospace"><br></font></div><div><font face="&#39;courier new&#39;, monospace">    def __repr__(self):</font></div><div><font face="&#39;courier new&#39;, monospace">        return &quot;VirtualPath&quot; + repr((self.__name__, self._parent))</font></div>
<div><font face="&#39;courier new&#39;, monospace"><br></font></div><div><font face="&#39;courier new&#39;, monospace">    def __contains__(self, item):</font></div><div><font face="&#39;courier new&#39;, monospace">        return item in self._calculate()</font></div>
</div><div><br></div><div><br></div><div>Using these objects in place of lists for __path__ objects would then do the trick.</div><div><br></div><div>(And of course, you&#39;d want to change &quot;Virtual&quot; to &quot;Namespace&quot; throughout, I suppose.  ;-) )</div>
<div><br></div></div>