Commit b45914fa authored by Julien Muchembled's avatar Julien Muchembled

ExternalMethod: code refactoring + optimizations

The main optimization is the replacement of
  erp5.component.extension.find_load_module
by a simple
  __import__
like it was before commit c2ce3ba0
parent 3a5ac55b
...@@ -14,46 +14,64 @@ ...@@ -14,46 +14,64 @@
from inspect import getargs from inspect import getargs
from Products.ExternalMethod.ExternalMethod import * from Products.ExternalMethod.ExternalMethod import *
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from zLOG import LOG, WARNING
from . import PatchClass from . import PatchClass
from .PythonScript import addGuard from .PythonScript import addGuard
class _(PatchClass(ExternalMethod)): class _(PatchClass(ExternalMethod)):
def getFunction(self, reload=False, f=None): reloadIfChanged = getFuncDefaults = getFuncCode = filepath = None
"""
Patch to get ZODB Component Extension function if available, otherwise @property
fallback on filesystem Extension def func_defaults(self):
Patch2: do not use hasattr. return self.getFunction()[1]
"""
if f is None: @property
def func_code(self):
return self.getFunction()[2]
def getFunction(self, reload=False):
try: try:
component_module = __import__( component_module = __import__(
'erp5.component.extension.' + self._module, 'erp5.component.extension.' + self._module,
fromlist=['erp5.component.extension'], fromlist="*", level=0)
level=0) except ImportError, e:
except ImportError: if str(e) != "No module named " + self._module:
# Fall back loudly if a component exists but is broken.
# XXX: We used __import__ instead of
# erp5.component.extension.find_load_module
# because the latter is much slower.
# XXX: Should we also fall back on FS if the module imports
# successfully but does not contain the wanted function?
LOG("ERP5Type.dynamic", WARNING,
"Could not load Component module %r"
% ('erp5.component.extension.' + self._module),
error=1)
if not reload:
from Globals import DevelopmentMode
if DevelopmentMode:
try:
last_read, path = self._v_fs
except AttributeError:
last_read = None
path = getPath('Extensions', self._module,
suffixes=('', 'py', 'pyc'))
ts = os.stat(path)[stat.ST_MTIME]
if last_read != ts:
self._v_fs = ts, path
reload = True
f = getObject(self._module, self._function, reload) f = getObject(self._module, self._function, reload)
else: else:
f = getattr(component_module, self._function) f = getattr(component_module, self._function)
ff = getattr(f, 'im_func', f)
self._v_func_defaults = ff.func_defaults
self._v_func_code = FuncCode(ff,f is not ff)
self._v_f=f
return f
ExternalMethod_reloadIfChanged = ExternalMethod.reloadIfChanged
def reloadIfChanged(self):
try: try:
component_module = __import__( _f = self._v_f
'erp5.component.extension.' + self._module, if _f[0] is f:
fromlist=['erp5.component.extension'], return _f
level=0) except AttributeError:
except ImportError: pass
return ExternalMethod_reloadIfChanged(self) ff = getattr(f, 'im_func', f)
self._v_f = _f = f, ff.func_defaults, FuncCode(ff, f is not ff)
return _f
def __call__(self, *args, **kw): def __call__(self, *args, **kw):
"""Call an ExternalMethod """Call an ExternalMethod
...@@ -72,48 +90,13 @@ class _(PatchClass(ExternalMethod)): ...@@ -72,48 +90,13 @@ class _(PatchClass(ExternalMethod)):
In this case, the URL parent of the object is supplied as the In this case, the URL parent of the object is supplied as the
first argument. first argument.
Monkey patches:
- check guard against context, if guard exists.
- call ZODB Component Extension, by trying first to import ZODB
Component Extension if available, otherwise fallback on filesystem
Extension
- access volatile attribute safely
- fix magic "self" argument when positional arguments get their values
from kw.
""" """
self.checkGuard(True) self.checkGuard(True)
import erp5.component.extension _f = self.getFunction()
component_module = erp5.component.extension.find_load_module(self._module) f = _f[0]
if component_module is not None:
f = getattr(component_module, self._function)
else:
import Globals # for data
filePath = self.filepath()
if filePath==None:
raise RuntimeError,\
"external method could not be called " \
"because it is None"
if not os.path.exists(filePath):
raise RuntimeError,\
"external method could not be called " \
"because the file does not exist"
if Globals.DevelopmentMode:
self.reloadIfChanged()
f = None
_v_f = getattr(self, '_v_f', None)
if not _v_f or (f and f is not _v_f):
f = self.getFunction(f=f)
else:
f = _v_f
__traceback_info__=args, kw, self._v_func_defaults __traceback_info__ = args, kw, _f[1]
# XXX: We'd like to use inspect.getcallargs instead of try..except. # XXX: We'd like to use inspect.getcallargs instead of try..except.
# However, for the same reason as we use getargs instead of # However, for the same reason as we use getargs instead of
......
...@@ -34,6 +34,7 @@ import unittest ...@@ -34,6 +34,7 @@ import unittest
import transaction import transaction
from persistent import Persistent from persistent import Persistent
from ZODB.broken import BrokenModified from ZODB.broken import BrokenModified
from zExceptions import NotFound
from Products.ERP5Type.dynamic.portal_type_class import synchronizeDynamicModules from Products.ERP5Type.dynamic.portal_type_class import synchronizeDynamicModules
from Products.ERP5Type.dynamic.lazy_class import ERP5BaseBroken, InitGhostBase from Products.ERP5Type.dynamic.lazy_class import ERP5BaseBroken, InitGhostBase
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
...@@ -2011,14 +2012,8 @@ class TestZodbExtensionComponent(_TestZodbComponent): ...@@ -2011,14 +2012,8 @@ class TestZodbExtensionComponent(_TestZodbComponent):
test_component.invalidate() test_component.invalidate()
self.tic() self.tic()
# XXX-arnau: perhaps the error message should be more meaningful? self.assertRaisesRegexp(NotFound, "The specified module,"
try: " 'TestExternalMethodComponent', couldn't be found.", external_method)
external_method()
except RuntimeError, e:
self.assertEqual(e.message,
'external method could not be called because it is None')
else:
self.fail("TestExternalMethod should not be callable")
from Products.ERP5Type.Core.DocumentComponent import DocumentComponent from Products.ERP5Type.Core.DocumentComponent import DocumentComponent
......
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