# -*- coding: utf-8 -*-
##############################################################################
-# Copyright (C) 2004-2010 OpenERP s.a. (<http://www.openerp.com>).
+# Copyright (C) 2004-2012 OpenERP s.a. (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# - http://code.activestate.com/recipes/286134/
# - safe_eval in lp:~xrg/openobject-server/optimize-5.0
# - safe_eval in tryton http://hg.tryton.org/hgwebdir.cgi/trytond/rev/bbb5f73319ad
-# - python 2.6's ast.literal_eval
from opcode import HAVE_ARGUMENT, opmap, opname
from types import CodeType
import logging
-import os
-__all__ = ['test_expr', 'literal_eval', 'safe_eval', 'const_eval', 'ext_eval' ]
+__all__ = ['test_expr', 'safe_eval', 'const_eval']
# The time module is usually already provided in the safe_eval environment
# but some code, e.g. datetime.datetime.now() (Windows/Python 2.5.2, bug
_ALLOWED_MODULES = ['_strptime', 'time']
_CONST_OPCODES = set(opmap[x] for x in [
- 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP','POP_BLOCK','SETUP_LOOP',
- 'BUILD_LIST', 'BUILD_MAP', 'BUILD_TUPLE',
- 'LOAD_CONST', 'RETURN_VALUE', 'STORE_SUBSCR'] if x in opmap)
+ 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP', 'DUP_TOPX',
+ 'POP_BLOCK','SETUP_LOOP', 'BUILD_LIST', 'BUILD_MAP', 'BUILD_TUPLE',
+ 'LOAD_CONST', 'RETURN_VALUE', 'STORE_SUBSCR', 'STORE_MAP'] if x in opmap)
_EXPR_OPCODES = _CONST_OPCODES.union(set(opmap[x] for x in [
'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT',
'BINARY_DIVIDE', 'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE',
'BINARY_MODULO', 'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_SUBSCR',
'BINARY_LSHIFT', 'BINARY_RSHIFT', 'BINARY_AND', 'BINARY_XOR',
- 'BINARY_OR'] if x in opmap))
+ 'BINARY_OR', 'INPLACE_ADD', 'INPLACE_SUBTRACT', 'INPLACE_MULTIPLY',
+ 'INPLACE_DIVIDE', 'INPLACE_REMAINDER', 'INPLACE_POWER',
+ 'INPLACE_LEFTSHIFT', 'INPLACE_RIGHTSHIFT', 'INPLACE_AND',
+ 'INPLACE_XOR','INPLACE_OR'
+ ] if x in opmap))
_SAFE_OPCODES = _EXPR_OPCODES.union(set(opmap[x] for x in [
- 'STORE_MAP', 'LOAD_NAME', 'CALL_FUNCTION', 'COMPARE_OP', 'LOAD_ATTR',
+ 'LOAD_NAME', 'CALL_FUNCTION', 'COMPARE_OP', 'LOAD_ATTR',
'STORE_NAME', 'GET_ITER', 'FOR_ITER', 'LIST_APPEND', 'DELETE_NAME',
'JUMP_FORWARD', 'JUMP_IF_TRUE', 'JUMP_IF_FALSE', 'JUMP_ABSOLUTE',
'MAKE_FUNCTION', 'SLICE+0', 'SLICE+1', 'SLICE+2', 'SLICE+3',
'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'END_FINALLY'
] if x in opmap))
-_logger = logging.getLogger('safe_eval')
+_logger = logging.getLogger(__name__)
def _get_opcodes(codeobj):
"""_get_opcodes(codeobj) -> [opcodes]
expr = expr.strip()
code_obj = compile(expr, "", mode)
except (SyntaxError, TypeError):
- _logger.debug('Invalid eval expression', exc_info=True)
raise
- except Exception:
- _logger.debug('Disallowed or invalid eval expression', exc_info=True)
- raise ValueError("%s is not a valid expression" % expr)
+ except Exception, e:
+ import sys
+ exc_info = sys.exc_info()
+ raise ValueError, '"%s" while compiling\n%s' % (str(e), expr), exc_info[2]
for code in _get_opcodes(code_obj):
if code not in allowed_codes:
raise ValueError("opcode %s not allowed (%r)" % (opname[code], expr))
c = test_expr(expr, _EXPR_OPCODES)
return eval(c)
-
-# Port of Python 2.6's ast.literal_eval for use under Python 2.5
-SAFE_CONSTANTS = {'None': None, 'True': True, 'False': False}
-
-try:
- # first, try importing directly
- from ast import literal_eval
-except ImportError:
- import _ast as ast
-
- def _convert(node):
- if isinstance(node, ast.Str):
- return node.s
- elif isinstance(node, ast.Num):
- return node.n
- elif isinstance(node, ast.Tuple):
- return tuple(map(_convert, node.elts))
- elif isinstance(node, ast.List):
- return list(map(_convert, node.elts))
- elif isinstance(node, ast.Dict):
- return dict((_convert(k), _convert(v)) for k, v
- in zip(node.keys, node.values))
- elif isinstance(node, ast.Name):
- if node.id in SAFE_CONSTANTS:
- return SAFE_CONSTANTS[node.id]
- raise ValueError('malformed or disallowed expression')
-
- def parse(expr, filename='<unknown>', mode='eval'):
- """parse(source[, filename], mode]] -> code object
- Parse an expression into an AST node.
- Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST).
- """
- return compile(expr, filename, mode, ast.PyCF_ONLY_AST)
-
- def literal_eval(node_or_string):
- """literal_eval(expression) -> value
- Safely evaluate an expression node or a string containing a Python
- expression. The string or node provided may only consist of the
- following Python literal structures: strings, numbers, tuples,
- lists, dicts, booleans, and None.
-
- >>> literal_eval('[1,True,"spam"]')
- [1, True, 'spam']
-
- >>> literal_eval('1+3')
- Traceback (most recent call last):
- ...
- ValueError: malformed or disallowed expression
- """
- if isinstance(node_or_string, basestring):
- node_or_string = parse(node_or_string)
- if isinstance(node_or_string, ast.Expression):
- node_or_string = node_or_string.body
- return _convert(node_or_string)
-
-def _import(name, globals={}, locals={}, fromlist=[], level=-1):
+def _import(name, globals=None, locals=None, fromlist=None, level=-1):
+ if globals is None:
+ globals = {}
+ if locals is None:
+ locals = {}
+ if fromlist is None:
+ fromlist = []
if name in _ALLOWED_MODULES:
return __import__(name, globals, locals, level)
raise ImportError(name)
# isinstance() does not work below, we want *exactly* the dict class
if (globals_dict is not None and type(globals_dict) is not dict) \
or (locals_dict is not None and type(locals_dict) is not dict):
- logging.getLogger('safe_eval').warning('Looks like you are trying to pass a dynamic environment,"\
- "you should probably pass nocopy=True to safe_eval()')
+ _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 locals_dict is not None:
'set' : set
}
)
- return eval(test_expr(expr,_SAFE_OPCODES, mode=mode), globals_dict, locals_dict)
+ c = test_expr(expr, _SAFE_OPCODES, mode=mode)
+ return eval(c, globals_dict, locals_dict)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: