<div class="gmail_quote">On Thu, Mar 15, 2012 at 1:50 PM, Guido van Rossum <span dir="ltr">&lt;<a href="mailto:guido@python.org">guido@python.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div></div><div class="h5">How would you implement that anyway? </div></div></blockquote><div><br></div><div>From the PEP:</div><div><br></div><div><span style="font-family:Arial,Verdana,Geneva,&#39;Bitstream Vera Sans&#39;,Helvetica,sans-serif;font-size:15px;line-height:21px;background-color:rgb(255,255,255)">&quot;&quot;&quot;If the parent package does not exist, or exists but lacks a </span><tt class="docutils literal" style="background-color:rgb(255,255,255)">__path__</tt><span style="font-family:Arial,Verdana,Geneva,&#39;Bitstream Vera Sans&#39;,Helvetica,sans-serif;font-size:15px;line-height:21px;background-color:rgb(255,255,255)"> attribute, an attempt is first made to create a &quot;virtual path&quot; for the parent package (following the algorithm described in the section on </span><a class="reference internal" href="http://www.python.org/dev/peps/pep-0402/#virtual-paths" style="border-bottom-width:1px;border-bottom-style:dashed;border-bottom-color:rgb(204,204,204);color:rgb(85,26,139);text-decoration:none;font-family:Arial,Verdana,Geneva,&#39;Bitstream Vera Sans&#39;,Helvetica,sans-serif;font-size:15px;line-height:21px;background-color:rgb(255,255,255)">virtual paths</a><span style="font-family:Arial,Verdana,Geneva,&#39;Bitstream Vera Sans&#39;,Helvetica,sans-serif;font-size:15px;line-height:21px;background-color:rgb(255,255,255)">, below).</span>&quot;&quot;&quot;<br>
</div><div><br></div><div>This is actually a pretty straightforward change to the import process; I drafted a patch for importlib at one point, and somebody else created another.</div><div><br></div><div>(The main difference from the new proposal is that you do have to go back over the path list a second time in the event the parent package isn&#39;t found; but there&#39;s no reason why the protocols in the PEP wouldn&#39;t allow you to build and cache a virtual path while doing the first search, if you&#39;re worried about the performance.)</div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5">The import logic always tries to<br></div></div>
import the parent module before importing the child module. So the<br>
import attempt for &quot;foo&quot; has no idea whether it is imported as *part*<br>
of &quot;import foo.bar&quot;, or as plain &quot;import foo&quot;, or perhaps as part of<br>
&quot;from foo import bar&quot;.<br></blockquote><div><br></div><div>Actually, this isn&#39;t entirely true.   __import__ is called with &#39;foo.bar&#39; when you import foo.bar.  In importlib, it recursively invokes __import__ with parent portions, and in import.c, it loops left to right for the parents.  Either way, it knows the difference throughout the process, and it&#39;s fairly straightforward to backtrack and create the parent modules when the submodule import succeeds.</div>
<div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">It would also be odd to find that<br>
<br>
  import foo<br>
  import foo.bar<br>
<br>
would fail, whereas<br>
<br>
  import foo.bar<br>
  import foo<br>
<br>
would succeed, because as a side effect of &quot;import foo.bar&quot;, a module<br>
object for foo is created and inserted as sys.modules[&#39;foo&#39;].<br></blockquote><div><br></div><div>Assuming we know that the foo subdirectories actually exist, the ImportError would simply say, &quot;Can&#39;t import namespace package &#39;foo&#39; before one of its modules or subpackages are imported&quot;.</div>
<div><br></div><div>Granted, that does seem a bit crufty.  I erred this direction in order to avoid pitchforks coming from the backward-compatibility direction, on account of the ease with which something can get messed up at a distance without this condition, and in a way that may be hard to identify, if a piece of code is using package presence to control optional features.</div>
<div><br></div><div>IOW, it&#39;s not like either proposal results in a perfect clean result for everybody.  It&#39;s a choice of which group to upset, where one group is developers fiddling with their import order (and getting an error message that says how to fix it), and the other group is people whose code suddenly crashes or behaves differently because somebody created a directory somewhere they shouldn&#39;t have (and which they might not be able to delete or remove from sys.path for one reason or another), and which was there and worked okay before until they installed a new version of the application that&#39;s built on a new version of Python.</div>
<div><br></div><div>That is, the backward compatibility problem can break an app in the field that worked perfectly in the developer&#39;s testing, and only fails when it gets to the end user who has no way of even knowing it could be a problem.</div>
<div><br></div><div>It&#39;s up to you decide which of those groups&#39; pitchforks to face; I just want to be clear about why the tradeoff was proposed the way it was.  It&#39;s not that the backward compatibilty problem harms a lot of people, so much as that when it harms them, it can harm them a lot (e.g. crashing), and at *runtime*, compared to tweaking your import sequence during *development* and getting a clear and immediate &quot;don&#39;t do that.&quot;</div>
<div><br></div><div>Why crashing?  Because &quot;try: import json&quot; will succeed, and then the app does json.foobar() and boom, an unexpected AttributeError.  Far fetched?  Perhaps, but the worst runtime import ordering problem I can think of is if you have a bad import that&#39;s working due to a global import ordering that&#39;s determined at runtime because of plugin loading.  But if you have that problem, you correct the bad import in the plugin and it can never happen again.</div>
<div><br></div><div>Granted, directory naming conflicts can *also* be fixed by changing your imports; you can (and should) &quot;try: from json import foobar&quot; instead.  But there isn&#39;t any way for us to give the user or developer an error message that *tells* them that, or even clues them in as to why the json module on that user&#39;s machine seems to be borked whenever they run the app from a certain directory...</div>
<div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Finally, in your example, why on earth would unittest/mock/ exist as<br>
an empty directory???<br></blockquote><div><br></div><div>It&#39;s definitely true that the impact is limited in scope; the things most likely to be affected are generically-named top-level packages like json, email, text, xml, html, etc., that could collide with other directories lying around, AND it&#39;s a package name you try/import to test for the presence of.</div>
<div><br></div><div>As I said though, it&#39;s just that when it happens, it can happen to an *end user*, whereas import order crankiness can essentially only happen during actual coding.  Also, nobody&#39;s come up with examples of breakage caused by trying to import the namespace, on account of there aren&#39;t many use cases for importing an empty namespace, vs use cases for having a &#39;json&#39; directory or some such.  ;-)</div>
<div><br></div><div>All this being said, if you&#39;re happy with the tradeoff, I&#39;m happy with the tradeoff.  I&#39;m not the one they&#39;re gonna come after with the pitchforks.  ;-)</div><div><br></div></div>