Robot Framework
context.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 contextlib import contextmanager
17 
18 from robot.errors import DataError
19 
20 
22 
23  def __init__(self):
24  self._contexts_contexts = []
25 
26  @property
27  current = property
28 
29  def current(self):
30  return self._contexts_contexts[-1] if self._contexts_contexts else None
31 
32  @property
33  top = property
34 
35  def top(self):
36  return self._contexts_contexts[0] if self._contexts_contexts else None
37 
38  def __iter__(self):
39  return iter(self._contexts_contexts)
40 
41  @property
42  namespaces = property
43 
44  def namespaces(self):
45  return (context.namespace for context in self)
46 
47  def start_suite(self, suite, namespace, output, dry_run=False):
48  ctx = _ExecutionContext(suite, namespace, output, dry_run)
49  self._contexts_contexts.append(ctx)
50  return ctx
51 
52  def end_suite(self):
53  self._contexts_contexts.pop()
54 
55 
56 # This is ugly but currently needed e.g. by BuiltIn
57 EXECUTION_CONTEXTS = ExecutionContexts()
58 
59 
61 
64  _started_keywords_threshold = 100
65 
66  def __init__(self, suite, namespace, output, dry_run=False):
67  self.suitesuite = suite
68  self.testtest = None
69  self.timeoutstimeouts = set()
70  self.namespacenamespace = namespace
71  self.outputoutput = output
72  self.dry_rundry_run = dry_run
73  self.in_suite_teardownin_suite_teardown = False
74  self.in_test_teardownin_test_teardown = False
75  self.in_keyword_teardownin_keyword_teardown = 0
76  self._started_keywords_started_keywords = 0
77  self.timeout_occurredtimeout_occurred = False
78  self.user_keywordsuser_keywords = []
79  self.step_typesstep_types = []
80 
81  @contextmanager
82  def suite_teardown(self):
83  self.in_suite_teardownin_suite_teardown = True
84  try:
85  yield
86  finally:
87  self.in_suite_teardownin_suite_teardown = False
88 
89  @contextmanager
90  def test_teardown(self, test):
91  self.variablesvariablesvariables.set_test('${TEST_STATUS}', test.status)
92  self.variablesvariablesvariables.set_test('${TEST_MESSAGE}', test.message)
93  self.in_test_teardownin_test_teardown = True
94  self._remove_timeout_remove_timeout(test.timeout)
95  try:
96  yield
97  finally:
98  self.in_test_teardownin_test_teardown = False
99 
100  @contextmanager
101  def keyword_teardown(self, error):
102  self.variablesvariablesvariables.set_keyword('${KEYWORD_STATUS}', 'FAIL' if error else 'PASS')
103  self.variablesvariablesvariables.set_keyword('${KEYWORD_MESSAGE}', str(error or ''))
104  self.in_keyword_teardownin_keyword_teardown += 1
105  try:
106  yield
107  finally:
108  self.in_keyword_teardownin_keyword_teardown -= 1
109 
110  @contextmanager
111  def user_keyword(self, handler):
112  self.user_keywordsuser_keywords.append(handler)
113  self.namespacenamespace.start_user_keyword()
114  try:
115  yield
116  finally:
117  self.namespacenamespace.end_user_keyword()
118  self.user_keywordsuser_keywords.pop()
119 
120  def warn_on_invalid_private_call(self, handler):
121  parent = self.user_keywordsuser_keywords[-1] if self.user_keywordsuser_keywords else None
122  if not parent or parent.source != handler.source:
123  self.warnwarn(f"Keyword '{handler.longname}' is private and should only "
124  f"be called by keywords in the same file.")
125 
126  @contextmanager
127  def timeout(self, timeout):
128  self._add_timeout_add_timeout(timeout)
129  try:
130  yield
131  finally:
132  self._remove_timeout_remove_timeout(timeout)
133 
134  @property
135  in_teardown = property
136 
137  def in_teardown(self):
138  return bool(self.in_suite_teardownin_suite_teardown or
139  self.in_test_teardownin_test_teardown or
140  self.in_keyword_teardownin_keyword_teardown)
141 
142  @property
143  variables = property
144 
145  def variables(self):
146  return self.namespacenamespace.variables
147 
148  def continue_on_failure(self, default=False):
149  parents = ([self.testtest] if self.testtest else []) + self.user_keywordsuser_keywords
150  for index, parent in enumerate(reversed(parents)):
151  if (parent.tags.robot('recursive-stop-on-failure')
152  or index == 0 and parent.tags.robot('stop-on-failure')):
153  return False
154  if (parent.tags.robot('recursive-continue-on-failure')
155  or index == 0 and parent.tags.robot('continue-on-failure')):
156  return True
157  return default or self.in_teardownin_teardownin_teardown
158 
159  @property
160  allow_loop_control = property
161 
163  for typ in reversed(self.step_typesstep_types):
164  if typ == 'ITERATION':
165  return True
166  if typ == 'KEYWORD':
167  return False
168  return False
169 
170  def end_suite(self, suite):
171  for name in ['${PREV_TEST_NAME}',
172  '${PREV_TEST_STATUS}',
173  '${PREV_TEST_MESSAGE}']:
174  self.variablesvariablesvariables.set_global(name, self.variablesvariablesvariables[name])
175  self.outputoutput.end_suite(suite)
176  self.namespacenamespace.end_suite(suite)
177  EXECUTION_CONTEXTS.end_suite()
178 
179  def set_suite_variables(self, suite):
180  self.variablesvariablesvariables['${SUITE_NAME}'] = suite.longname
181  self.variablesvariablesvariables['${SUITE_SOURCE}'] = suite.source or ''
182  self.variablesvariablesvariables['${SUITE_DOCUMENTATION}'] = suite.doc
183  self.variablesvariablesvariables['${SUITE_METADATA}'] = suite.metadata.copy()
184 
185  def report_suite_status(self, status, message):
186  self.variablesvariablesvariables['${SUITE_STATUS}'] = status
187  self.variablesvariablesvariables['${SUITE_MESSAGE}'] = message
188 
189  def start_test(self, test):
190  self.testtest = test
191  self._add_timeout_add_timeout(test.timeout)
192  self.namespacenamespace.start_test()
193  self.variablesvariablesvariables.set_test('${TEST_NAME}', test.name)
194  self.variablesvariablesvariables.set_test('${TEST_DOCUMENTATION}', test.doc)
195  self.variablesvariablesvariables.set_test('@{TEST_TAGS}', list(test.tags))
196 
197  def _add_timeout(self, timeout):
198  if timeout:
199  timeout.start()
200  self.timeoutstimeouts.add(timeout)
201 
202  def _remove_timeout(self, timeout):
203  if timeout in self.timeoutstimeouts:
204  self.timeoutstimeouts.remove(timeout)
205 
206  def end_test(self, test):
207  self.testtest = None
208  self._remove_timeout_remove_timeout(test.timeout)
209  self.namespacenamespace.end_test()
210  self.variablesvariablesvariables.set_suite('${PREV_TEST_NAME}', test.name)
211  self.variablesvariablesvariables.set_suite('${PREV_TEST_STATUS}', test.status)
212  self.variablesvariablesvariables.set_suite('${PREV_TEST_MESSAGE}', test.message)
213  self.timeout_occurredtimeout_occurred = False
214 
215  def start_keyword(self, keyword):
216  self._started_keywords_started_keywords += 1
217  if self._started_keywords_started_keywords > self._started_keywords_threshold_started_keywords_threshold:
218  raise DataError('Maximum limit of started keywords and control '
219  'structures exceeded.')
220  self.outputoutput.start_keyword(keyword)
221  if keyword.libname != 'BuiltIn':
222  self.step_typesstep_types.append(keyword.type)
223 
224  def end_keyword(self, keyword):
225  self.outputoutput.end_keyword(keyword)
226  self._started_keywords_started_keywords -= 1
227  if keyword.libname != 'BuiltIn':
228  self.step_typesstep_types.pop()
229 
230  def get_runner(self, name):
231  return self.namespacenamespace.get_runner(name)
232 
233  def trace(self, message):
234  self.outputoutput.trace(message)
235 
236  def debug(self, message):
237  self.outputoutput.debug(message)
238 
239  def info(self, message):
240  self.outputoutput.info(message)
241 
242  def warn(self, message):
243  self.outputoutput.warn(message)
244 
245  def fail(self, message):
246  self.outputoutput.fail(message)
247 
248  def skip(self, message):
249  self.outputoutput.skip(message)
def start_suite(self, suite, namespace, output, dry_run=False)
Definition: context.py:47
def continue_on_failure(self, default=False)
Definition: context.py:148
def report_suite_status(self, status, message)
Definition: context.py:185
def _remove_timeout(self, timeout)
Definition: context.py:202
def warn_on_invalid_private_call(self, handler)
Definition: context.py:120
def __init__(self, suite, namespace, output, dry_run=False)
Definition: context.py:66