diff --git a/addons/web/static/src/core/py_js/py.js b/addons/web/static/src/core/py_js/py.js index 322e5287bfa..0c482d90068 100644 --- a/addons/web/static/src/core/py_js/py.js +++ b/addons/web/static/src/core/py_js/py.js @@ -29,10 +29,10 @@ export function parseExpr(expr) { * Evaluates a python expression * * @param {string} expr - * @param {Object} context + * @param {Object} [context] * @returns {any} */ -export function evaluateExpr(expr, context) { +export function evaluateExpr(expr, context = {}) { const ast = parseExpr(expr); return evaluate(ast, context); } diff --git a/addons/web/static/src/core/py_js/py_interpreter.js b/addons/web/static/src/core/py_js/py_interpreter.js index 6343310a34a..d2cac83b4f1 100644 --- a/addons/web/static/src/core/py_js/py_interpreter.js +++ b/addons/web/static/src/core/py_js/py_interpreter.js @@ -2,7 +2,7 @@ import { BUILTINS } from "./py_builtin"; import { PyDate, PyDateTime, PyRelativeDelta, PyTime } from "./py_date"; -import { parseArgs, PY_DICT } from "./py_utils"; +import { parseArgs, PY_DICT, toPyDict } from "./py_utils"; // ----------------------------------------------------------------------------- // Types @@ -199,6 +199,16 @@ const DICT = { */ export function evaluate(ast, context = {}) { const dicts = new Set(); + let pyContext; + const evalContext = Object.create(context); + Object.defineProperty(evalContext, "context", { + get() { + if (!pyContext) { + pyContext = toPyDict(context); + } + return pyContext; + }, + }); /** * @param {AST} ast @@ -209,8 +219,8 @@ export function evaluate(ast, context = {}) { case 1 /* String */: return ast.value; case 5 /* Name */: - if (ast.value in context) { - return context[ast.value]; + if (ast.value in evalContext) { + return evalContext[ast.value]; } else if (ast.value in BUILTINS) { return BUILTINS[ast.value]; } else { @@ -221,9 +231,9 @@ export function evaluate(ast, context = {}) { case 2 /* Boolean */: return ast.value; case 6 /* UnaryOperator */: - return applyUnaryOp(ast, context); + return applyUnaryOp(ast, evalContext); case 7 /* BinaryOperator */: - return applyBinaryOp(ast, context); + return applyBinaryOp(ast, evalContext); case 14 /* BooleanOperator */: const left = _evaluate(ast.left); if (ast.op === "and") { diff --git a/addons/web/static/tests/core/py_js/py_interpreter_tests.js b/addons/web/static/tests/core/py_js/py_interpreter_tests.js index 5d969a7d9e8..d9d3b9c115a 100644 --- a/addons/web/static/tests/core/py_js/py_interpreter_tests.js +++ b/addons/web/static/tests/core/py_js/py_interpreter_tests.js @@ -1,7 +1,6 @@ /** @odoo-module **/ import { evaluateExpr } from "@web/core/py_js/py"; -import { toPyDict } from "@web/core/py_js/py_utils"; QUnit.module("py", {}, () => { QUnit.module("interpreter", () => { @@ -138,11 +137,13 @@ QUnit.module("py", {}, () => { assert.deepEqual(evaluateExpr("foo", { foo: [1, 2, 3] }), [1, 2, 3]); }); - QUnit.test("python values in context", (assert) => { - const context = toPyDict({ b: 3 }); - assert.strictEqual(evaluateExpr("context.get('b', 54)", { context }), 3); - assert.strictEqual(evaluateExpr("context.get('c', 54)", { context }), 54); - }); + QUnit.test( + "special case for context: the eval context can be accessed as 'context'", + (assert) => { + assert.strictEqual(evaluateExpr("context.get('b', 54)", { b: 3 }), 3); + assert.strictEqual(evaluateExpr("context.get('c', 54)", { b: 3 }), 54); + } + ); QUnit.test("throw error if name is not defined", (assert) => { assert.throws(() => evaluateExpr("a"));