diff --git a/CHANGELOG b/CHANGELOG index c74216ec97..ed7c6281fb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add cache to safe_eval * Use versioned configuration directory * Next and Previous scroll per page on list and don't loop * Add AccelGroup on search windows (CTRL+Return) diff --git a/tryton/common/common.py b/tryton/common/common.py index 9f5c16c6d7..a1599bf2ce 100644 --- a/tryton/common/common.py +++ b/tryton/common/common.py @@ -1180,24 +1180,34 @@ _ALLOWED_CODES = set(dis.opmap[x] for x in [ 'DELETE_NAME', 'JUMP_IF_TRUE', 'JUMP_IF_FALSE', 'BINARY_SUBSCR', ] if x in dis.opmap) +_SAFE_EVAL_CACHE = {} def safe_eval(source, data=None): if '__subclasses__' in source: raise ValueError('__subclasses__ not allowed') - c = compile(source, '', 'eval') - codes = [] - s = c.co_code - i = 0 - while i < len(s): - code = ord(s[i]) - codes.append(code) - if code >= dis.HAVE_ARGUMENT: - i += 3 - else: - i += 1 - for code in codes: - if code not in _ALLOWED_CODES: - raise ValueError('opcode %s not allowed' % dis.opname[code]) + if hashlib: + key = hashlib.md5(source).hexdigest() + else: + key = md5.new(source).hexdigest() + c = _SAFE_EVAL_CACHE.get(key) + if not c: + c = compile(source, '', 'eval') + codes = [] + s = c.co_code + i = 0 + while i < len(s): + code = ord(s[i]) + codes.append(code) + if code >= dis.HAVE_ARGUMENT: + i += 3 + else: + i += 1 + for code in codes: + if code not in _ALLOWED_CODES: + raise ValueError('opcode %s not allowed' % dis.opname[code]) + if len(_SAFE_EVAL_CACHE) > 1024: + _SAFE_EVAL_CACHE.clear() + _SAFE_EVAL_CACHE[key] = c return eval(c, {'__builtins__': { 'True': True, 'False': False,