Commit 2ac367df authored by Arnaud Fontaine's avatar Arnaud Fontaine

ZODB Components: Cache source code in the loader to avoid access to ERP5 Site.

Using DeadlockDebugguer lead to a deadlock when trying to debug a deadlock on
ZODB Component as it was trying to load the Component. Moreover, this required
accessing to ERP5 Site whereas DeadlockDebugguer is used outside.

Steps to reproduce:
  1. Create an Extension Component with a sleep and run it.
  2. Call manage_debug_threads.
parent 6b03c39e
......@@ -76,6 +76,7 @@ class ComponentDynamicPackage(ModuleType):
self._portal_type = portal_type
self.__version_suffix_len = len('_version')
self.__registry_dict = collections.defaultdict(dict)
self.__fullname_source_code_dict = {}
# Add this module to sys.path for future imports
sys.modules[namespace] = self
......@@ -132,17 +133,14 @@ class ComponentDynamicPackage(ModuleType):
def get_source(self, fullname):
"""
Get the source code of the given module name from the ID defined on the
dynamic module (setting getTextContent() on the module directly may not
work properly upon reset and there is no need for performance there as it
is only used for traceback or pdb anyway)
"""
module = __import__(fullname, fromlist=[fullname.rsplit('.', 1)[0]],
level=0)
component = getSite().unrestrictedTraverse(module.__file__[1:-1])
PEP-302 function to get the source code, used mainly by linecache for
tracebacks, pdb...
return component.getTextContent(validated_only=True)
Use internal cache rather than accessing the Component directly as this
would require accessing ERP5 Site even though the source code may be
retrieved outside of ERP5 (eg DeadlockDebugguer).
"""
return self.__fullname_source_code_dict.get(fullname)
def find_module(self, fullname, path=None):
"""
......@@ -390,6 +388,9 @@ class ComponentDynamicPackage(ModuleType):
module_cache_set.add(module)
# Only useful for get_source()
self.__fullname_source_code_dict[module_fullname] = source_code_str
return module
finally:
# load_module() can be called outside of import machinery, for example
......@@ -415,8 +416,9 @@ class ComponentDynamicPackage(ModuleType):
if sub_package:
package = sub_package
else:
# Clear the Component registry only once
# Clear the Component registry and source code dict only once
self.__registry_dict.clear()
self.__fullname_source_code_dict.clear()
package = self
for name, module in package.__dict__.items():
......
......@@ -128,9 +128,9 @@ def patch_linecache():
display ZODB Components, Python Script source code and TALES Expressions
properly without requiring to create a temporary file on the filesystem
The filename is is always '<string>' for any code executed by exec() (ZODB
Components), '(FILENAME)?Script \(Python\)' for Zope Python Scripts and
'Python Expression "CODE"' for TALES expressions.
The filename is is always '<portal_components/*>' for ZODB Components,
'(FILENAME)?Script \(Python\)' for Zope Python Scripts and 'Python
Expression "CODE"' for TALES expressions.
linecache.cache filled by linecache.updatecache() called by the original
linecache.getlines() is bypassed for Python Script to avoid getting
......@@ -142,7 +142,7 @@ def patch_linecache():
if module_globals is None:
module_globals = get_globals(sys._getframe(1))
# Get source code of ZODB Components (following PEP 302)
# Get source code of ZODB Components following PEP 302
if (filename.startswith('<portal_components/') and
'__loader__' in module_globals):
data = None
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment