Commit 4406c9b3 authored by Arnaud Fontaine's avatar Arnaud Fontaine

Display source code of ZODB Component package for any module using linecache (pdb and traceback).

parent 3d209aa1
......@@ -125,6 +125,19 @@ class ComponentDynamicPackage(ModuleType):
return self.__registry_dict
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)
return getattr(getSite().portal_components,
module.__file__[1:-1]).getTextContent(validated_only=True)
def find_module(self, fullname, path=None):
# Ignore imports with a path which are filesystem-only and any
# absolute imports which does not start with this package prefix,
......@@ -240,7 +253,7 @@ class ComponentDynamicPackage(ModuleType):
sys.modules[component_id_alias] = new_module
# This must be set for imports at least (see PEP 302)
new_module.__file__ = '<' + component_name + '>'
new_module.__file__ = '<' + component.getId() + '>'
try:
component.load(new_module.__dict__, validated_only=True)
......@@ -290,4 +303,14 @@ class ComponentDynamicPackage(ModuleType):
# The module must be deleted first from sys.modules to avoid imports in
# the meantime
del sys.modules[module_name]
# Delete linecache data
import linecache
try:
del linecache.cache[getattr(package, name).__file__]
# __file__ may not be defined
except (AttributeError, KeyError):
pass
# And finally remove the module
delattr(package, name)
......@@ -99,3 +99,56 @@ if 1:
# Required by PortalTransforms.transforms.rest
from docutils import utils
utils.relative_path = lambda source, target: os.path.abspath(target)
# Patch of linecache module (used in traceback and pdb module) to display ZODB
# Components source code properly without requiring to create a temporary file
# on the filesystem
import linecache
linecache_getlines = linecache.getlines
def getlines(filename, module_globals=None):
"""
The filename is always '<string>' for any code executed by exec(). ZODB
Component modules always set __file__ attribute to <erp5.component...>.
The original getlines() will be called which look into the cache and if not
available, call updatecache.
"""
if filename == '<string>' and module_globals and '__file__' in module_globals:
filename = module_globals['__file__']
return linecache_getlines(filename, module_globals)
linecache.getlines = getlines
linecache_updatecache = linecache.updatecache
def updatecache(filename, module_globals=None):
"""
Original updatecache requires a filename which doesn't match <.*>, which is
strange considering that it then looks whether the module has been loaded
through PEP 302 Loader, but it is perhaps to be more generic. Anyhow, <> is
really needed to differenciate files on the filesystem to the ones only in
memory...
"""
if (filename[0] == '<' and filename[-1] == '>' and module_globals and
'__loader__' in module_globals):
name = module_globals.get('__name__')
loader = module_globals['__loader__']
get_source = getattr(loader, 'get_source', None)
if name and get_source:
try:
data = get_source(name)
except (ImportError, AttributeError):
pass
else:
if data is None:
return []
data_len = len(data)
data = [line + '\n' for line in data.splitlines()]
linecache.cache[filename] = (data_len, None, data, filename)
return data
return linecache_updatecache(filename, module_globals)
linecache.updatecache = updatecache
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