Re: PEP 547: Could we implement a usable "get_code()" for extension modules?
Brett Cannon <[email protected]> Fri, 12 Jan 2018 17:52:39 +0000
| Newsgroups | gmane.comp.python.import |
|---|---|
| Message-ID | <CAP1=2W68Q0yQ7T_ezp_FApxXVjpDnz4oQs2EbY4wamZrNUbzWQ@mail.gmail.com> |
--===============8474104652976345864== Content-Type: multipart/alternative; boundary="001a11407a12cfd095056297ee2e" --001a11407a12cfd095056297ee2e Content-Type: text/plain; charset="UTF-8" So obviously implementing get_code() for the extension module loader would be great. :) So the question becomes how? On Thu, 11 Jan 2018 at 22:57 Nick Coghlan <[email protected]> wrote: > (cc'ed a couple of folks that I expect will be interested in this > question, but may not be subscribed to import-sig) > > The current version of PEP 547 (supporting the -m switch for extension > modules) works by defining a new optional "exec_in_module" API for > loaders to implement, and then updating runpy._run_module_as_main to > call it. > > However, reviewing Mario Corchero's patches for > https://bugs.python.org/issue9325 (adding "-m" switch support to > assorted modules) has highlighted a potential challenge with that > approach: it turns out the most useful private API in runpy for > emulating the -m switch is "mod_name, mod_spec, code = > runpy._get_module_details(module_name)". > > That means that if we can figure out a way to have > ExtensionFileLoader.get_code() emit a Python code object that > delegates to Py_mod_exec, then we'd be well on our way to supporting > "python -m <extension module>" without making *any changes to runpy* > (or the other modules that are gaining "-m" equivalents). > > If we did decide to go down that path, the main way I could see it > working without any new features in the C interface is to structure > things such that the extension module would still run in its own > namespace, with the interface adaptation code returned from get_code() > (after compilation) looking something like: > > ns = globals() > if ns is not locals(): > raise RuntimeError("Cannot execute extension module > {<interpolated_name>} with separate local namespace") > module = _imp.create_dynamic(<interpolated_spec_details>) > module.__dict__.update(ns) > _imp.exec_dynamic(module) > ns.update(module.__dict__) > > The biggest advantages of this approach are that it would still work > for Cython (and other) modules that defined Py_mod_create, and it > would implicitly interoperate (at least to some degree) with anything > that relied on the "get code and exec it" model of interacting with > Python modules. > > Alternatively, we could instead push the decision on how to handle > this case down to extension module authors as follows: > > 1. Define a new Py_mod_exec_in_namespace slot that accepts a target > namespace as its parameter instead of a pre-existing module > 2. Add a new "_imp.exec_dynamic_in_namespace(spec, namespace)" API > 3. When Py_mod_exec_in_namespace is defined, make the adapter code > look something like: > > ns = globals() > if ns is not locals(): > import collections > ns = collections.ChainMap(locals(), ns) > _imp.exec_dynamic_in_namespace(<interpolated_spec_details>, ns) > > (There are several ways the functionality could be split up between > the generated code and the _imp module, this is just an example that > suggests the idea is technically feasible) > > The nice thing about including the new slot in the design is that it > gives extension modules a way to avoid the overhead of copying > attributes in and out, as would be needed if relying solely on the PEP > 489 APIs. > > Cheers, > Nick. > > P.S. Given these changes we could technically define "get_source()" on > extension modules as well, but that doesn't seem especially useful. > > -- > Nick Coghlan | [email protected] | Brisbane, Australia > _______________________________________________ > Import-SIG mailing list > [email protected] > https://mail.python.org/mailman/listinfo/import-sig > --001a11407a12cfd095056297ee2e Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable <div dir=3D"ltr">So obviously implementing get_code() for the extension mod= ule loader would be great. :) So the question becomes how?<br></div><br><di= v class=3D"gmail_quote"><div dir=3D"ltr">On Thu, 11 Jan 2018 at 22:57 Nick = Coghlan <<a href=3D"mailto:[email protected]">[email protected]</a>>= ; wrote:<br></div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .= 8ex;border-left:1px #ccc solid;padding-left:1ex">(cc'ed a couple of fol= ks that I expect will be interested in this<br> question, but may not be subscribed to import-sig)<br> <br> The current version of PEP 547 (supporting the -m switch for extension<br> modules) works by defining a new optional "exec_in_module" API fo= r<br> loaders to implement, and then updating runpy._run_module_as_main to<br> call it.<br> <br> However, reviewing Mario Corchero's patches for<br> <a href=3D"https://bugs.python.org/issue9325" rel=3D"noreferrer" target=3D"= _blank">https://bugs.python.org/issue9325</a> (adding "-m" switch= support to<br> assorted modules) has highlighted a potential challenge with that<br> approach: it turns out the most useful private API in runpy for<br> emulating the -m switch is "mod_name, mod_spec, code =3D<br> runpy._get_module_details(module_name)".<br> <br> That means that if we can figure out a way to have<br> ExtensionFileLoader.get_code() emit a Python code object that<br> delegates to Py_mod_exec, then we'd be well on our way to supporting<br= > "python -m <extension module>" without making *any changes = to runpy*<br> (or the other modules that are gaining "-m" equivalents).<br> <br> If we did decide to go down that path, the main way I could see it<br> working without any new features in the C interface is to structure<br> things such that the extension module would still run in its own<br> namespace, with the interface adaptation code returned from get_code()<br> (after compilation) looking something like:<br> <br> =C2=A0 =C2=A0 ns =3D globals()<br> =C2=A0 =C2=A0 if ns is not locals():<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 raise RuntimeError("Cannot execute extensi= on module<br> {<interpolated_name>} with separate local namespace")<br> =C2=A0 =C2=A0 module =3D _imp.create_dynamic(<interpolated_spec_details&= gt;)<br> =C2=A0 =C2=A0 module.__dict__.update(ns)<br> =C2=A0 =C2=A0 _imp.exec_dynamic(module)<br> =C2=A0 =C2=A0 ns.update(module.__dict__)<br> <br> The biggest advantages of this approach are that it would still work<br> for Cython (and other) modules that defined Py_mod_create, and it<br> would implicitly interoperate (at least to some degree) with anything<br> that relied on the "get code and exec it" model of interacting wi= th<br> Python modules.<br> <br> Alternatively, we could instead push the decision on how to handle<br> this case down to extension module authors as follows:<br> <br> 1. Define a new Py_mod_exec_in_namespace slot that accepts a target<br> namespace as its parameter instead of a pre-existing module<br> 2. Add a new "_imp.exec_dynamic_in_namespace(spec, namespace)" AP= I<br> 3. When Py_mod_exec_in_namespace is defined, make the adapter code<br> look something like:<br> <br> =C2=A0 =C2=A0 ns =3D globals()<br> =C2=A0 =C2=A0 if ns is not locals():<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 import collections<br> =C2=A0 =C2=A0 =C2=A0 =C2=A0 ns =3D collections.ChainMap(locals(), ns)<br> =C2=A0 =C2=A0 _imp.exec_dynamic_in_namespace(<interpolated_spec_details&= gt;, ns)<br> <br> (There are several ways the functionality could be split up between<br> the generated code and the _imp module, this is just an example that<br> suggests the idea is technically feasible)<br> <br> The nice thing about including the new slot in the design is that it<br> gives extension modules a way to avoid the overhead of copying<br> attributes in and out, as would be needed if relying solely on the PEP<br> 489 APIs.<br> <br> Cheers,<br> Nick.<br> <br> P.S. Given these changes we could technically define "get_source()&quo= t; on<br> extension modules as well, but that doesn't seem especially useful.<br> <br> --<br> Nick Coghlan=C2=A0 =C2=A0|=C2=A0 =C2=A0<a href=3D"mailto:[email protected]= " target=3D"_blank">[email protected]</a>=C2=A0 =C2=A0|=C2=A0 =C2=A0Brisba= ne, Australia<br> _______________________________________________<br> Import-SIG mailing list<br> <a href=3D"mailto:[email protected]" target=3D"_blank">Import-SIG@pytho= n.org</a><br> <a href=3D"https://mail.python.org/mailman/listinfo/import-sig" rel=3D"nore= ferrer" target=3D"_blank">https://mail.python.org/mailman/listinfo/import-s= ig</a><br> </blockquote></div> --001a11407a12cfd095056297ee2e-- --===============8474104652976345864== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Import-SIG mailing list [email protected] https://mail.python.org/mailman/listinfo/import-sig --===============8474104652976345864==--