Skip to content

Commit 5b158c9

Browse files
committed
[IMP] adapt code to new simplified safe_eval API
`safe_eval` loose the `nocopy` option. As we rely on it when evaluating expression, we need to reimplement `safe_eval` to do the evaluation using the `SelfPrintEvalContext`. See odoo/odoo#206846 See odoo/enterprise#83818
1 parent bdab4ce commit 5b158c9

File tree

4 files changed

+31
-14
lines changed

4 files changed

+31
-14
lines changed

src/base/tests/test_util.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
from odoo import modules
1919
from odoo.tools import mute_logger
20-
from odoo.tools.safe_eval import safe_eval
2120

2221
from odoo.addons.base.maintenance.migrations import util
2322
from odoo.addons.base.maintenance.migrations.testing import UnitTestCase, parametrize
@@ -1690,11 +1689,11 @@ def test_expand_braces_failure(self, value):
16901689
]
16911690
)
16921691
def test_SelfPrint(self, value, expected):
1693-
evaluated = safe_eval(value, util.SelfPrintEvalContext(), nocopy=True)
1692+
evaluated = util.SelfPrintEvalContext().safe_eval(value)
16941693
self.assertEqual(str(evaluated), expected, "Self printed result differs")
16951694

16961695
replaced_value, ctx = util.SelfPrintEvalContext.preprocess(value)
1697-
evaluated = safe_eval(replaced_value, ctx, nocopy=True)
1696+
evaluated = ctx.safe_eval(replaced_value)
16981697
self.assertEqual(str(evaluated), expected, "Prepared self printed result differs")
16991698

