Skip to content

Commit fa6ed2a

Browse files
KangOlantonrom1
authored andcommitted
[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 closes #265 Signed-off-by: Christophe Simonis (chs) <[email protected]>
1 parent 4e120db commit fa6ed2a

File tree

4 files changed

+48
-19
lines changed

4 files changed

+48
-19
lines changed

src/base/tests/test_util.py

Lines changed: 4 additions & 5 deletions
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
@@ -1735,11 +1734,11 @@ def test_expand_braces_failure(self, value):
17351734
]
17361735
)
17371736
def test_SelfPrint(self, value, expected):
1738-
evaluated = safe_eval(value, util.SelfPrintEvalContext(), nocopy=True)
1737+
evaluated = util.safe_eval(value, util.SelfPrintEvalContext())
17391738
self.assertEqual(str(evaluated), expected, "Self printed result differs")
17401739

17411740
replaced_value, ctx = util.SelfPrintEvalContext.preprocess(value)
1742-
evaluated = safe_eval(replaced_value, ctx, nocopy=True)
1741+
evaluated = util.safe_eval(replaced_value, ctx)
17431742
self.assertEqual(str(evaluated), expected, "Prepared self printed result differs")
17441743

17451744
@parametrize(
@@ -1759,7 +1758,7 @@ def test_SelfPrint(self, value, expected):
17591758
@unittest.skipUnless(util.ast_unparse is not None, "`ast.unparse` available from Python3.9")
17601759
def test_SelfPrint_prepare(self, value, expected):
17611760
replaced_value, ctx = util.SelfPrintEvalContext.preprocess(value)
1762-
evaluated = safe_eval(replaced_value, ctx, nocopy=True)
1761+
evaluated = util.safe_eval(replaced_value, ctx)
17631762
# extra fallback for old unparse from astunparse package
17641763
self.assertIn(str(evaluated), [expected, "({})".format(expected)])
17651764

@@ -1776,7 +1775,7 @@ def test_SelfPrint_prepare(self, value, expected):
17761775
def test_SelfPrint_failure(self, value):
17771776
# note: `safe_eval` will re-raise a ValueError
17781777
with self.assertRaises(ValueError):
1779-
safe_eval(value, util.SelfPrintEvalContext(), nocopy=True)
1778+
util.safe_eval(value)
17801779

17811780
@parametrize(
17821781
[

src/util/domains.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,13 @@
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
3836
from .inherit import for_each_inherit
39-
from .misc import SelfPrintEvalContext, ast_unparse, literal_replace, version_gte
37+
from .misc import SelfPrintEvalContext, ast_unparse, literal_replace, safe_eval, version_gte
4038
from .pg import column_exists, get_value_or_en_translation, table_exists
4139
from .records import edit_view
4240

@@ -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(safe_eval(replaced_domain, ctx))
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

Lines changed: 6 additions & 8 deletions
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

@@ -47,7 +46,7 @@ def make_index_name(table_name, column_name):
4746
from .exceptions import SleepyDeveloperError
4847
from .helpers import _dashboard_actions, _validate_model, resolve_model_fields_path, table_of_model
4948
from .inherit import for_each_inherit
50-
from .misc import SelfPrintEvalContext, log_progress, version_gte
49+
from .misc import log_progress, safe_eval, version_gte
5150
from .orm import env, invalidate
5251
from .pg import (
5352
SQLStr,
@@ -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 = 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 = 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)
@@ -1347,7 +1346,6 @@ def _update_field_usage_multi(cr, models, old, new, domain_adapter=None, skip_in
13471346

13481347
# ir.ui.view.custom
13491348
# adapt the context. The domain will be done by `adapt_domain`
1350-
eval_context = SelfPrintEvalContext()
13511349
def_old = "default_{}".format(old)
13521350
def_new = "default_{}".format(new)
13531351
match = "{0[old]}|{0[def_old]}".format(p)
@@ -1387,7 +1385,7 @@ def adapt_dict(d):
13871385
adapt_dict(d[vt])
13881386

13891387
for _, act in _dashboard_actions(cr, match, *only_models or ()):
1390-
context = safe_eval(act.get("context", "{}"), eval_context, nocopy=True)
1388+
context = safe_eval(act.get("context", "{}"))
13911389
adapt_dict(context)
13921390

13931391
if def_old in context:

src/util/misc.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,11 +495,11 @@ def preprocess(klass, expr):
495495
Example: [('company_id', 'in', [*company_ids, False])
496496
497497
Returns a pair with the new expression and an evaluation context that should
498-
be used in `safe_eval`.
498+
be used in :func:`~odoo.upgrade.util.misc.safe_eval`.
499499
500500
```
501501
>>> prepared_domain, context = util.SelfPrintEvalContext.preprocess(domain)
502-
>>> safe_eval(prepared_domain, context, nocopy=True)
502+
>>> safe_eval(prepared_domain, context)
503503
```
504504
505505
:meta private: exclude from online docs
@@ -533,6 +533,40 @@ def visit_UnaryOp(self, node):
533533
return (ast_unparse(visited).strip(), SelfPrintEvalContext(replacer.replaces))
534534

535535

536+
if version_gte("saas~18.4"):
537+
import odoo.tools.safe_eval as _safe_eval_mod
538+
539+
def safe_eval(expr, context=None):
540+
if context is None:
541+
context = SelfPrintEvalContext()
542+
543+
assert isinstance(expr, (str, bytes))
544+
assert isinstance(context, SelfPrintEvalContext)
545+
546+
c = _safe_eval_mod.test_expr(expr, _safe_eval_mod._SAFE_OPCODES, mode="eval", filename=None)
547+
context["__builtins__"] = dict(_safe_eval_mod._BUILTINS)
548+
try:
549+
return _safe_eval_mod.unsafe_eval(c, context, None)
550+
except _safe_eval_mod._BUBBLEUP_EXCEPTIONS:
551+
raise
552+
except Exception as e:
553+
raise ValueError("{!r} while evaluating\n{!r}".format(e, expr))
554+
finally:
555+
del context["__builtins__"]
556+
else:
557+
try:
558+
from odoo.tools.safe_eval import safe_eval as _safe_eval_func
559+
except ImportError:
560+
from openerp.tools.safe_eval import safe_eval as _safe_eval_func
561+
562+
def safe_eval(expr, context=None):
563+
if context is None:
564+
context = SelfPrintEvalContext()
565+
assert isinstance(context, SelfPrintEvalContext)
566+
567+
return _safe_eval_func(expr, context, nocopy=True)
568+
569+
536570
class _Replacer(ast.NodeTransformer):
537571
"""Replace literal nodes in an AST."""
538572

0 commit comments

Comments
 (0)