Robot Framework
evaluation.py
Go to the documentation of this file.
1 # Copyright 2008-2015 Nokia Networks
2 # Copyright 2016- Robot Framework Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 import builtins
17 import token
18 from collections.abc import MutableMapping
19 from io import StringIO
20 from tokenize import generate_tokens, untokenize
21 
22 from robot.errors import DataError
23 from robot.utils import get_error_message, type_name
24 
25 from .notfound import variable_not_found
26 
27 
28 PYTHON_BUILTINS = set(builtins.__dict__)
29 
30 
31 def evaluate_expression(expression, variable_store, modules=None, namespace=None):
32  try:
33  if not isinstance(expression, str):
34  raise TypeError(f'Expression must be string, got {type_name(expression)}.')
35  if not expression:
36  raise ValueError('Expression cannot be empty.')
37  return _evaluate(expression, variable_store, modules, namespace)
38  except Exception:
39  raise DataError(f"Evaluating expression '{expression}' failed: "
40  f"{get_error_message()}")
41 
42 
43 def _evaluate(expression, variable_store, modules=None, namespace=None):
44  if '$' in expression:
45  expression = _decorate_variables(expression, variable_store)
46  # Given namespace must be included in our custom local namespace to make
47  # it possible to detect which names are not found and should be imported
48  # automatically as modules. It must be also be used as the global namespace
49  # with `eval()` because lambdas and possibly other special constructs don't
50  # see the local namespace at all.
51  namespace = dict(namespace) if namespace else {}
52  if modules:
53  namespace.update(_import_modules(modules))
54  local_ns = EvaluationNamespace(variable_store, namespace)
55  return eval(expression, namespace, local_ns)
56 
57 
58 def _decorate_variables(expression, variable_store):
59  variable_started = False
60  variable_found = False
61  tokens = []
62  for toknum, tokval, _, _, _ in generate_tokens(StringIO(expression).readline):
63  if variable_started:
64  if toknum == token.NAME:
65  if tokval not in variable_store:
66  variable_not_found('$%s' % tokval,
67  variable_store.as_dict(decoration=False),
68  deco_braces=False)
69  tokval = 'RF_VAR_' + tokval
70  variable_found = True
71  else:
72  tokens.append((token.ERRORTOKEN, '$'))
73  variable_started = False
74  if toknum == token.ERRORTOKEN and tokval == '$':
75  variable_started = True
76  else:
77  tokens.append((toknum, tokval))
78  return untokenize(tokens).strip() if variable_found else expression
79 
80 
81 def _import_modules(module_names):
82  modules = {}
83  for name in module_names.replace(' ', '').split(','):
84  if not name:
85  continue
86  modules[name] = __import__(name)
87  # If we just import module 'root.sub', module 'root' is not found.
88  while '.' in name:
89  name, _ = name.rsplit('.', 1)
90  modules[name] = __import__(name)
91  return modules
92 
93 
94 class EvaluationNamespace(MutableMapping):
95 
96  def __init__(self, variable_store, namespace):
97  self.namespacenamespace = namespace
98  self.variablesvariables = variable_store
99 
100  def __getitem__(self, key):
101  if key.startswith('RF_VAR_'):
102  return self.variablesvariables[key[7:]]
103  if key in self.namespacenamespace:
104  return self.namespacenamespace[key]
105  return self._import_module_import_module(key)
106 
107  def __setitem__(self, key, value):
108  self.namespacenamespace[key] = value
109 
110  def __delitem__(self, key):
111  self.namespacenamespace.pop(key)
112 
113  def _import_module(self, name):
114  if name in PYTHON_BUILTINS:
115  raise KeyError
116  try:
117  return __import__(name)
118  except ImportError:
119  raise NameError(f"name '{name}' is not defined nor importable as module")
120 
121  def __iter__(self):
122  yield from self.variablesvariables
123  yield from self.namespacenamespace
124 
125  def __len__(self):
126  return len(self.variablesvariables) + len(self.namespacenamespace)
def __init__(self, variable_store, namespace)
Definition: evaluation.py:96
def evaluate_expression(expression, variable_store, modules=None, namespace=None)
Definition: evaluation.py:31
def _import_modules(module_names)
Definition: evaluation.py:81
def _evaluate(expression, variable_store, modules=None, namespace=None)
Definition: evaluation.py:43
def _decorate_variables(expression, variable_store)
Definition: evaluation.py:58
def variable_not_found(name, candidates, message=None, deco_braces=True)
Raise DataError for missing variable name.
Definition: notfound.py:26