17001699
@parametrize(
@@ -1714,7 +1713,7 @@ def test_SelfPrint(self, value, expected):
17141713
@unittest.skipUnless(util.ast_unparse is not None, "`ast.unparse` available from Python3.9")
17151714
def test_SelfPrint_prepare(self, value, expected):
17161715
replaced_value, ctx = util.SelfPrintEvalContext.preprocess(value)
1717-
evaluated = safe_eval(replaced_value, ctx, nocopy=True)
1716+
evaluated = ctx.safe_eval(replaced_value)
17181717
# extra fallback for old unparse from astunparse package
17191718
self.assertIn(str(evaluated), [expected, "({})".format(expected)])
17201719

@@ -1731,7 +1730,7 @@ def test_SelfPrint_prepare(self, value, expected):
17311730
def test_SelfPrint_failure(self, value):
17321731
# note: `safe_eval` will re-raise a ValueError
17331732
with self.assertRaises(ValueError):
1734-
safe_eval(value, util.SelfPrintEvalContext(), nocopy=True)
1733+
util.SelfPrintEvalContext().safe_eval(value)
17351734

17361735
@parametrize(
17371736
[

src/util/domains.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,8 @@
2828

2929
try:
3030
from odoo.tools import exception_to_unicode
31-
from odoo.tools.safe_eval import safe_eval
3231
except ImportError:
3332
from openerp.tools import exception_to_unicode
34-
from openerp.tools.safe_eval import safe_eval
3533

3634
from .const import NEARLYWARN
3735
from .helpers import _dashboard_actions, _validate_model, resolve_model_fields_path
@@ -300,7 +298,7 @@ def _adapt_one_domain_old(cr, target_model, old, new, model, domain, adapter=Non
300298
if isinstance(domain, basestring):
301299
try:
302300
replaced_domain, ctx = SelfPrintEvalContext.preprocess(domain)
303-
eval_dom = normalize_domain(safe_eval(replaced_domain, ctx, nocopy=True))
301+
eval_dom = normalize_domain(ctx.safe_eval(replaced_domain))
304302
except Exception as e:
305303
oops = exception_to_unicode(e)
306304
_logger.log(NEARLYWARN, "Cannot evaluate %r domain: %r: %s", model, domain, oops)

src/util/fields.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import logging
1313
import re
1414
import warnings
15+
from ast import literal_eval
1516

1617
import psycopg2
1718
from psycopg2 import sql
@@ -25,11 +26,9 @@
2526
try:
2627
from odoo import release
2728
from odoo.tools.misc import mute_logger
28-
from odoo.tools.safe_eval import safe_eval
2929
except ImportError:
3030
from openerp import release
3131
from openerp.tools.misc import mute_logger
32-
from openerp.tools.safe_eval import safe_eval
3332

3433
from .domains import FALSE_LEAF, TRUE_LEAF
3534

@@ -117,7 +116,7 @@ def _remove_field_from_filters(cr, model, field):
117116
[model, r"\y{}\y".format(field)],
118117
)
119118
for id_, name, context_s in cr.fetchall():
120-
context = safe_eval(context_s or "{}", SelfPrintEvalContext(), nocopy=True)
119+
context = SelfPrintEvalContext().safe_eval(context_s or "{}")
121120
changed = _remove_field_from_context(context, field)
122121
cr.execute("UPDATE ir_filters SET context = %s WHERE id = %s", [unicode(context), id_])
123122
if changed:
@@ -206,7 +205,7 @@ def remove_field(cr, model, fieldname, cascade=False, drop_column=True, skip_inh
206205

207206
# clean dashboard's contexts
208207
for id_, action in _dashboard_actions(cr, r"\y{}\y".format(fieldname), model):
209-
context = safe_eval(action.get("context", "{}"), SelfPrintEvalContext(), nocopy=True)
208+
context = SelfPrintEvalContext().safe_eval(action.get("context", "{}"))
210209
changed = _remove_field_from_context(context, fieldname)
211210
action.set("context", unicode(context))
212211
if changed:
@@ -350,7 +349,7 @@ def adapter(leaf, is_or, negated):
350349
)
351350
for alias_id, defaults_s in cr.fetchall():
352351
try:
353-
defaults = dict(safe_eval(defaults_s)) # XXX literal_eval should works.
352+
defaults = dict(literal_eval(defaults_s))
354353
except Exception:
355354
continue
356355
defaults.pop(fieldname, None)
@@ -1387,7 +1386,7 @@ def adapt_dict(d):
13871386
adapt_dict(d[vt])
13881387

13891388
for _, act in _dashboard_actions(cr, match, *only_models or ()):
1390-
context = safe_eval(act.get("context", "{}"), eval_context, nocopy=True)
1389+
context = eval_context.safe_eval(act.get("context", "{}"))
13911390
adapt_dict(context)
13921391

13931392
if def_old in context:

src/util/misc.py

+21
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
try:
1616
from odoo import release
1717
from odoo.modules.module import get_module_path
18+
from odoo.tools import safe_eval as _safe_eval
1819
from odoo.tools.parse_version import parse_version
1920
except ImportError:
2021
from openerp import release
2122
from openerp.modules.module import get_module_path
23+
from openerp.tools import safe_eval as _safe_eval
2224
from openerp.tools.parse_version import parse_version
2325

2426
from .exceptions import SleepyDeveloperError
@@ -532,6 +534,25 @@ def visit_UnaryOp(self, node):
532534
visited = replacer.visit(root)
533535
return (ast_unparse(visited).strip(), SelfPrintEvalContext(replacer.replaces))
534536

537+
if version_gte("saas~18.3"):
538+
539+
def safe_eval(self, expr, mode="eval"):
540+
assert isinstance(expr, (str, bytes))
541+
c = _safe_eval.test_expr(expr, _safe_eval._SAFE_OPCODES, mode=mode, filename=None)
542+
self["__builtins__"] = dict(_safe_eval._BUILTINS)
543+
try:
544+
return _safe_eval.unsafe_eval(c, self, None)
545+
except _safe_eval._BUBBLEUP_EXCEPTIONS:
546+
raise
547+
except Exception as e:
548+
raise ValueError("{!r} while evaluating\n{!r}".format(e, expr))
549+
finally:
550+
del self["__builtins__"]
551+
else:
552+
553+
def safe_eval(self, expr, mode="eval"):
554+
return _safe_eval.safe_eval(expr, self, nocopy=True)
555+
535556

536557
class _Replacer(ast.NodeTransformer):
537558
"""Replace literal nodes in an AST."""

0 commit comments

Comments
 (0)