summaryrefslogtreecommitdiff
path: root/openerp/tools/safe_eval.py
diff options
context:
space:
mode:
authorXavier Morel <xmo@odoo.com>2015-04-01 15:10:08 +0200
committerOlivier Dony <odo@openerp.com>2015-04-10 18:23:32 +0200
commit20b6003dce87728e55be616e6eee2cbb162bc871 (patch)
tree9e932735b87370df23892859cbaaa3df3ff492e0 /openerp/tools/safe_eval.py
parent60ad1fec4d2367c8d6b67fb3f1a78f081a501e1b (diff)
[IMP] safe_eval: speed up evaluation
* creating the huge builtins dict inline represents ~40% of safe_eval's total cost according to line_profiler * for the specific situation of opcodes, using a set difference is apparently much faster than for+if. all(gencomp) is also faster than for+if but not as much as letting Python do (almost) all of it might be that returning a set from _get_opcodes would be even faster, but didn't test it
Diffstat (limited to 'openerp/tools/safe_eval.py')
-rw-r--r--openerp/tools/safe_eval.py104
1 files changed, 52 insertions, 52 deletions
diff --git a/openerp/tools/safe_eval.py b/openerp/tools/safe_eval.py
index 4eef310f68c..c30d39ef02c 100644
--- a/openerp/tools/safe_eval.py
+++ b/openerp/tools/safe_eval.py
@@ -144,10 +144,12 @@ def assert_valid_codeobj(allowed_codes, code_obj, expr):
is found in ``code_obj``
"""
assert_no_dunder_name(code_obj, expr)
- for opcode in _get_opcodes(code_obj):
- if opcode not in allowed_codes:
- raise ValueError(
- "opcode %s not allowed (%r)" % (opname[opcode], expr))
+
+ # almost twice as fast as a manual iteration + condition when loading
+ # /web according to line_profiler
+ if set(_get_opcodes(code_obj)) - allowed_codes:
+ raise ValueError("forbidden opcode(s) in %r" % expr)
+
for const in code_obj.co_consts:
if isinstance(const, CodeType):
assert_valid_codeobj(allowed_codes, const, 'lambda')
@@ -227,7 +229,44 @@ def _import(name, globals=None, locals=None, fromlist=None, level=-1):
if name in _ALLOWED_MODULES:
return __import__(name, globals, locals, level)
raise ImportError(name)
-
+_BUILTINS = {
+ '__import__': _import,
+ 'True': True,
+ 'False': False,
+ 'None': None,
+ 'str': str,
+ 'unicode': unicode,
+ 'bool': bool,
+ 'int': int,
+ 'float': float,
+ 'long': long,
+ 'enumerate': enumerate,
+ 'dict': dict,
+ 'list': list,
+ 'tuple': tuple,
+ 'map': map,
+ 'abs': abs,
+ 'min': min,
+ 'max': max,
+ 'sum': sum,
+ 'reduce': reduce,
+ 'filter': filter,
+ 'round': round,
+ 'len': len,
+ 'repr': repr,
+ 'set': set,
+ 'all': all,
+ 'any': any,
+ 'ord': ord,
+ 'chr': chr,
+ 'cmp': cmp,
+ 'divmod': divmod,
+ 'isinstance': isinstance,
+ 'range': range,
+ 'xrange': xrange,
+ 'zip': zip,
+ 'Exception': Exception,
+}
def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=False, locals_builtins=False):
"""safe_eval(expression[, globals[, locals[, mode[, nocopy]]]]) -> result
@@ -245,12 +284,9 @@ def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=Fal
:throws NameError: If the expression provided accesses forbidden names
:throws ValueError: If the expression provided uses forbidden bytecode
"""
- if isinstance(expr, CodeType):
+ if type(expr) is CodeType:
raise TypeError("safe_eval does not allow direct evaluation of code objects.")
- if globals_dict is None:
- globals_dict = {}
-
# prevent altering the globals/locals from within the sandbox
# by taking a copy.
if not nocopy:
@@ -260,55 +296,19 @@ def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=Fal
_logger.warning(
"Looks like you are trying to pass a dynamic environment, "
"you should probably pass nocopy=True to safe_eval().")
-
- globals_dict = dict(globals_dict)
+ if globals_dict is not None:
+ globals_dict = dict(globals_dict)
if locals_dict is not None:
locals_dict = dict(locals_dict)
- globals_dict.update(
- __builtins__={
- '__import__': _import,
- 'True': True,
- 'False': False,
- 'None': None,
- 'str': str,
- 'unicode': unicode,
- 'bool': bool,
- 'int': int,
- 'float': float,
- 'long': long,
- 'enumerate': enumerate,
- 'dict': dict,
- 'list': list,
- 'tuple': tuple,
- 'map': map,
- 'abs': abs,
- 'min': min,
- 'max': max,
- 'sum': sum,
- 'reduce': reduce,
- 'filter': filter,
- 'round': round,
- 'len': len,
- 'repr': repr,
- 'set': set,
- 'all': all,
- 'any': any,
- 'ord': ord,
- 'chr': chr,
- 'cmp': cmp,
- 'divmod': divmod,
- 'isinstance': isinstance,
- 'range': range,
- 'xrange': xrange,
- 'zip': zip,
- 'Exception': Exception,
- }
- )
+ if globals_dict is None:
+ globals_dict = {}
+
+ globals_dict['__builtins__'] = _BUILTINS
if locals_builtins:
if locals_dict is None:
locals_dict = {}
- locals_dict.update(globals_dict.get('__builtins__'))
+ locals_dict.update(_BUILTINS)
c = test_expr(expr, _SAFE_OPCODES, mode=mode)
try:
return eval(c, globals_dict, locals_dict)