|
Michael Sanders
m.r.sanders at gmail.com
Thu May 10 09:27:58 EDT 2007
Michael Sanders wrote:
> Further to this, one of my colleagues has made some other changes to
> 'importer.py' that appear to fix the problem (please find a diff attached).
>
> The modification is a change to the locking in the import_module()
> method of the _ModuleCache class:
>
> The 'self._lock1' lock is now used to lock out the actual 'loading' code
> as well as the check-for-cache code: _reload_required().
>
> See line 554 ish (Code up to line 769 indented)
Apologies, I sent the whole file and not a diff. I've (hopefully)
attached the diff this time!
Thanks,
Michael
-------------- next part --------------
--- importer.py 2007-05-10 13:57:01.157138700 +0100
+++ importer.py.new 2007-05-10 14:18:22.976417100 +0100
@@ -551,218 +551,222 @@
# Import module or obtain it from cache as is
# appropriate.
- if load:
-
- # Setup a new empty module to load the code for
- # the module into. Increment the instance count
- # and set the reload flag to force a reload if
- # the import fails.
-
- cache.instance = cache.instance + 1
- cache.reload = 1
-
- module = imp.new_module(label)
-
- # If the module was previously loaded we need to
- # manage the transition to the new instance of
- # the module that is being loaded to replace it.
- # This entails calling the special clone method,
- # if provided within the existing module. Using
- # this method the existing module can then
- # selectively indicate what should be transfered
- # over to the next instance of the module,
- # including thread locks. If this process fails
- # the special purge method is called, if
- # provided, to indicate that the existing module
- # is being forcibly purged out of the system. In
- # that case any existing state will not be
- # transferred.
-
- if cache.module != None:
- if hasattr(cache.module, "__mp_clone__"):
- try:
- # Migrate any existing state data from
- # existing module instance to new module
- # instance.
-
- if log:
- msg = "Cloning module '%s'" % file
+ try:
+ self._lock1.acquire()
+ if load:
+
+ # Setup a new empty module to load the code for
+ # the module into. Increment the instance count
+ # and set the reload flag to force a reload if
+ # the import fails.
+
+ cache.instance = cache.instance + 1
+ cache.reload = 1
+
+ module = imp.new_module(label)
+
+ # If the module was previously loaded we need to
+ # manage the transition to the new instance of
+ # the module that is being loaded to replace it.
+ # This entails calling the special clone method,
+ # if provided within the existing module. Using
+ # this method the existing module can then
+ # selectively indicate what should be transfered
+ # over to the next instance of the module,
+ # including thread locks. If this process fails
+ # the special purge method is called, if
+ # provided, to indicate that the existing module
+ # is being forcibly purged out of the system. In
+ # that case any existing state will not be
+ # transferred.
+
+ if cache.module != None:
+ if hasattr(cache.module, "__mp_clone__"):
+ try:
+ # Migrate any existing state data from
+ # existing module instance to new module
+ # instance.
+
+ if log:
+ msg = "Cloning module '%s'" % file
+ self._log_notice(msg)
+
+ cache.module.__mp_clone__(module)
+
+ except:
+ # Forcibly purging module from system.
+
+ self._log_exception()
+
+ if log:
+ msg = "Purging module '%s'" % file
+ self._log_notice(msg)
+
+ if hasattr(cache.module, "__mp_purge__"):
+ try:
+ cache.module.__mp_purge__()
+ except:
+ self._log_exception()
+
+ cache.module = None
+
+ # Setup a fresh new module yet again.
+
+ module = imp.new_module(label)
+
+ if log:
+ if cache.module == None:
+ msg = "Importing module '%s'" % file
self._log_notice(msg)
-
- cache.module.__mp_clone__(module)
-
- except:
- # Forcibly purging module from system.
-
- self._log_exception()
-
- if log:
- msg = "Purging module '%s'" % file
+ else:
+ msg = "Reimporting module '%s'" % file
self._log_notice(msg)
-
- if hasattr(cache.module, "__mp_purge__"):
- try:
- cache.module.__mp_purge__()
- except:
- self._log_exception()
-
- cache.module = None
-
- # Setup a fresh new module yet again.
-
- module = imp.new_module(label)
-
- if log:
- if cache.module == None:
+ else:
+ if log:
msg = "Importing module '%s'" % file
self._log_notice(msg)
- else:
- msg = "Reimporting module '%s'" % file
- self._log_notice(msg)
- else:
- if log:
- msg = "Importing module '%s'" % file
- self._log_notice(msg)
-
- # Must add to the module the path to the modules
- # file. This ensures that module looks like a
- # normal module and this path will also be used
- # in certain contexts when the import statement
- # is used within the module.
-
- module.__file__ = file
-
- # Setup a new instance object to store in the
- # module. This will refer back to the actual
- # cache entry and is used to record information
- # which is specific to this incarnation of the
- # module when reloading is occuring.
-
- instance = _InstanceInfo(label, file, cache)
-
- module.__mp_info__ = instance
-
- # Cache any additional module search path which
- # should be used for this instance of the module
- # or package. The path shouldn't be able to be
- # changed during the lifetime of the module to
- # ensure that module imports are always done
- # against the same path.
-
- if path is None:
- path = []
-
- module.__mp_path__ = list(path)
-
- # Place a reference to the module within the
- # request specific cache of imported modules.
- # This makes module lookup more efficient when
- # the same module is imported more than once
- # within the context of a request. In the case
- # of a cyclical import, avoids a never ending
- # recursive loop.
-
- if modules is not None:
- modules[label] = module
-
- # If this is a child import of some parent
- # module, add this module as a child of the
- # parent.
-
- atime = time.time()
+
+ # Must add to the module the path to the modules
+ # file. This ensures that module looks like a
+ # normal module and this path will also be used
+ # in certain contexts when the import statement
+ # is used within the module.
+
+ module.__file__ = file
+
+ # Setup a new instance object to store in the
+ # module. This will refer back to the actual
+ # cache entry and is used to record information
+ # which is specific to this incarnation of the
+ # module when reloading is occuring.
+
+ instance = _InstanceInfo(label, file, cache)
+
+ module.__mp_info__ = instance
+
+ # Cache any additional module search path which
+ # should be used for this instance of the module
+ # or package. The path shouldn't be able to be
+ # changed during the lifetime of the module to
+ # ensure that module imports are always done
+ # against the same path.
+
+ if path is None:
+ path = []
+
+ module.__mp_path__ = list(path)
+
+ # Place a reference to the module within the
+ # request specific cache of imported modules.
+ # This makes module lookup more efficient when
+ # the same module is imported more than once
+ # within the context of a request. In the case
+ # of a cyclical import, avoids a never ending
+ # recursive loop.
+
+ if modules is not None:
+ modules[label] = module
+
+ # If this is a child import of some parent
+ # module, add this module as a child of the
+ # parent.
+
+ atime = time.time()
+
+ if parent_info:
+ parent_info.children[label] = atime
+
+ # Perform actual import of the module.
+
+ try:
+ execfile(file, module.__dict__)
+
+ except:
+
+ # Importation of the module has failed for
+ # some reason. If this is the very first
+ # import of the module, need to discard the
+ # cache entry entirely else a subsequent
+ # attempt to load the module will wrongly
+ # think it was successfully loaded already.
+
+ if cache.module is None:
+ del self._cache[label]
+
+ raise
+
+ # Update the cache and clear the reload flag.
+
+ cache.module = module
+ cache.reload = 0
+
+ # Need to also update the list of child modules
+ # stored in the cache entry with the actual
+ # children found during the import. A copy is
+ # made, meaning that any future imports
+ # performed within the context of the request
+ # handler don't result in the module later being
+ # reloaded if they change.
+
+ cache.children = dict(module.__mp_info__.children)
+
+ # Create link to embedded path at end of import.
+
+ cache.path = module.__mp_path__
+
+ # Increment the generation count of the global
+ # state of all modules. This is used in the
+ # dependency management scheme for reloading to
+ # determine if a module dependency has been
+ # reloaded since it was loaded.
+
+ self._lock2.acquire()
+ self._generation = self._generation + 1
+ cache.generation = self._generation
+ self._lock2.release()
+
+ # Update access time and reset access counts.
+
+ cache.ctime = atime
+ cache.atime = atime
+ cache.direct = 1
+ cache.indirect = 0
- if parent_info:
- parent_info.children[label] = atime
-
- # Perform actual import of the module.
-
- try:
- execfile(file, module.__dict__)
-
- except:
-
- # Importation of the module has failed for
- # some reason. If this is the very first
- # import of the module, need to discard the
- # cache entry entirely else a subsequent
- # attempt to load the module will wrongly
- # think it was successfully loaded already.
-
- if cache.module is None:
- del self._cache[label]
-
- raise
-
- # Update the cache and clear the reload flag.
-
- cache.module = module
- cache.reload = 0
-
- # Need to also update the list of child modules
- # stored in the cache entry with the actual
- # children found during the import. A copy is
- # made, meaning that any future imports
- # performed within the context of the request
- # handler don't result in the module later being
- # reloaded if they change.
-
- cache.children = dict(module.__mp_info__.children)
-
- # Create link to embedded path at end of import.
-
- cache.path = module.__mp_path__
-
- # Increment the generation count of the global
- # state of all modules. This is used in the
- # dependency management scheme for reloading to
- # determine if a module dependency has been
- # reloaded since it was loaded.
-
- self._lock2.acquire()
- self._generation = self._generation + 1
- cache.generation = self._generation
- self._lock2.release()
-
- # Update access time and reset access counts.
-
- cache.ctime = atime
- cache.atime = atime
- cache.direct = 1
- cache.indirect = 0
-
- else:
-
- # Update the cache.
-
- module = cache.module
-
- # Place a reference to the module within the
- # request specific cache of imported modules.
- # This makes module lookup more efficient when
- # the same module is imported more than once
- # within the context of a request. In the case
- # of a cyclical import, avoids a never ending
- # recursive loop.
-
- if modules is not None:
- modules[label] = module
-
- # If this is a child import of some parent
- # module, add this module as a child of the
- # parent.
-
- atime = time.time()
-
- if parent_info:
- parent_info.children[label] = atime
-
- # Didn't need to reload the module so simply
- # increment access counts and last access time.
-
- cache.atime = atime
- cache.direct = cache.direct + 1
-
- return module
+ else:
+
+ # Update the cache.
+
+ module = cache.module
+
+ # Place a reference to the module within the
+ # request specific cache of imported modules.
+ # This makes module lookup more efficient when
+ # the same module is imported more than once
+ # within the context of a request. In the case
+ # of a cyclical import, avoids a never ending
+ # recursive loop.
+
+ if modules is not None:
+ modules[label] = module
+
+ # If this is a child import of some parent
+ # module, add this module as a child of the
+ # parent.
+
+ atime = time.time()
+
+ if parent_info:
+ parent_info.children[label] = atime
+
+ # Didn't need to reload the module so simply
+ # increment access counts and last access time.
+
+ cache.atime = atime
+ cache.direct = cache.direct + 1
+
+ return module
+ finally:
+ self._lock1.release() # always unlock
finally:
# Lock on cache object can now be released.
|