Skip to content

Commit 177a41c

Browse files
committed
Fix Evaluate with custom namespace used in lambda.
Fixes robotframework#3681.
1 parent 67ca937 commit 177a41c

File tree

3 files changed

+19
-4
lines changed

3 files changed

+19
-4
lines changed

atest/robot/standard_libraries/builtin/evaluate.robot

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ Custom namespace
4343
Custom namespace is case-sensitive
4444
Check Test Case ${TESTNAME}
4545

46+
Custon namespace used in lambda
47+
Check Test Case ${TESTNAME}
48+
4649
Namespace from Get Variables
4750
Check Test Case ${TESTNAME}
4851

atest/testdata/standard_libraries/builtin/evaluate.robot

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ Custom namespace is case-sensitive
121121
Should Be Equal ${result} xyz
122122
Evaluate B namespace=${ns}
123123

124+
Custon namespace used in lambda
125+
${ns} = Create Dictionary alphabet=aAbBcCdDeEfFgGhHiIjJkKlLmMnNoO input=Hello
126+
${sorted} = Evaluate ''.join(sorted(input, key=lambda word: [alphabet.find(c) for c in word])) namespace=${ns}
127+
Should Be Equal ${sorted} eHllo
128+
124129
Namespace from Get Variables
125130
${foo} = Set variable value
126131
${ns} = Get variables no_decoration=Yes

src/robot/variables/evaluation.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,16 @@ def evaluate_expression(expression, variable_store, modules=None,
4747
def _evaluate(expression, variable_store, modules=None, namespace=None):
4848
if '$' in expression:
4949
expression = _decorate_variables(expression, variable_store)
50-
global_ns = _import_modules(modules) if modules else {}
50+
# Given namespace must be included in our custom local namespace to make
51+
# it possible to detect which names are not found and should be imported
52+
# automatically as modules. It must be also be used as the global namespace
53+
# with `eval()` because lambdas and possibly other special constructs don't
54+
# see the local namespace at all.
55+
namespace = dict(namespace) if namespace else {}
56+
if modules:
57+
namespace.update(_import_modules(modules))
5158
local_ns = EvaluationNamespace(variable_store, namespace)
52-
return eval(expression, global_ns, local_ns)
59+
return eval(expression, namespace, local_ns)
5360

5461

5562
def _decorate_variables(expression, variable_store):
@@ -93,8 +100,8 @@ def _import_modules(module_names):
93100
# namespace. Using just Mapping would allow removing __set/delitem__.
94101
class EvaluationNamespace(MutableMapping):
95102

96-
def __init__(self, variable_store, namespace=None):
97-
self.namespace = {} if namespace is None else dict(namespace)
103+
def __init__(self, variable_store, namespace):
104+
self.namespace = namespace
98105
self.variables = variable_store
99106

100107
def __getitem__(self, key):

0 commit comments

Comments
 (0)