[Python-Dev] is this the fault of import_fresh_module or pickle?
Nick Coghlan
ncoghlan at gmail.com
Wed Jan 9 02:28:21 CET 2013
On Wed, Jan 9, 2013 at 2:58 AM, Eli Bendersky <eliben at gmail.com> wrote:
>> Handling this case is why having a context-manager form of
>> import_fresh_module was suggested earlier in this meta-thread. At
>> least, I think that would solve it, I haven't tried it :)
>
>
> Would you mind extracting just this idea into this discussion so we can
> focus on it here? I personally don't see how making import_fresh_module a
> context manager will solve things, unless you add some extra functionality
> to it? AFAIU it doesn't remove modules from sys.modules *before* importing,
> at this point.
Sure it does, that's how it works: the module being imported, as well
as anything requested as a "fresh" module is removed from sys.modules,
anything requested as a "blocked" module is replaced with None (or
maybe 0 - whatever it is that will force ImportError). It then does
the requested import and then *reverts all those changes* to
sys.modules.
It's that last part which is giving you trouble: by the time you run
the actual tests, sys.modules has been reverted to its original state,
so pickle gets confused when it attempts to look things up by name.
Rather than a context manager form of import_fresh_module, what we
really want is a "modules_replaced" context manager:
@contextmanager
def modules_replaced(replacements):
_missing = object()
saved = {}
try:
for name, mod in replacements.items():
saved[name] = sys.modules.get(name, _missing)
sys.modules[name] = mod
yield
finally:
for name, mod in saved.items():
if mod is _missing:
del sys.modules[name]
else:
sys.modules[name] = mod
And a new import_fresh_modules function that is like
import_fresh_module, but returns a 2-tuple of the requested module and
a mapping of all the affected modules, rather than just the module
object.
However, there will still be cases where this doesn't work (i.e.
modules with import-time side effects that don't support repeated
execution), and the pickle and copy global registries are a couple of
the places that are notorious for not coping with repeated imports of
a module. In that case, the test case will need to figure out what
global state is being modified and deal with that specifically.
(FWIW, this kind of problem is why import_fresh_module is in
test.support rather than importlib and the reload builtin became
imp.reload in Python 3 - "module level code is executed once per
process" is an assumption engrained in most Python developer's brains,
and these functions deliberately violate it).
Cheers,
Nick.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
More information about the Python-Dev
mailing list