<p dir="ltr">On 9 Oct 2014 13:14, "Petr Viktorin" <<a href="mailto:encukou@gmail.com">encukou@gmail.com</a>> wrote:<br>
><br>
> On Thu, Oct 9, 2014 at 5:24 AM, Nathaniel Smith <<a href="mailto:njs@pobox.com">njs@pobox.com</a>> wrote:<br>
> [...]<br>
> >> 1) Analogy with __new__: For packages only, if there's a __new__.py, that gets executed first. If it "returns" (not sure how that was defined) an instance of a subclass of ModuleType, that instance is used to run __init__.py instead of a normal module instance.<br>
> ><br>
> > This is very similar to the current approach of having __init__.py<br>
> > reassign sys.modules[__name__]. The advantages are:<br>
> > - It gives a way to enforce the rule that you have to do this<br>
> > assignment as the very first thing inside your module, before allowing<br>
> > arbitrary code to run (e.g. by importing other modules which might<br>
> > recursively import your module in turn, and access<br>
> > sys.modules[__name__] before you've modified it).<br>
> > - Because __new__.py would run *before* the It avoids the headache of<br>
> > having to juggle two module objects, one of whose __dict__'s is<br>
> > already being used as the execution environment for the code that is<br>
> > trying to do the switcheroo.<br>
> ><br>
> > But:<br>
> > - It's a pretty complicated way to accomplish the stated goals.<br>
><br>
> As in, a non-obvious way to do a non-obvious thing, from the user's<br>
> point of view?<br>
> On the implementation side it doesn't strike me as particularly<br>
> complicated, am I wrong?</p>
<p dir="ltr">Mostly I mean that it requires a much heavier-weight design discussion. It would add qualitatively new concepts to the language, require its own PEP, we'd have to have the debate about whether the cost of extra stat calls on every import was worth it, we'd have to figure out all the fiddly details about how exactly it should work (are variables defined in __new__.py visible in __init__.py?), etc.</p>
<p dir="ltr">Usually one requirement in such debates is to demonstrate that there is no acceptable lighter-weight alternative. I think that's a hard argument to make here. The __class__ assignment approach requires minimal changes to python itself, and works pretty much just as well as the __new__.py approach; maybe better in some ways (as per below). So it's hard to justify the effort it would require to get consensus.</p>
<p dir="ltr">> > - The restriction to packages is unfortunate.<br>
><br>
> True, but it seems to me that you'd usually want it for the<br>
> __init__.py – after all if you need to do `from mypackage import<br>
> submodule` anyway, and submodule isn't a package itself, you can<br>
> usually can just make `submodule` a class directly. Or have the<br>
> top-level package define an import hook.<br>
> Seems to me that this would really only hurt single-file top-level<br>
> modules, but those are easily converted to a package.</p>
<p dir="ltr">Yeah, this is not a showstopper, but given that we have an alternative that *doesn't* have such fiddly restrictions, it's worth mentioning.</p>
<p dir="ltr">> > - The backcompat story is terrible -- faking __new__.py support in old<br>
> > versions of python would be really difficult, and the main reason I<br>
> > care about this stuff in the first place is because I want to be able<br>
> > to e.g. deprecate module attributes that are in the public API of old,<br>
> > widely-used packages. It will be many years before such packages can<br>
> > require 3.5.<br>
><br>
> That seems like a terrible reason to me – if it should work nicely on<br>
> older Pythons, it means a 3rd party module would be enough. Why bother<br>
> adding it to the language?</p>
<p dir="ltr">I posted an implementation that works fine on older pythons. If you look at the code though then I think you will agree that the way it works is OK for a backcompat shim that only has to target a specific range of python versions, but... it is also something that we should feel embarrassed to recommend as a long-term solution.</p>
<p dir="ltr">> Valid reasons would be to make it easier for alternative interpreters,<br>
> or to catch edge cases (e.g. make it work nicely with custom<br>
> importers, zip files, etc). But at that point, we can just do it the<br>
> "right way"*, have the backcompat story to a third-party shim.<br>
><br>
> * i.e. "not paying attention to older Pythons" – I'm not saying<br>
> __new__.py is necessarily the right way, I'm criticizing this reason<br>
> against it</p>
<p dir="ltr">My point is just that it matters whether a backcompat shim is doable. Probably there is some way to implement a backcompat shim for __new__.py, but I don't immediately know how to do it. (E.g., how do you read a file that's a sibling of the current source file in a compatible way across python versions, taking into account zipimport etc.? Is it possible to get the right namespace semantics when executing __new__.py *after* the package module object has been created? Of course it depends on what those semantics are, which is currently underspecified....)</p>
<p dir="ltr">And again, this is to be compared to the __class__ assignment approach, where we know very well that a backcompat shim is doable because it is done :-).</p>
<p dir="ltr">-n</p>