<div dir="ltr"><br><br><div class="gmail_quote">On Sat Feb 21 2015 at 7:27:19 AM Nick Coghlan <<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On 21 February 2015 at 00:56, Petr Viktorin <<a href="mailto:encukou@gmail.com" target="_blank">encukou@gmail.com</a>> wrote:<br>
> Hello list,<br>
><br>
> I have taken Nick's challenge of extension module loading.<br>
<br>
Thanks for tackling this!<br>
<br>
> I've read some of the relevant discussions, and bounced my ideas off Nick<br>
> to see if I missed anything important.<br>
><br>
> The main idea I realized, which was not obvious from the discussion,<br>
> was that in addition to playing well with PEP 451 (ModuleSpec) and supporting<br>
> subinterpreters and multiple Py_Initialize/Py_Finalize cycles,<br>
> Nick's Create/Exec proposal allows executing the module in a "foreign",<br>
> externally created module object. The main use case for that would be runpy and<br>
> __main__, but lazy-loading mechanisms were mentioned that would benefit as well.<br>
<br>
For everyone else's reference: this actually came up in Petr's earlier<br>
off-list discussions with me, when I realised I'd had the "running<br>
extension modules as __main__" use case in mind myself, but never<br>
actually written that notion down anywhere.<br>
<br>
It's the one capability of PyModuleExec_* that simply doesn't exist today.<br>
<br>
> As I was writing this down, I realized that once pre-created modules are<br>
> allowed, it makes no sense to insist that they actually are module<br>
> instances -- PyModule_Type provides little functionality above a plain object<br>
> subclass. I'm not sure there are any use cases for this, but I don't see a<br>
> reason to limit things artificially. Any bugs caused by allowing<br>
> non-ModuleType modules are unlikely to be subtle, unless the custom object<br>
> passes the "asked for it" line.<br>
><br>
> Comments appreciated.<br>
<br>
This generally looks good to me. Some more specific feedback inline below.<br>
<br>
> PEP: XXX<br>
> Title: Redesigning extension module loading<br>
<br>
For the BDFL-Delegate question: Brett would you be happy tackling this one?<br></blockquote><div><br></div><div>I don't know if "be happy tackling" is the right way to phrase it. =)</div><div><br></div><div>Honestly I don't think I'm the best person for this PEP. My experience with the C API and extension modules is rather limited and so I don't think I will be able to properly think of the impact on more complex, sane extension module use cases.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
> Motivation<br>
> ==========<br>
><br>
> Python modules and extension modules are not being set up in the same way.<br>
> For Python modules, the module is created and set up first, then the module<br>
> code is being executed (PEP 302).<br>
> A ModuleSpec object (PEP 451) is used to hole information about the module,<br>
> and pased to the relevant hooks.<br>
<br>
s/hole/hold/<br>
s/pased/passed/<br>
<br>
<snip><br>
<br>
> Furthermore, the majority of currently existing extension modules has<br>
> problems with sub-interpreter support and/or reloading, and, while it is<br>
> it possible with the current infrastructure to support these<br>
> features, is neither easy nor efficient.<br>
> Addressing these issues was the goal of PEP 3121, but many extensions<br>
> took the least-effort approach to porting to Python 3, leaving many of these<br>
> issues unresolved.<br>
<br>
It's probably worth noting that some of those "least-effort" porting<br>
approaches are in the standard library: this PEP is about solving our<br>
own problems in addition to other people's.<br>
<br>
> Thius PEP keeps the backwards-compatible behavior, which should reduce pressure<br>
> and give extension authors adequate time to consider these issues when porting.<br>
<br>
s/thius/this/<br>
<br>
> The proposal<br>
> ============<br>
><br>
> The current extension module initialisation will be deprecated in favour of<br>
> a new initialisation scheme. Since the current scheme will continue to be<br>
> available, existing code will continue to work unchanged, including binary<br>
> compatibility.<br>
><br>
> Extension modules that support the new initialisation scheme must export one<br>
> or both of the public symbols "PyModuleCreate_modulename" and<br>
> "PyModuleExec_modulename", where "modulename" is the<br>
> name of the shared library. This mimics the previous naming convention for<br>
> the "PyInit_modulename" function.<br>
><br>
> This symbols, if defined, must resolve to C functions with the following<br>
> signatures, respectively::<br>
><br>
>     PyObject* (*PyModuleCreateFunction)(<u></u>PyObject* module_spec)<br>
>     int (*PyModuleExecFunction)(<u></u>PyObject* module)<br>
<br>
For the Python level, the model we ended up with for 3.5 is:<br>
<br>
1. create_module must exist, but may return None<br>
2. exec_module must exist, but may have no effect on the module state<br>
<br>
For the new C level API, it's probably worth drawing the more explicit<br>
parallel to __new__ and __init__ on classes, where you can implement<br>
both of them if you want, but in most cases, implementing only one or<br>
the other will be sufficient.<br>
<br>
The reason I suggest that is because I was going to ask if we should<br>
make providing both APIs, or at least PyModuleExec_*, compulsory<br>
(based on the Python Loader API requirements), but thinking of the<br>
__new__/__init__ analogy made me realise that your current design<br>
makes sense, since dealing with it is confined specifically to the<br>
extension module loader implementation.<br></blockquote><div><br></div><div>See I don't like this fork from the PEP 451 API. Unless we want to change importlib to not require exec_module() and instead let create_module() partially fulfill the role load_module() had by doing everything then I say the C API should try to follow how the rest of the import machinery operates, especially if the separation is mostly a refactoring of what some combined PyModuleCreate_* would probably do anyway.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
> The PyModuleCreate function<br>
> ---------------------------<br>
<br>
<snip><br>
<br>
> When called, this function must create and return a module object.<br>
><br>
> If "PyModuleExec_module" is undefined, this function must also initialize<br>
> the module; see PyModuleExec_module for details on initialization.<br>
<br>
This should be clarified to point out that, as per PEP 451, the import<br>
machinery will still take care of setting the import related<br>
attributes after the loader returns the module from create_module.<br>
<br>
> There is no requirement for the returned object to be an instance of<br>
> types.ModuleType. Any type can be used.<br>
<br>
The requirement for the returned object to support getting and setting<br>
attributes (as per<br>
<a href="https://www.python.org/dev/peps/pep-0451/#attributes" target="_blank">https://www.python.org/dev/<u></u>peps/pep-0451/#attributes</a>) should be<br>
defined here.<br>
<br>
> This follows the current<br>
> support for allowing arbitrary objects in sys.modules and makes it easier<br>
> for extension modules to define a type that exactly matches their needs for<br>
> holding module state.<br>
<br>
+1<br>
<br>
> The PyModuleExec function<br>
> -------------------------<br>
><br>
> This PyModuleExec function is used to implement "loader.exec_module"<br>
> defined in PEP 451.<br>
> It is called after ModuleSpec-related attributes such as ``__loader__``,<br>
> ``__spec__`` and ``__name__`` are set on the module.<br>
> (The full list is in PEP 451 [#pep-0451-attributes]_)<br>
><br>
> The "PyModuleExec_modulename" function will be called to initialize a module.<br>
> This happens in two situations: when the module is first initialized for<br>
> a given (sub-)interpreter, and when the module is reloaded.<br>
><br>
> The "module" argument receives the module object.<br>
> If PyModuleCreate is defined, this will be the the object returned by it.<br>
> If PyModuleCreate is not defined, PyModuleExec is epected to operate<br>
> on any Python object for which attributes can be added by PyObject_GetAttr*<br>
> and retreived by PyObject_SetAttr*.<br>
> Specifically, as the module may not be a PyModule_Type subclass,<br>
> PyModule_* functions should not be used on it, unless they explicitly support<br>
> operating on all objects.<br>
<br>
I think this is too permissive on the interpreter side of things, thus<br>
making things more complicated than we'd like them to be for extension<br>
module authors.<br>
<br>
If PyModuleCreate_* is defined, PyModuleExec_* will receive the object<br>
returned there, while if it isn't defined, the interpreter *will*<br>
provide a PyModule_Type instance, as per PEP 451.<br>
<br>
However, permitting module authors to make the PyModule_Type (or a<br>
subclass) assumption in their implementation does introduce a subtle<br>
requirement on the implementation of both the load_module method, and<br>
on custom PyModuleExec_* functions that are paired with a<br>
PyModuleCreate_* function.<br>
<br>
Firstly, we need to enforce the following constraint in load_module:<br>
if the underlying C module does *not* define a custom PyModuleCreate_*<br>
function, and we're passed a module execution environment which is<br>
*not* an instance of PyModule_Type, then we should throw TypeError.<br>
<br>
By contrast, in the presence of a custom PyModuleCreate_* function,<br>
the requirement for checking the type of the execution environment<br>
(and throwing TypeError if the module can't handle it) should be<br>
delegated to the PyModuleExec_* function, and that will need to be<br>
documented appropriately.<br>
<br>
That keeps things simple in the default case (extension module authors<br>
just using PyModuleExec_* can continue to assume the use of<br>
PyModule_Type or a subclass), while allowing more flexibility in the<br>
"power user" case of creating your own module object.<br>
<br>
> Usage<br>
> =====<br>
><br>
> This PEP allows three new ways of creating modules, each with its<br>
> advantages and disadvantages.<br>
><br>
><br>
> Exec-only<br>
> ---------<br>
><br>
> The preferred way to create C extensions is to define "PyModuleExec_modulename"<br>
> only. This brings the following advantages:<br>
><br>
> * The extension can be loaded into a pre-created module, making it possible<br>
>   to run them as ``__main__``, participate in certain lazy-loading schemes<br>
>   [#lazy_import_concerns]_, or enable other creative uses.<br>
> * The module can be reloaded in the same way as Python modules.<br>
><br>
> As Exec-only extension modules do not have C-level storage,<br>
> all module-local data must be stored in the module object's attributes,<br>
> possibly using the PyCapsule mechanism.<br>
<br>
With my suggested change above, this approach will also let module<br>
authors assume PyModule_Type (or a subclass), and have the interpreter<br>
enforce that assumption on their behalf.<br>
<br>
> Create-only<br>
> -----------<br>
><br>
> Extensions defining only the "PyModuleCreate_modulename" hook behave similarly<br>
> to current extensions.<br>
><br>
> This is the easiest way to create modules that require custom module objects,<br>
> or substantial per-module state at the C level (using positive<br>
> ``PyModuleDef.m_size``).<br>
><br>
> When the PyModuleCreate function is called, the module has not yet been added<br>
> to sys.modules.<br>
> Attempts to load the module again (possibly transitively) will result in an<br>
> infinite loop.<br>
> If user code needs to me called in module initialization,<br>
> module authors are advised to do so from the PyModuleExec function.<br>
><br>
> Reloading a Create-only module does nothing, except re-setting<br>
> ModuleSpec-related attributes described in PEP 0451 [#pep-0451-attributes].<br>
<br>
Another advantage of this approach is that you don't need to worry<br>
about potentially being passed a module object of an arbitrary type.<br>
<br>
> Exec and Create<br>
> ---------------<br>
><br>
> Extensions that need to create a custom module object,<br>
> and either need to run user code during initialization or support reloading,<br>
> should define both "PyModuleCreate_modulename" and "PyModuleExec_modulename".<br>
<br>
This approach will have the downside of needing to check the type of<br>
the passed in module against the module implementation's assumptions.<br>
<br>
> Subinterpreters and Interpreter Reloading<br>
> ------------------------------<u></u>-----------<br>
><br>
> Extensions using the new initialization scheme are expected to support<br>
> subinterpreters and multiple Py_Initialize/Py_Finalize cycles correctly.<br>
> The mechanism is designed to make this easy, but care is still required<br>
> on the part of the extension author.<br>
> No user-defined functions, methods, or instances may leak to different<br>
> interpreters.<br>
> To achieve this, all module-level state should be kept in either the module<br>
> dict, or in the module object.<br>
> A simple rule of thumb is: Do not define any static data, except built-in types<br>
> with no mutable or user-settable class attributes.<br>
<br>
Worth noting here that this is why we consider it desirable to provide<br>
a utility somewhere in the standard library to make it easy to do<br>
these kinds of checks.<br>
<br>
At the very least we need it in the test.support module to do our own<br>
tests, but it would be preferable to have it as a supported API<br>
somewhere in the standard library.<br>
<br>
This isn't the only area where this kind of question of making it<br>
easier for people to test whether or not they're implementing or<br>
emulating a protocol correctly has come up - it's applicable to<br>
testing things like total ordering support in custom objects, operand<br>
precedence handling, ABC compliance, code generation, exception<br>
traceback manipulation, etc.<br>
<br>
Perhaps we should propose a new unittest submodule for compatibility<br>
and compliance tests that are too esoteric for the module top level,<br>
but we also don't want to ask people to write for themselves?<br>
<br>
> Module Reloading<br>
> ----------------<br>
><br>
> Extensions that support reloading must define PyModuleExec, which is called<br>
> in reload() to re-initialize the module in place.<br>
> The same caveats apply to reloading an extension module as to reloading<br>
> a Python module.<br>
<br>
Assuming you go with my suggestion regarding the PyModule_Type<br>
assumption above, that would be worth reiterating here.<br>
<br>
> Multiple modules in one library<br>
> ------------------------------<u></u>-<br>
><br>
> To support multiple Python modules in one shared library, the library<br>
> must export all appropriate PyModuleExec_<name> or PyModuleCreate_<name> hooks<br>
> for each exported module.<br>
> The modules are loaded using a ModuleSpec with origin set to the name of the<br>
> library file, and name set to the module name.<br>
> Note that this mechanism can only be used to *load* such modules,<br>
> not to *find* them.<br>
<br>
If I recall correctly, Brett already updated the extension module<br>
finder to handle locating such modules. It's either that or there's an<br>
existing issue on the tracker for it.<br></blockquote><div><br></div><div>Existing issue; extensions use FileFinder and do no caching or search of what initialization functions are exported by the module.</div><div><br></div><div>-Brett</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
> Open issues<br>
> ===========<br>
><br>
> Now that PEP 442 is implemented, it would be nice if module finalization<br>
> did not set all attributes to None,<br>
<br>
Antoine added that in 3.4: <a href="http://bugs.python.org/issue18214" target="_blank">http://bugs.python.org/<u></u>issue18214</a><br>
<br>
However, it wasn't entirely effective, as several extension modules<br>
still need to be hit with a sledgehammer to get them to drop<br>
references properly. Asking "Why is that so?" is actually one of the<br>
things that got me started digging into this area a couple of years<br>
back.<br>
<br>
> In this scheme, it is not possible to create a module with C-level state,<br>
> which would be able to exec itself in any externally provided module object,<br>
> short of putting PyCapsules in the module dict.<br>
<br>
I suspect "PyCapsule in the module dict" may be the right answer here,<br>
in which case some suitable documentation and perhaps some convenience<br>
APIs could be a good way to go.<br>
<br>
Relying on PyCapsule also has the advantage of potentially supporting<br>
better collaboration between extension modules, without needing to<br>
link them with each other directly.<br>
<br>
> The proposal repurposes PyModule_SetDocString, PyModule_AddObject,<br>
> PyModule_AddIntMacro <a href="http://et.al" target="_blank">et.al</a>. to work on any object.<br>
> Would it be better to have these in the PyObject namespace?<br>
<br>
With my proposal above to keep the PyModule_Type assumption in most<br>
cases, I think it may be better to leave them alone entirely. If folks<br>
decide to allow non module types, they can decide to handle the<br>
consequences.<br>
<br>
> We should expose some kind of API in importlib.util (or a better place?) that<br>
> can be used to check that a module works with reloading and subinterpreters.<br>
<br>
See comments above on that.<br>
<br>
> The runpy module will need to be modified to take advantage of PEP 451<br>
> and this PEP. This might out of scope for this PEP.<br>
<br>
I think it's out of scope, but runpy *does* need an internal redesign<br>
to take full advantage of PEP 451. Currently it works by attempting to<br>
extract the code object directly in most situations, whereas PEP 451<br>
should let it rely almost entirely on exec_code instead (with direct<br>
execution used only when it's actually given a path directly to a<br>
Python source or bytecode file.<br>
<br>
Cheers,<br>
Nick.<br>
<br>
--<br>
Nick Coghlan   |   <a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>   |   Brisbane, Australia<br>
______________________________<u></u>_________________<br>
Import-SIG mailing list<br>
<a href="mailto:Import-SIG@python.org" target="_blank">Import-SIG@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/import-sig" target="_blank">https://mail.python.org/<u></u>mailman/listinfo/import-sig</a><br>
</blockquote></div></div>