This is a followup to the closed discussion from last year
(referenced) about reloading.  I've always been mildly bitten by this,
and now that we're trying to make the company's Python code more
organized, I'm getting bitten worse.  I took the suggested code from
that post, and added a couple things.  It seems to be working OK for
me right now, but I'm really wondering whether there's some better
approach out there, which is why I'm posting.

1.  That code seems to assume that the modules being included only
nest 2 levels deep.  Our libraries nest quite deeply now, so I made it
actually prefix-match against the items in sys.modules to try and
broom out the whole library.

To expand on this, the code from the previous post removed the named
module from sys.modules, as well as anything in that module's dir
which was a module as well.  However, if I have:

foo/
    __init__.py
    bar/
        __init__.py
        baz/
             __init__.py

I believe (and found in practice) the previous version removed foo and
foo.bar from sys.modules, but not foo.bar.baz.  Thus, when foo.bar was
re-imported and executed it's "import baz" code, the module mechanism
thought baz was already loaded, which in turn meant foo.bar.baz was
not defined.

2.  For convenience, I wanted to be able to pass top-level module
names as well as module objects, so I can just park the call to the
reload in my Script Editor without worrying about whether I've already
loaded the modules once or not.

3.  I wanted to be able to pass a whole bunch of modules at once, so
the reload code is a one-liner.

With those goals, I got to this (debugging code etc. elided):

def rehashModules(params):
        retVals = [ ]
        moduleObjects = [ ]
        for param in params:
                if not isinstance(param, __builtins__.__class__):
                        try:
                                moduleObject = eval(param)
                                moduleObjects.append(moduleObject)
                        except Exception:
                                print "%r is not a module or module name"%param
                else:
                        moduleObjects.append(param)
        toDel = []
        for moduleObject in moduleObjects:
                thisToDel = []
                thisToDel.append(moduleObject.__name__)
                for mod in dir(moduleObject):
                        thisToDel.append(moduleObject.__name__ + "." + mod)
                for subMod in sys.modules.iterkeys():
                        if subMod.startswith(moduleObject.__name__) and subMod 
not in
thisToDel:
                                thisToDel.append(subMod)
                toDel.extend(thisToDel)
        gToDel = []
        for mod in toDel:
                globals_ = globals()
                if mod in sys.modules:
                        del(sys.modules[mod])
                if mod in globals_:
                        gToDel.append(mod)
        if gToDel:
                for g in gToDel:
                        globals().pop(g)
        # For the extra-paranoid, comment these in
        # import gc
        # gc.collect()
        for moduleObject in moduleObjects:
                print "import %s" % moduleObject.__name__
                exec("import %s" % moduleObject.__name__, globals())

With all of that, my reload looks something like this:

rehashModules(('CompanyWideLibrary','TopLevelModuleA','TopLevelModuleB'))

I keep a collection of those one-liners in my script editor for
different projects I'm working on.

I'm really hoping somebody has a better approach out there, although
reading the Python groups it seems like they've given up on reload()
as a problem -- admittedly, it's a hard one to solve thoroughly.

Leo


-- 
http://groups.google.com/group/python_inside_maya

Reply via email to