Robot Framework
replacer.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 from robot.errors import DataError, VariableError
17 from robot.output import librarylogger as logger
18 from robot.utils import (DotDict, escape, get_error_message, is_dict_like, is_list_like,
19  is_string, safe_str, type_name, unescape)
20 
21 from .finders import VariableFinder
22 from .search import VariableMatch, search_variable
23 
24 
26 
27  def __init__(self, variable_store):
28  self._finder_finder = VariableFinder(variable_store)
29 
30 
41  def replace_list(self, items, replace_until=None, ignore_errors=False):
42  items = list(items or [])
43  if replace_until is not None:
44  return self._replace_list_until_replace_list_until(items, replace_until, ignore_errors)
45  return list(self._replace_list_replace_list(items, ignore_errors))
46 
47  def _replace_list_until(self, items, replace_until, ignore_errors):
48  # @{list} variables can contain more or less arguments than needed.
49  # Therefore we need to go through items one by one, and escape possible
50  # extra items we got.
51  replaced = []
52  while len(replaced) < replace_until and items:
53  replaced.extend(self._replace_list_replace_list([items.pop(0)], ignore_errors))
54  if len(replaced) > replace_until:
55  replaced[replace_until:] = [escape(item)
56  for item in replaced[replace_until:]]
57  return replaced + items
58 
59  def _replace_list(self, items, ignore_errors):
60  for item in items:
61  for value in self._replace_list_item_replace_list_item(item, ignore_errors):
62  yield value
63 
64  def _replace_list_item(self, item, ignore_errors):
65  match = search_variable(item, ignore_errors=ignore_errors)
66  if not match:
67  return [unescape(match.string)]
68  value = self.replace_scalarreplace_scalar(match, ignore_errors)
69  if match.is_list_variable() and is_list_like(value):
70  return value
71  return [value]
72 
73 
79  def replace_scalar(self, item, ignore_errors=False):
80  match = self._search_variable_search_variable(item, ignore_errors=ignore_errors)
81  if not match:
82  return unescape(match.string)
83  return self._replace_scalar_replace_scalar(match, ignore_errors)
84 
85  def _search_variable(self, item, ignore_errors):
86  if isinstance(item, VariableMatch):
87  return item
88  return search_variable(item, ignore_errors=ignore_errors)
89 
90  def _replace_scalar(self, match, ignore_errors=False):
91  if not match.is_variable():
92  return self.replace_stringreplace_string(match, ignore_errors=ignore_errors)
93  return self._get_variable_value_get_variable_value(match, ignore_errors)
94 
95 
99  def replace_string(self, item, custom_unescaper=None, ignore_errors=False):
100  unescaper = custom_unescaper or unescape
101  match = self._search_variable_search_variable(item, ignore_errors=ignore_errors)
102  if not match:
103  return safe_str(unescaper(match.string))
104  return self._replace_string_replace_string(match, unescaper, ignore_errors)
105 
106  def _replace_string(self, match, unescaper, ignore_errors):
107  parts = []
108  while match:
109  parts.extend([
110  unescaper(match.before),
111  safe_str(self._get_variable_value_get_variable_value(match, ignore_errors))
112  ])
113  match = search_variable(match.after, ignore_errors=ignore_errors)
114  parts.append(unescaper(match.string))
115  return ''.join(parts)
116 
117  def _get_variable_value(self, match, ignore_errors):
118  match.resolve_base(self, ignore_errors)
119  # TODO: Do we anymore need to reserve `*{var}` syntax for anything?
120  if match.identifier == '*':
121  logger.warn(r"Syntax '%s' is reserved for future use. Please "
122  r"escape it like '\%s'." % (match, match))
123  return str(match)
124  try:
125  value = self._finder_finder.find(match)
126  if match.items:
127  value = self._get_variable_item_get_variable_item(match, value)
128  try:
129  value = self._validate_value_validate_value(match, value)
130  except VariableError:
131  raise
132  except:
133  raise VariableError("Resolving variable '%s' failed: %s"
134  % (match, get_error_message()))
135  except DataError:
136  if not ignore_errors:
137  raise
138  value = unescape(match.match)
139  return value
140 
141  def _get_variable_item(self, match, value):
142  name = match.name
143  for item in match.items:
144  if is_dict_like(value):
145  value = self._get_dict_variable_item_get_dict_variable_item(name, value, item)
146  elif hasattr(value, '__getitem__'):
147  value = self._get_sequence_variable_item_get_sequence_variable_item(name, value, item)
148  else:
149  raise VariableError(
150  "Variable '%s' is %s, which is not subscriptable, and "
151  "thus accessing item '%s' from it is not possible. To use "
152  "'[%s]' as a literal value, it needs to be escaped like "
153  "'\\[%s]'." % (name, type_name(value), item, item, item)
154  )
155  name = '%s[%s]' % (name, item)
156  return value
157 
158  def _get_sequence_variable_item(self, name, variable, index):
159  index = self.replace_scalarreplace_scalar(index)
160  try:
161  index = self._parse_sequence_variable_index_parse_sequence_variable_index(index)
162  except ValueError:
163  try:
164  return variable[index]
165  except TypeError:
166  raise VariableError("%s '%s' used with invalid index '%s'. "
167  "To use '[%s]' as a literal value, it needs "
168  "to be escaped like '\\[%s]'."
169  % (type_name(variable, capitalize=True), name,
170  index, index, index))
171  except:
172  raise VariableError("Accessing '%s[%s]' failed: %s"
173  % (name, index, get_error_message()))
174  try:
175  return variable[index]
176  except IndexError:
177  raise VariableError("%s '%s' has no item in index %d."
178  % (type_name(variable, capitalize=True), name, index))
179 
181  if isinstance(index, (int, slice)):
182  return index
183  if not is_string(index):
184  raise ValueError
185  if ':' not in index:
186  return int(index)
187  if index.count(':') > 2:
188  raise ValueError
189  return slice(*[int(i) if i else None for i in index.split(':')])
190 
191  def _get_dict_variable_item(self, name, variable, key):
192  key = self.replace_scalarreplace_scalar(key)
193  try:
194  return variable[key]
195  except KeyError:
196  raise VariableError("Dictionary '%s' has no key '%s'."
197  % (name, key))
198  except TypeError as err:
199  raise VariableError("Dictionary '%s' used with invalid key: %s"
200  % (name, err))
201 
202  def _validate_value(self, match, value):
203  if match.identifier == '@':
204  if not is_list_like(value):
205  raise VariableError("Value of variable '%s' is not list or "
206  "list-like." % match)
207  return list(value)
208  if match.identifier == '&':
209  if not is_dict_like(value):
210  raise VariableError("Value of variable '%s' is not dictionary "
211  "or dictionary-like." % match)
212  return DotDict(value)
213  return value
Used when variable does not exist.
Definition: errors.py:72
def _validate_value(self, match, value)
Definition: replacer.py:202
def _parse_sequence_variable_index(self, index)
Definition: replacer.py:180
def _replace_list_item(self, item, ignore_errors)
Definition: replacer.py:64
def _get_variable_value(self, match, ignore_errors)
Definition: replacer.py:117
def replace_string(self, item, custom_unescaper=None, ignore_errors=False)
Replaces variables from a string.
Definition: replacer.py:99
def _get_variable_item(self, match, value)
Definition: replacer.py:141
def _get_sequence_variable_item(self, name, variable, index)
Definition: replacer.py:158
def replace_list(self, items, replace_until=None, ignore_errors=False)
Replaces variables from a list of items.
Definition: replacer.py:41
def _replace_list_until(self, items, replace_until, ignore_errors)
Definition: replacer.py:47
def _get_dict_variable_item(self, name, variable, key)
Definition: replacer.py:191
def _replace_list(self, items, ignore_errors)
Definition: replacer.py:59
def __init__(self, variable_store)
Definition: replacer.py:27
def _search_variable(self, item, ignore_errors)
Definition: replacer.py:85
def _replace_scalar(self, match, ignore_errors=False)
Definition: replacer.py:90
def _replace_string(self, match, unescaper, ignore_errors)
Definition: replacer.py:106
def replace_scalar(self, item, ignore_errors=False)
Replaces variables from a scalar item.
Definition: replacer.py:79
def get_error_message()
Returns error message of the last occurred exception.
Definition: error.py:34
def escape(item)
Definition: escaping.py:31
def is_dict_like(item)
Definition: robottypes.py:72
def type_name(item, capitalize=False)
Return "non-technical" type name for objects and types.
Definition: robottypes.py:86
def is_list_like(item)
Definition: robottypes.py:66
def safe_str(item)
Definition: unic.py:21
def search_variable(string, identifiers='$@&% *', ignore_errors=False)
Definition: search.py:22