Robot Framework
body.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 re
17 
18 from .itemlist import ItemList
19 from .modelobject import ModelObject, full_name
20 
21 
23  KEYWORD = 'KEYWORD'
24  SETUP = 'SETUP'
25  TEARDOWN = 'TEARDOWN'
26  FOR = 'FOR'
27  ITERATION = 'ITERATION'
28  IF_ELSE_ROOT = 'IF/ELSE ROOT'
29  IF = 'IF'
30  ELSE_IF = 'ELSE IF'
31  ELSE = 'ELSE'
32  TRY_EXCEPT_ROOT = 'TRY/EXCEPT ROOT'
33  TRY = 'TRY'
34  EXCEPT = 'EXCEPT'
35  FINALLY = 'FINALLY'
36  WHILE = 'WHILE'
37  RETURN = 'RETURN'
38  CONTINUE = 'CONTINUE'
39  BREAK = 'BREAK'
40  MESSAGE = 'MESSAGE'
41  type = None
42  __slots__ = ['parent']
43 
44  @property
45 
50  id = property
51 
52  def id(self):
53  # This algorithm must match the id creation algorithm in the JavaScript side
54  # or linking to warnings and errors won't work.
55  if not self:
56  return None
57  if not self.parent:
58  return 'k1'
59  return self._get_id_get_id(self.parent)
60 
61  def _get_id(self, parent):
62  steps = []
63  if parent.has_setup:
64  steps.append(parent.setup)
65  if hasattr(parent, 'body'):
66  steps.extend(step for step in parent.body.flatten()
67  if step.type != self.MESSAGEMESSAGE)
68  if parent.has_teardown:
69  steps.append(parent.teardown)
70  return '%s-k%d' % (parent.id, steps.index(self) + 1)
71 
72  @property
73  has_setup = property
74 
75  def has_setup(self):
76  return False
77 
78  @property
79  has_teardown = property
80 
81  def has_teardown(self):
82  return False
83 
84 
85 
87  __slots__ = []
88  # Set using 'Body.register' when these classes are created.
89  keyword_class = None
90  for_class = None
91  if_class = None
92  try_class = None
93  while_class = None
94  return_class = None
95  continue_class = None
96  break_class = None
97  message_class = None
98 
99  def __init__(self, parent=None, items=None):
100  super().__init__(BodyItem, {'parent': parent}, items)
101 
102  @classmethod
103  def register(cls, item_class):
104  name_parts = re.findall('([A-Z][a-z]+)', item_class.__name__) + ['class']
105  name = '_'.join(name_parts).lower()
106  if not hasattr(cls, name):
107  raise TypeError("Cannot register '%s'." % name)
108  setattr(cls, name, item_class)
109  return item_class
110 
111  @property
112  create = property
113 
114  def create(self):
115  raise AttributeError(
116  f"'{full_name(self)}' object has no attribute 'create'. "
117  f"Use item specific methods like 'create_keyword' instead."
118  )
119 
120  def create_keyword(self, *args, **kwargs):
121  return self._create_create(self.keyword_classkeyword_class, 'create_keyword', args, kwargs)
122 
123  def _create(self, cls, name, args, kwargs):
124  if cls is None:
125  raise TypeError(f"'{full_name(self)}' object does not support '{name}'.")
126  return self.appendappend(cls(*args, **kwargs))
127 
128  def create_for(self, *args, **kwargs):
129  return self._create_create(self.for_classfor_class, 'create_for', args, kwargs)
130 
131  def create_if(self, *args, **kwargs):
132  return self._create_create(self.if_classif_class, 'create_if', args, kwargs)
133 
134  def create_try(self, *args, **kwargs):
135  return self._create_create(self.try_classtry_class, 'create_try', args, kwargs)
136 
137  def create_while(self, *args, **kwargs):
138  return self._create_create(self.while_classwhile_class, 'create_while', args, kwargs)
139 
140  def create_return(self, *args, **kwargs):
141  return self._create_create(self.return_classreturn_class, 'create_return', args, kwargs)
142 
143  def create_continue(self, *args, **kwargs):
144  return self._create_create(self.continue_classcontinue_class, 'create_continue', args, kwargs)
145 
146  def create_break(self, *args, **kwargs):
147  return self._create_create(self.break_classbreak_class, 'create_break', args, kwargs)
148 
149  def create_message(self, *args, **kwargs):
150  return self._create_create(self.message_classmessage_class, 'create_message', args, kwargs)
151 
152 
175  def filter(self, keywords=None, messages=None, predicate=None):
176  if messages is not None and not self.message_classmessage_class:
177  raise TypeError(f"'{full_name(self)}' object does not support "
178  f"filtering by 'messages'.")
179  return self._filter_filter([(self.keyword_classkeyword_class, keywords),
180  (self.message_classmessage_class, messages)], predicate)
181 
182  def _filter(self, types, predicate):
183  include = tuple(cls for cls, activated in types if activated is True and cls)
184  exclude = tuple(cls for cls, activated in types if activated is False and cls)
185  if include and exclude:
186  raise ValueError('Items cannot be both included and excluded by type.')
187  items = list(self)
188  if include:
189  items = [item for item in items if isinstance(item, include)]
190  if exclude:
191  items = [item for item in items if not isinstance(item, exclude)]
192  if predicate:
193  items = [item for item in items if predicate(item)]
194  return items
195 
196 
201  def flatten(self):
202  roots = BodyItem.IF_ELSE_ROOT, BodyItem.TRY_EXCEPT_ROOT
203  steps = []
204  for item in self:
205  if item.type in roots:
206  steps.extend(item.body)
207  else:
208  steps.append(item)
209  return steps
210 
211 
212 
216 class Body(BaseBody):
217  pass
218 
219 
220 
221 class Branches(BaseBody):
222  __slots__ = ['branch_class']
223 
224  def __init__(self, branch_class, parent=None, items=None):
225  self.branch_classbranch_class = branch_class
226  super().__init__(parent, items)
227 
228  def create_branch(self, *args, **kwargs):
229  return self.appendappend(self.branch_classbranch_class(*args, **kwargs))
Base class for Body and Branches objects.
Definition: body.py:86
def create_keyword(self, *args, **kwargs)
Definition: body.py:120
def _filter(self, types, predicate)
Definition: body.py:182
def create_continue(self, *args, **kwargs)
Definition: body.py:143
def _create(self, cls, name, args, kwargs)
Definition: body.py:123
def flatten(self)
Return steps so that IF and TRY structures are flattened.
Definition: body.py:201
def __init__(self, parent=None, items=None)
Definition: body.py:99
def create_if(self, *args, **kwargs)
Definition: body.py:131
def create_message(self, *args, **kwargs)
Definition: body.py:149
def create_return(self, *args, **kwargs)
Definition: body.py:140
def create_break(self, *args, **kwargs)
Definition: body.py:146
def create_try(self, *args, **kwargs)
Definition: body.py:134
def create_while(self, *args, **kwargs)
Definition: body.py:137
def register(cls, item_class)
Definition: body.py:103
def create_for(self, *args, **kwargs)
Definition: body.py:128
def filter(self, keywords=None, messages=None, predicate=None)
Filter body items based on type and/or custom predicate.
Definition: body.py:175
def _get_id(self, parent)
Definition: body.py:61
id
Item id in format like s1-t3-k1.
Definition: body.py:50
A list-like object representing body of a suite, a test or a keyword.
Definition: body.py:216
def __init__(self, branch_class, parent=None, items=None)
Definition: body.py:224
def create_branch(self, *args, **kwargs)
Definition: body.py:228
def append(self, item)
Definition: itemlist.py:36