[Python-Dev] PEP 451 update (original) (raw)

Eric Snow ericsnowcurrently at gmail.com
Tue Oct 29 05:45:17 CET 2013


On Sat, Oct 26, 2013 at 11:03 PM, Nick Coghlan <ncoghlan at gmail.com> wrote:

I don't think we can postpone it to later, as we need to be clear on:

1. Does reload use name or spec.name when both are available? 2. Does name get restored to its original value if reloading via spec.name? 3. Do other import related attributes get restored to their original values? 4. Does createmodule get called if the loader has an execmodule method? 5. Does the loader have access to the previous spec when reloading a module? My answers to these questions (note that my answer to 2 differs from what I had in my initial sketch): 1. spec.name 2. Yes, name is updated to match spec.name, expect if it is currently "main" 3. Yes, other import related attributes are restored to their baseline values 4. No, createmodule is never called when reloading

I agree on all of these. I'm adding a "How reloading will work" section to the PEP.

5. Currently no, but I'm thinking that may be worth changing (more on that below)

The reload() implementation in my message is actually based on the current importlib.reload implementation. The only PEP 451 related changes were: - using spec.name (if available) instead of name - checking all parent modules exist rather than just the immediate parent module - calling import.findspec() rather than using the loader attribute directly - being explicit that name is left at the value it had prior to the reload - handling the namespace package, execmodule and no execmodule cases I also added an explicit check that the module was re-used properly, but I agree that change is out of scope for the PEP and should be considered as a separate question. Now, regarding the signature of execmodule(): I'm back to believing that loaders should receive a clear indication that a reload is taking place. Legacy loaders have to figure that out for themselves (by seeing that the module already exists in sys.modules), but we can do better for the new API by making the execmodule signature look like: def execmodule(self, module, previousspec=None): # module is as per the current PEP 451 text # previousspec would be set only in the reload() case # loaders that don't care still need to accept it, but can just ignore it ...

I'd rather give loaders another optional method: supports_reload(name). Complicating the spec methods signatures (to support passing the old spec through) just for the sake of reload seems less than optimal. I don't see any benefit to exposing the old spec to loader.exec_module().

So, with those revisions, the reload() semantics would be defined as: def reload(module): # Get the name to reload from the spec if it is available, # otherwise use name directly currentspec = getattr(module, "spec") currentname = module.name nametoreload = currentname if currentspec is None else currentspec.name # Check this module is properly imported before trying to reload it loadedmodule = sys.modules.get(nametoreload) if loadedmodule is not module: msg = "module {} not in sys.modules" raise ImportError(msg.format(nametoreload), name=nametoreload) parentname = nametoreload.rpartition('.')[0] while parentname: if parentname not in sys.modules: msg = "parent {!r} not in sys.modules" raise ImportError(msg.format(parentname), name=parentname) parentname = parentname.rpartition('.')[0] # Check for a modified spec (e.g. if the import hooks have changed) updatedspec = importlib.findspec(nametoreload) # The import-related module attributes get updated here initmoduleattrs(loadedmodule, updatedspec) if currentname == "main": loadedmodule.name = "main" # Now re-execute the module loader = updatedspec.loader if loader is None: # Namespace package, already reinitialised above return loadedmodule elif hasattr(loader, 'execmodule'): loader.execmodule(module, currentspec) else: loader.loadmodule(nametoreload) # Allow for the module to be replaced in sys.modules # (even though that likely won't work very well) return sys.modules[nametoreload]

This is pretty much the same thing I've implemented. :)

-eric



More information about the Python-Dev mailing list