Commit 22ebb8d6 authored by Arnaud Fontaine's avatar Arnaud Fontaine

Do not cache Python Script and ZODB Component source code in linecache.cache

to avoid concurrency and transactional issues.

Considering that linecache is used by traceback and pdb, this should not be an
issue from a performance point of view anyway.
parent 47dcd0b9
......@@ -339,13 +339,4 @@ class ComponentDynamicPackage(ModuleType):
# the meantime
del sys.modules[module_name]
# Delete linecache data to get updated source code (__file__ attribute
# (<ComponentID>) is used as linecache key)
import linecache
try:
del linecache.cache[getattr(package, name).__file__]
# __file__ may not be defined
except (AttributeError, KeyError):
pass
delattr(package, name)
......@@ -14,22 +14,6 @@ from Products.PythonScripts.PythonScript import PythonScript
from OFS.misc_ import p_
from App.ImageFile import ImageFile
PythonScript_write = PythonScript.write
def write(self, text):
"""
Change the Python Script and purge linecache cache entry (meaningful for any
module relying on linecache, such as traceback and pdb)
"""
PythonScript_write(self, text)
import linecache
try:
del linecache.cache[self.get_filepath()]
except KeyError:
pass
PythonScript.write = write
def haveProxyRole(self):
"""if a script has proxy role, return True"""
return bool(self._proxy_roles)
......
......@@ -100,48 +100,29 @@ if 1:
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...> and
'Script (Python)' for Zope Python Scripts.
The original getlines() will be called which look into the cache and if not
available, call updatecache.
"""
if ((filename == '<string>' or filename == 'Script (Python)')
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...
Also, get source code of Zope Python Script and store it in the linecache
cache as well (using __file__ module attribute equals to: 'Script
(Python):ABSOLUTE_URL'). See PythonScript.py patch as well to remove cache
entry when a PythonScript is modified.
Patch of linecache module (used in traceback and pdb module) to display ZODB
Components and Python Script source code properly without requiring to
create a temporary file on the filesystem
The filename is always '<string>' for any code executed by exec() (ZODB
Components) and '(FILENAME)?Script \(Python\)' for Zope Python Scripts.
linecache.cache filled by linecache.updatecache() called by the original
linecache.getlines() is bypassed for ZODB Components and Python Script to
avoid getting inconsistent source code. Having no cache could be an issue if
performances would be required here but as linecache module is only called
by traceback and pdb modules not used often, this should not be an issue.
"""
if filename and module_globals:
data = None
# Get source code of ZODB Components (following PEP 302)
if filename[0] == '<' and filename[-1] == '>' and '__loader__' in module_globals:
if filename == '<string>' and '__loader__' in module_globals:
name = module_globals.get('__name__')
loader = module_globals['__loader__']
get_source = getattr(loader, 'get_source', None)
......@@ -150,20 +131,15 @@ def updatecache(filename, module_globals=None):
data = get_source(name)
except (ImportError, AttributeError):
pass
else:
if data is None:
return []
# Get source code of Zope Python Script
elif filename.startswith('Script (Python)') and 'script' in module_globals:
elif 'Script (Python)' in filename and 'script' in module_globals:
data = module_globals['script'].body()
if data is not None:
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)
return linecache_getlines(filename, module_globals)
linecache.updatecache = updatecache
linecache.getlines = getlines
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