'reload M' doesn't update 'from M inport *'

kedra marbun kedra.marbun at gmail.com
Sun Jul 11 04:31:07 EDT 2010


> from m import f
>
> look for module m in the global cache
> if not there, then:
>     search for m.py
>     compile it to a Module object
>     put the Module object in the cache
> look for object named "f" in the Module object
agree

> create a new name "f" in the local namespace
> set the name "f" to cached object
strongly agree

> The important thing to notice is the the name "f" is a local variable. It
> doesn't, and can't, remember that it comes from module m. Reloading m
> can't do anything to f, because the connection is lost.
disagree with 2nd stmt. local 'f' does *remember* (if that's the right
word) where it comes from, because it points to the original obj, as
you said: 'set the name "f" to cached object'

py0.py
======
def f(): ...

>>> from py0 import *
>>> assert f.__module__ == sys.modules['py0'].__name__
>>> assert f.__globals__ is sys.modules['py0'].__dict__

> Now consider that the object "f" that came from m was itself imported
> from another module, "service". Reloading service doesn't help, because
> m.f doesn't know it came from service. Reloading m doesn't help, because
> all that does is run "from service import f" again, and that just fetches
> f from the global cache.
disagree with 2nd stmt, partially disagree with 3rd stmt

reloading 'service' partially helps, since it updates the mod obj
pointed by 'service' in global cache. it needs to be followed by
reloading m, then we have m.f points to the new obj. the important
part is the order of reloading mods

l.py
====
def f(): ...

m.py
====
from l import *

>>> from m import *

at this point, the func obj is referenced by 3 distinct variables with
name 'm'(one in each mod)

>>> assert sys.getrefcount(f) == 4
>>> referrers = gc.get_referrers(f)
>>> mod_dicts = [sys.modules[k].__dict__ for k in sys.modules if k == 'l' or k == 'm' or k == __name__]
>>> for d in mod_dicts:
...	referrers.remove(d)
>>> assert len(referrers) == 0

>>> imp.reload(sys.modules['l'])

now the original func obj is ref'ed by 2 vars, the new func obj is
ref'ed by 1 var

>>> imp.reload(sys.modules['m'])
>>> f = sys.modules['m'].f

now the original func obj is ready to be recollected, the new func obj
is ref'ed by 3 vars

> The simplest, easiest way of dealing with this is not to have to deal
> with it: don't use "from service import f", and ESPECIALLY don't use
> "from service import *". Always use fully-qualified importing:
>
> import service
> service.f
strongly agree

> The other way is not to bother with reload. It's not very powerful, only
> good for the simplest use in the interactive interpreter. Just exit the
> interpreter and restart it.
set, del, reload are useful when it comes to structure manipulation at
runtime, most langs differentiate it, labeling as metaprogramming, py
smashes the diff in an elegant way (oh flattery, i love it) ;)

as their names say, set & del only bind & unbind obj to var, the obj
to bind must be in its bytecode form, in theory one could do it, maybe
thru modules that are categorized as "Python Language Services" in the
lib manual, but it's not practical (e.g. see the last stmt of
types.CodeType.__doc__). this is where 'reload' steps in, and so the
story goes ...



More information about the Python-list mailing list