Robot Framework
statements.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 ast
17 import re
18 
19 from robot.conf import Language
20 from robot.running.arguments import UserKeywordArgumentParser
21 from robot.utils import is_list_like, normalize_whitespace, seq2str, split_from_equals
22 from robot.variables import is_scalar_assign, is_dict_variable, search_variable
23 
24 from ..lexer import Token
25 
26 
27 FOUR_SPACES = ' '
28 EOL = '\n'
29 
30 
31 class Statement(ast.AST):
32  type = None
33  handles_types = ()
34 
37  _fields = ('type', 'tokens')
38 
41  _attributes = ('lineno', 'col_offset', 'end_lineno', 'end_col_offset', 'errors')
42 
45  _statement_handlers = {}
46 
47  def __init__(self, tokens, errors=()):
48  self.tokenstokens = tuple(tokens)
49  self.errorserrors = errors
50 
51  @property
52  lineno = property
53 
54  def lineno(self):
55  return self.tokenstokens[0].lineno if self.tokenstokens else -1
56 
57  @property
58  col_offset = property
59 
60  def col_offset(self):
61  return self.tokenstokens[0].col_offset if self.tokenstokens else -1
62 
63  @property
64  end_lineno = property
65 
66  def end_lineno(self):
67  return self.tokenstokens[-1].lineno if self.tokenstokens else -1
68 
69  @property
70  end_col_offset = property
71 
72  def end_col_offset(self):
73  return self.tokenstokens[-1].end_col_offset if self.tokenstokens else -1
74 
75  @classmethod
76  def register(cls, subcls):
77  types = subcls.handles_types or (subcls.type,)
78  for typ in types:
79  cls._statement_handlers_statement_handlers[typ] = subcls
80  return subcls
81 
82  @classmethod
83  def from_tokens(cls, tokens):
84  handlers = cls._statement_handlers_statement_handlers
85  for token in tokens:
86  if token.type in handlers:
87  return handlers[token.type](tokens)
88  if any(token.type == Token.ASSIGN for token in tokens):
89  return KeywordCall(tokens)
90  return EmptyLine(tokens)
91 
92  @classmethod
93 
107  def from_params(cls, *args, **kwargs):
108  raise NotImplementedError
109 
110  @property
111  data_tokens = property
112 
113  def data_tokens(self):
114  return [t for t in self.tokenstokens if t.type not in Token.NON_DATA_TOKENS]
115 
116 
121  def get_token(self, *types):
122  for token in self.tokenstokens:
123  if token.type in types:
124  return token
125  return None
126 
127 
128  def get_tokens(self, *types):
129  return [t for t in self.tokenstokens if t.type in types]
130 
131 
136  def get_value(self, type, default=None):
137  token = self.get_tokenget_token(type)
138  return token.value if token else default
139 
140 
141  def get_values(self, *types):
142  return tuple(t.value for t in self.tokenstokens if t.type in types)
143 
144  @property
145  lines = property
146 
147  def lines(self):
148  line = []
149  for token in self.tokenstokens:
150  line.append(token)
151  if token.type == Token.EOL:
152  yield line
153  line = []
154  if line:
155  yield line
156 
157  def validate(self, context):
158  pass
159 
160  def __iter__(self):
161  return iter(self.tokens)
162 
163  def __len__(self):
164  return len(self.tokenstokens)
165 
166  def __getitem__(self, item):
167  return self.tokenstokens[item]
168 
169  def __repr__(self):
170  errors = '' if not self.errorserrors else ', errors=%s' % list(self.errorserrors)
171  return '%s(tokens=%s%s)' % (type(self).__name__, list(self.tokenstokens), errors)
172 
173 
175 
176  def _join_value(self, tokens):
177  lines = self._get_lines_get_lines(tokens)
178  return ''.join(self._yield_lines_with_newlines_yield_lines_with_newlines(lines))
179 
180  def _get_lines(self, tokens):
181  lines = []
182  line = None
183  lineno = -1
184  for t in tokens:
185  if t.lineno != lineno:
186  line = []
187  lines.append(line)
188  line.append(t.value)
189  lineno = t.lineno
190  return [' '.join(line) for line in lines]
191 
192  def _yield_lines_with_newlines(self, lines):
193  last_index = len(lines) - 1
194  for index, line in enumerate(lines):
195  yield line
196  if index < last_index and not self._escaped_or_has_newline_escaped_or_has_newline(line):
197  yield '\n'
198 
199  def _escaped_or_has_newline(self, line):
200  match = re.search(r'(\\+)n?$', line)
201  return match and len(match.group(1)) % 2 == 1
202 
203 
205 
206  @property
207  value = property
208 
209  def value(self):
210  values = self.get_valuesget_values(Token.NAME, Token.ARGUMENT)
211  if values and values[0].upper() != 'NONE':
212  return values[0]
213  return None
214 
215 
217 
218  @property
219  values = property
220 
221  def values(self):
222  return self.get_valuesget_values(Token.ARGUMENT)
223 
224 
226 
227  @property
228  name = property
229 
230  def name(self):
231  return self.get_valueget_value(Token.NAME)
232 
233  @property
234  args = property
235 
236  def args(self):
237  return self.get_valuesget_values(Token.ARGUMENT)
238 
239 
240 @Statement.register
242  handles_types = (Token.SETTING_HEADER, Token.VARIABLE_HEADER,
243  Token.TESTCASE_HEADER, Token.TASK_HEADER,
244  Token.KEYWORD_HEADER, Token.COMMENT_HEADER)
245 
246  @classmethod
247  def from_params(cls, type, name=None, eol=EOL):
248  if not name:
249  names = ('Settings', 'Variables', 'Test Cases', 'Tasks',
250  'Keywords', 'Comments')
251  name = dict(zip(cls.handles_typeshandles_typeshandles_types, names))[type]
252  if not name.startswith('*'):
253  name = '*** %s ***' % name
254  return cls([
255  Token(type, name),
256  Token('EOL', '\n')
257  ])
258 
259  @property
260  type = property
261 
262  def type(self):
263  token = self.get_tokenget_token(*self.handles_typeshandles_typeshandles_types)
264  return token.type
265 
266  @property
267  name = property
268 
269  def name(self):
270  token = self.get_tokenget_token(*self.handles_typeshandles_typeshandles_types)
271  return normalize_whitespace(token.value).strip('* ')
272 
273 
274 @Statement.register
276  type = Token.LIBRARY
277 
278  @classmethod
279  def from_params(cls, name, args=(), alias=None, separator=FOUR_SPACES, eol=EOL):
280  tokens = [Token(Token.LIBRARY, 'Library'),
281  Token(Token.SEPARATOR, separator),
282  Token(Token.NAME, name)]
283  for arg in args:
284  tokens.extend([Token(Token.SEPARATOR, separator),
285  Token(Token.ARGUMENT, arg)])
286  if alias is not None:
287  tokens.extend([Token(Token.SEPARATOR, separator),
288  Token(Token.WITH_NAME),
289  Token(Token.SEPARATOR, separator),
290  Token(Token.NAME, alias)])
291  tokens.append(Token(Token.EOL, eol))
292  return cls(tokens)
293 
294  @property
295  name = property
296 
297  def name(self):
298  return self.get_valueget_value(Token.NAME)
299 
300  @property
301  args = property
302 
303  def args(self):
304  return self.get_valuesget_values(Token.ARGUMENT)
305 
306  @property
307  alias = property
308 
309  def alias(self):
310  with_name = self.get_tokenget_token(Token.WITH_NAME)
311  return self.get_tokensget_tokens(Token.NAME)[-1].value if with_name else None
312 
313 
314 @Statement.register
316  type = Token.RESOURCE
317 
318  @classmethod
319  def from_params(cls, name, separator=FOUR_SPACES, eol=EOL):
320  return cls([
321  Token(Token.RESOURCE, 'Resource'),
322  Token(Token.SEPARATOR, separator),
323  Token(Token.NAME, name),
324  Token(Token.EOL, eol)
325  ])
326 
327  @property
328  name = property
329 
330  def name(self):
331  return self.get_valueget_value(Token.NAME)
332 
333 
334 @Statement.register
336  type = Token.VARIABLES
337 
338  @classmethod
339  def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL):
340  tokens = [Token(Token.VARIABLES, 'Variables'),
341  Token(Token.SEPARATOR, separator),
342  Token(Token.NAME, name)]
343  for arg in args:
344  tokens.extend([Token(Token.SEPARATOR, separator),
345  Token(Token.ARGUMENT, arg)])
346  tokens.append(Token(Token.EOL, eol))
347  return cls(tokens)
348 
349  @property
350  name = property
351 
352  def name(self):
353  return self.get_valueget_value(Token.NAME)
354 
355  @property
356  args = property
357 
358  def args(self):
359  return self.get_valuesget_values(Token.ARGUMENT)
360 
361 
362 @Statement.register
364  type = Token.DOCUMENTATION
365 
366  @classmethod
367  def from_params(cls, value, indent=FOUR_SPACES, separator=FOUR_SPACES,
368  eol=EOL, settings_section=True):
369  if settings_section:
370  tokens = [Token(Token.DOCUMENTATION, 'Documentation'),
371  Token(Token.SEPARATOR, separator)]
372  else:
373  tokens = [Token(Token.SEPARATOR, indent),
374  Token(Token.DOCUMENTATION, '[Documentation]'),
375  Token(Token.SEPARATOR, separator)]
376  multiline_separator = ' ' * (len(tokens[-2].value) + len(separator) - 3)
377  doc_lines = value.splitlines()
378  if doc_lines:
379  tokens.extend([Token(Token.ARGUMENT, doc_lines[0]),
380  Token(Token.EOL, eol)])
381  for line in doc_lines[1:]:
382  if not settings_section:
383  tokens.append(Token(Token.SEPARATOR, indent))
384  tokens.append(Token(Token.CONTINUATION))
385  if line:
386  tokens.extend([Token(Token.SEPARATOR, multiline_separator),
387  Token(Token.ARGUMENT, line)])
388  tokens.append(Token(Token.EOL, eol))
389  return cls(tokens)
390 
391  @property
392  value = property
393 
394  def value(self):
395  tokens = self.get_tokensget_tokens(Token.ARGUMENT)
396  return self._join_value_join_value(tokens)
397 
398 
399 @Statement.register
401  type = Token.METADATA
402 
403  @classmethod
404  def from_params(cls, name, value, separator=FOUR_SPACES, eol=EOL):
405  tokens = [Token(Token.METADATA, 'Metadata'),
406  Token(Token.SEPARATOR, separator),
407  Token(Token.NAME, name)]
408  metadata_lines = value.splitlines()
409  if metadata_lines:
410  tokens.extend([Token(Token.SEPARATOR, separator),
411  Token(Token.ARGUMENT, metadata_lines[0]),
412  Token(Token.EOL, eol)])
413  for line in metadata_lines[1:]:
414  tokens.extend([Token(Token.CONTINUATION),
415  Token(Token.SEPARATOR, separator),
416  Token(Token.ARGUMENT, line),
417  Token(Token.EOL, eol)])
418  return cls(tokens)
419 
420  @property
421  name = property
422 
423  def name(self):
424  return self.get_valueget_value(Token.NAME)
425 
426  @property
427  value = property
428 
429  def value(self):
430  tokens = self.get_tokensget_tokens(Token.ARGUMENT)
431  return self._join_value_join_value(tokens)
432 
433 
434 @Statement.register
436  type = Token.FORCE_TAGS
437 
438  @classmethod
439  def from_params(cls, values, separator=FOUR_SPACES, eol=EOL):
440  tokens = [Token(Token.FORCE_TAGS, 'Force Tags')]
441  for tag in values:
442  tokens.extend([Token(Token.SEPARATOR, separator),
443  Token(Token.ARGUMENT, tag)])
444  tokens.append(Token(Token.EOL, eol))
445  return cls(tokens)
446 
447 
448 @Statement.register
450  type = Token.DEFAULT_TAGS
451 
452  @classmethod
453  def from_params(cls, values, separator=FOUR_SPACES, eol=EOL):
454  tokens = [Token(Token.DEFAULT_TAGS, 'Default Tags')]
455  for tag in values:
456  tokens.extend([Token(Token.SEPARATOR, separator),
457  Token(Token.ARGUMENT, tag)])
458  tokens.append(Token(Token.EOL, eol))
459  return cls(tokens)
460 
461 
462 @Statement.register
464  type = Token.KEYWORD_TAGS
465 
466  @classmethod
467  def from_params(cls, values, separator=FOUR_SPACES, eol=EOL):
468  tokens = [Token(Token.KEYWORD_TAGS, 'Keyword Tags')]
469  for tag in values:
470  tokens.extend([Token(Token.SEPARATOR, separator),
471  Token(Token.ARGUMENT, tag)])
472  tokens.append(Token(Token.EOL, eol))
473  return cls(tokens)
474 
475 
476 @Statement.register
478  type = Token.SUITE_SETUP
479 
480  @classmethod
481  def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL):
482  tokens = [Token(Token.SUITE_SETUP, 'Suite Setup'),
483  Token(Token.SEPARATOR, separator),
484  Token(Token.NAME, name)]
485  for arg in args:
486  tokens.extend([Token(Token.SEPARATOR, separator),
487  Token(Token.ARGUMENT, arg)])
488  tokens.append(Token(Token.EOL, eol))
489  return cls(tokens)
490 
491 
492 @Statement.register
494  type = Token.SUITE_TEARDOWN
495 
496  @classmethod
497  def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL):
498  tokens = [Token(Token.SUITE_TEARDOWN, 'Suite Teardown'),
499  Token(Token.SEPARATOR, separator),
500  Token(Token.NAME, name)]
501  for arg in args:
502  tokens.extend([Token(Token.SEPARATOR, separator),
503  Token(Token.ARGUMENT, arg)])
504  tokens.append(Token(Token.EOL, eol))
505  return cls(tokens)
506 
507 
508 @Statement.register
510  type = Token.TEST_SETUP
511 
512  @classmethod
513  def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL):
514  tokens = [Token(Token.TEST_SETUP, 'Test Setup'),
515  Token(Token.SEPARATOR, separator),
516  Token(Token.NAME, name)]
517  for arg in args:
518  tokens.extend([Token(Token.SEPARATOR, separator),
519  Token(Token.ARGUMENT, arg)])
520  tokens.append(Token(Token.EOL, eol))
521  return cls(tokens)
522 
523 
524 @Statement.register
526  type = Token.TEST_TEARDOWN
527 
528  @classmethod
529  def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL):
530  tokens = [Token(Token.TEST_TEARDOWN, 'Test Teardown'),
531  Token(Token.SEPARATOR, separator),
532  Token(Token.NAME, name)]
533  for arg in args:
534  tokens.extend([Token(Token.SEPARATOR, separator),
535  Token(Token.ARGUMENT, arg)])
536  tokens.append(Token(Token.EOL, eol))
537  return cls(tokens)
538 
539 
540 @Statement.register
542  type = Token.TEST_TEMPLATE
543 
544  @classmethod
545  def from_params(cls, value, separator=FOUR_SPACES, eol=EOL):
546  return cls([
547  Token(Token.TEST_TEMPLATE, 'Test Template'),
548  Token(Token.SEPARATOR, separator),
549  Token(Token.NAME, value),
550  Token(Token.EOL, eol)
551  ])
552 
553 
554 @Statement.register
556  type = Token.TEST_TIMEOUT
557 
558  @classmethod
559  def from_params(cls, value, separator=FOUR_SPACES, eol=EOL):
560  return cls([
561  Token(Token.TEST_TIMEOUT, 'Test Timeout'),
562  Token(Token.SEPARATOR, separator),
563  Token(Token.ARGUMENT, value),
564  Token(Token.EOL, eol)
565  ])
566 
567 
568 @Statement.register
570  type = Token.VARIABLE
571 
572  @classmethod
573 
574  def from_params(cls, name, value, separator=FOUR_SPACES, eol=EOL):
575  values = value if is_list_like(value) else [value]
576  tokens = [Token(Token.VARIABLE, name)]
577  for value in values:
578  tokens.extend([Token(Token.SEPARATOR, separator),
579  Token(Token.ARGUMENT, value)])
580  tokens.append(Token(Token.EOL, eol))
581  return cls(tokens)
582 
583  @property
584  name = property
585 
586  def name(self):
587  name = self.get_valueget_value(Token.VARIABLE)
588  if name.endswith('='):
589  return name[:-1].rstrip()
590  return name
591 
592  @property
593  value = property
594 
595  def value(self):
596  return self.get_valuesget_values(Token.ARGUMENT)
597 
598  def validate(self, context):
599  name = self.get_valueget_value(Token.VARIABLE)
600  match = search_variable(name, ignore_errors=True)
601  if not match.is_assign(allow_assign_mark=True):
602  self.errorserrors += ("Invalid variable name '%s'." % name,)
603  if match.is_dict_assign(allow_assign_mark=True):
604  self._validate_dict_items_validate_dict_items()
605 
607  for item in self.get_valuesget_values(Token.ARGUMENT):
608  if not self._is_valid_dict_item_is_valid_dict_item(item):
609  self.errorserrors += (
610  "Invalid dictionary variable item '%s'. "
611  "Items must use 'name=value' syntax or be dictionary "
612  "variables themselves." % item,
613  )
614 
615  def _is_valid_dict_item(self, item):
616  name, value = split_from_equals(item)
617  return value is not None or is_dict_variable(item)
618 
619 
620 @Statement.register
622  type = Token.TESTCASE_NAME
623 
624  @classmethod
625  def from_params(cls, name, eol=EOL):
626  tokens = [Token(Token.TESTCASE_NAME, name)]
627  if eol:
628  tokens.append(Token(Token.EOL, eol))
629  return cls(tokens)
630 
631  @property
632  name = property
633 
634  def name(self):
635  return self.get_valueget_value(Token.TESTCASE_NAME)
636 
637 
638 @Statement.register
640  type = Token.KEYWORD_NAME
641 
642  @classmethod
643  def from_params(cls, name, eol=EOL):
644  tokens = [Token(Token.KEYWORD_NAME, name)]
645  if eol:
646  tokens.append(Token(Token.EOL, eol))
647  return cls(tokens)
648 
649  @property
650  name = property
651 
652  def name(self):
653  return self.get_valueget_value(Token.KEYWORD_NAME)
654 
655 
656 @Statement.register
657 class Setup(Fixture):
658  type = Token.SETUP
659 
660  @classmethod
661  def from_params(cls, name, args=(), indent=FOUR_SPACES, separator=FOUR_SPACES,
662  eol=EOL):
663  tokens = [Token(Token.SEPARATOR, indent),
664  Token(Token.SETUP, '[Setup]'),
665  Token(Token.SEPARATOR, separator),
666  Token(Token.NAME, name)]
667  for arg in args:
668  tokens.extend([Token(Token.SEPARATOR, separator),
669  Token(Token.ARGUMENT, arg)])
670  tokens.append(Token(Token.EOL, eol))
671  return cls(tokens)
672 
673 
674 @Statement.register
676  type = Token.TEARDOWN
677 
678  @classmethod
679  def from_params(cls, name, args=(), indent=FOUR_SPACES, separator=FOUR_SPACES,
680  eol=EOL):
681  tokens = [Token(Token.SEPARATOR, indent),
682  Token(Token.TEARDOWN, '[Teardown]'),
683  Token(Token.SEPARATOR, separator),
684  Token(Token.NAME, name)]
685  for arg in args:
686  tokens.extend([Token(Token.SEPARATOR, separator),
687  Token(Token.ARGUMENT, arg)])
688  tokens.append(Token(Token.EOL, eol))
689  return cls(tokens)
690 
691 
692 @Statement.register
694  type = Token.TAGS
695 
696  @classmethod
697  def from_params(cls, values, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL):
698  tokens = [Token(Token.SEPARATOR, indent),
699  Token(Token.TAGS, '[Tags]')]
700  for tag in values:
701  tokens.extend([Token(Token.SEPARATOR, separator),
702  Token(Token.ARGUMENT, tag)])
703  tokens.append(Token(Token.EOL, eol))
704  return cls(tokens)
705 
706 
707 @Statement.register
709  type = Token.TEMPLATE
710 
711  @classmethod
712  def from_params(cls, value, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL):
713  return cls([
714  Token(Token.SEPARATOR, indent),
715  Token(Token.TEMPLATE, '[Template]'),
716  Token(Token.SEPARATOR, separator),
717  Token(Token.NAME, value),
718  Token(Token.EOL, eol)
719  ])
720 
721 
722 @Statement.register
724  type = Token.TIMEOUT
725 
726  @classmethod
727  def from_params(cls, value, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL):
728  return cls([
729  Token(Token.SEPARATOR, indent),
730  Token(Token.TIMEOUT, '[Timeout]'),
731  Token(Token.SEPARATOR, separator),
732  Token(Token.ARGUMENT, value),
733  Token(Token.EOL, eol)
734  ])
735 
736 
737 @Statement.register
739  type = Token.ARGUMENTS
740 
741  @classmethod
742  def from_params(cls, args, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL):
743  tokens = [Token(Token.SEPARATOR, indent),
744  Token(Token.ARGUMENTS, '[Arguments]')]
745  for arg in args:
746  tokens.extend([Token(Token.SEPARATOR, separator),
747  Token(Token.ARGUMENT, arg)])
748  tokens.append(Token(Token.EOL, eol))
749  return cls(tokens)
750 
751  def validate(self, context):
752  errors = []
753  UserKeywordArgumentParser(error_reporter=errors.append).parse(self.valuesvaluesvalues)
754  self.errorserrorserrors = tuple(errors)
755 
756 
757 @Statement.register
759  type = Token.RETURN
760 
761  @classmethod
762  def from_params(cls, args, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL):
763  tokens = [Token(Token.SEPARATOR, indent),
764  Token(Token.RETURN, '[Return]')]
765  for arg in args:
766  tokens.extend([Token(Token.SEPARATOR, separator),
767  Token(Token.ARGUMENT, arg)])
768  tokens.append(Token(Token.EOL, eol))
769  return cls(tokens)
770 
771 
772 @Statement.register
774  type = Token.KEYWORD
775 
776  @classmethod
777  def from_params(cls, name, assign=(), args=(), indent=FOUR_SPACES,
778  separator=FOUR_SPACES, eol=EOL):
779  tokens = [Token(Token.SEPARATOR, indent)]
780  for assignment in assign:
781  tokens.extend([Token(Token.ASSIGN, assignment),
782  Token(Token.SEPARATOR, separator)])
783  tokens.append(Token(Token.KEYWORD, name))
784  for arg in args:
785  tokens.extend([Token(Token.SEPARATOR, separator),
786  Token(Token.ARGUMENT, arg)])
787  tokens.append(Token(Token.EOL, eol))
788  return cls(tokens)
789 
790  @property
791  keyword = property
792 
793  def keyword(self):
794  return self.get_valueget_value(Token.KEYWORD)
795 
796  @property
797  args = property
798 
799  def args(self):
800  return self.get_valuesget_values(Token.ARGUMENT)
801 
802  @property
803  assign = property
804 
805  def assign(self):
806  return self.get_valuesget_values(Token.ASSIGN)
807 
808 
809 @Statement.register
811  type = Token.ARGUMENT
812 
813  @classmethod
814  def from_params(cls, args, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL):
815  tokens = []
816  for index, arg in enumerate(args):
817  tokens.extend([Token(Token.SEPARATOR, separator if index else indent),
818  Token(Token.ARGUMENT, arg)])
819  tokens.append(Token(Token.EOL, eol))
820  return cls(tokens)
821 
822  @property
823  args = property
824 
825  def args(self):
826  return self.get_valuesget_values(self.typetypetype)
827 
828 
829 @Statement.register
831  type = Token.FOR
832 
833  @classmethod
834  def from_params(cls, variables, values, flavor='IN', indent=FOUR_SPACES,
835  separator=FOUR_SPACES, eol=EOL):
836  tokens = [Token(Token.SEPARATOR, indent),
837  Token(Token.FOR),
838  Token(Token.SEPARATOR, separator)]
839  for variable in variables:
840  tokens.extend([Token(Token.VARIABLE, variable),
841  Token(Token.SEPARATOR, separator)])
842  tokens.append(Token(Token.FOR_SEPARATOR, flavor))
843  for value in values:
844  tokens.extend([Token(Token.SEPARATOR, separator),
845  Token(Token.ARGUMENT, value)])
846  tokens.append(Token(Token.EOL, eol))
847  return cls(tokens)
848 
849  @property
850  variables = property
851 
852  def variables(self):
853  return self.get_valuesget_values(Token.VARIABLE)
854 
855  @property
856  values = property
857 
858  def values(self):
859  return self.get_valuesget_values(Token.ARGUMENT)
860 
861  @property
862  flavor = property
863 
864  def flavor(self):
865  separator = self.get_tokenget_token(Token.FOR_SEPARATOR)
866  return normalize_whitespace(separator.value) if separator else None
867 
868  def validate(self, context):
869  if not self.variablesvariablesvariables:
870  self._add_error_add_error('no loop variables')
871  if not self.flavorflavorflavor:
872  self._add_error_add_error("no 'IN' or other valid separator")
873  else:
874  for var in self.variablesvariablesvariables:
875  if not is_scalar_assign(var):
876  self._add_error_add_error("invalid loop variable '%s'" % var)
877  if not self.valuesvaluesvalues:
878  self._add_error_add_error('no loop values')
879 
880  def _add_error(self, error):
881  self.errorserrors += ('FOR loop has %s.' % error,)
882 
883 
885 
886  @property
887  condition = property
888 
889  def condition(self):
890  return None
891 
892  @property
893  assign = property
894 
895  def assign(self):
896  return None
897 
898 
899 @Statement.register
901  type = Token.IF
902 
903  @classmethod
904  def from_params(cls, condition, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL):
905  tokens = [Token(Token.SEPARATOR, indent),
906  Token(cls.typetypetype),
907  Token(Token.SEPARATOR, separator),
908  Token(Token.ARGUMENT, condition)]
909  if cls.typetypetype != Token.INLINE_IF:
910  tokens.append(Token(Token.EOL, eol))
911  return cls(tokens)
912 
913  @property
914  condition = property
915 
916  def condition(self):
917  values = self.get_valuesget_values(Token.ARGUMENT)
918  if len(values) != 1:
919  return ', '.join(values) if values else None
920  return values[0]
921 
922  def validate(self, context):
923  conditions = len(self.get_tokensget_tokens(Token.ARGUMENT))
924  if conditions == 0:
925  self.errorserrors += ('%s must have a condition.' % self.typetypetype,)
926  if conditions > 1:
927  self.errorserrors += ('%s cannot have more than one condition.' % self.typetypetype,)
928 
929 
930 @Statement.register
932  type = Token.INLINE_IF
933 
934  @property
935  assign = property
936 
937  def assign(self):
938  return self.get_valuesget_values(Token.ASSIGN)
939 
940 
941 @Statement.register
943  type = Token.ELSE_IF
944 
945 
946 @Statement.register
948  type = Token.ELSE
949 
950  @classmethod
951  def from_params(cls, indent=FOUR_SPACES, eol=EOL):
952  return cls([
953  Token(Token.SEPARATOR, indent),
954  Token(Token.ELSE),
955  Token(Token.EOL, eol)
956  ])
957 
958  def validate(self, context):
959  if self.get_tokensget_tokens(Token.ARGUMENT):
960  values = self.get_valuesget_values(Token.ARGUMENT)
961  self.errorserrors += (f'ELSE does not accept arguments, got {seq2str(values)}.',)
962 
963 
965 
966  @classmethod
967  def from_params(cls, indent=FOUR_SPACES, eol=EOL):
968  return cls([
969  Token(Token.SEPARATOR, indent),
970  Token(cls.typetype),
971  Token(Token.EOL, eol)
972  ])
973 
974  def validate(self, context):
975  if self.get_tokensget_tokens(Token.ARGUMENT):
976  self.errorserrors += (f'{self.type} does not accept arguments, got '
977  f'{seq2str(self.values)}.',)
978 
979  @property
980  values = property
981 
982  def values(self):
983  return self.get_valuesget_values(Token.ARGUMENT)
984 
985 
986 @Statement.register
988  type = Token.TRY
989 
990 
991 @Statement.register
993  type = Token.EXCEPT
994 
995  @classmethod
996  def from_params(cls, patterns=(), type=None, variable=None, indent=FOUR_SPACES,
997  separator=FOUR_SPACES, eol=EOL):
998  tokens = [Token(Token.SEPARATOR, indent),
999  Token(Token.EXCEPT)]
1000  for pattern in patterns:
1001  tokens.extend([Token(Token.SEPARATOR, separator),
1002  Token(Token.ARGUMENT, pattern)]),
1003  if type:
1004  tokens.extend([Token(Token.SEPARATOR, separator),
1005  Token(Token.OPTION, f'type={type}')])
1006  if variable:
1007  tokens.extend([Token(Token.SEPARATOR, separator),
1008  Token(Token.AS),
1009  Token(Token.SEPARATOR, separator),
1010  Token(Token.VARIABLE, variable)])
1011  tokens.append(Token(Token.EOL, eol))
1012  return cls(tokens)
1013 
1014  @property
1015  patterns = property
1016 
1017  def patterns(self):
1018  return self.get_valuesget_values(Token.ARGUMENT)
1019 
1020  @property
1021  pattern_type = property
1022 
1023  def pattern_type(self):
1024  value = self.get_valueget_value(Token.OPTION)
1025  return value[len('type='):] if value else None
1026 
1027  @property
1028  variable = property
1029 
1030  def variable(self):
1031  return self.get_valueget_value(Token.VARIABLE)
1032 
1033  def validate(self, context):
1034  as_token = self.get_tokenget_token(Token.AS)
1035  if as_token:
1036  variables = self.get_tokensget_tokens(Token.VARIABLE)
1037  if not variables:
1038  self.errorserrors += ("EXCEPT's AS requires variable.",)
1039  elif len(variables) > 1:
1040  self.errorserrors += ("EXCEPT's AS accepts only one variable.",)
1041  elif not is_scalar_assign(variables[0].value):
1042  self.errorserrors += (f"EXCEPT's AS variable '{variables[0].value}' is invalid.",)
1043 
1044 
1045 @Statement.register
1047  type = Token.FINALLY
1048 
1049 
1050 @Statement.register
1052  type = Token.END
1053 
1054 
1055 @Statement.register
1057  type = Token.WHILE
1058 
1059  @classmethod
1060  def from_params(cls, condition, limit=None, indent=FOUR_SPACES,
1061  separator=FOUR_SPACES, eol=EOL):
1062  tokens = [Token(Token.SEPARATOR, indent),
1063  Token(cls.typetypetype),
1064  Token(Token.SEPARATOR, separator),
1065  Token(Token.ARGUMENT, condition)]
1066  if limit:
1067  tokens.extend([Token(Token.SEPARATOR, indent),
1068  Token(Token.OPTION, f'limit={limit}')])
1069  tokens.append(Token(Token.EOL, eol))
1070  return cls(tokens)
1071 
1072  @property
1073  condition = property
1074 
1075  def condition(self):
1076  return ', '.join(self.get_valuesget_values(Token.ARGUMENT))
1077 
1078  @property
1079  limit = property
1080 
1081  def limit(self):
1082  value = self.get_valueget_value(Token.OPTION)
1083  return value[len('limit='):] if value else None
1084 
1085  def validate(self, context):
1086  values = self.get_valuesget_values(Token.ARGUMENT)
1087  if len(values) == 0:
1088  self.errorserrors += ('WHILE must have a condition.',)
1089  if len(values) == 2:
1090  self.errorserrors += (f"Second WHILE loop argument must be 'limit', "
1091  f"got '{values[1]}'.",)
1092  if len(values) > 2:
1093  self.errorserrors += ('WHILE cannot have more than one condition.',)
1094 
1095 
1096 @Statement.register
1098  type = Token.RETURN_STATEMENT
1099 
1100  @property
1101  values = property
1102 
1103  def values(self):
1104  return self.get_valuesget_values(Token.ARGUMENT)
1105 
1106  @classmethod
1107  def from_params(cls, values=(), indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL):
1108  tokens = [Token(Token.SEPARATOR, indent),
1109  Token(Token.RETURN_STATEMENT)]
1110  for value in values:
1111  tokens.extend([Token(Token.SEPARATOR, separator),
1112  Token(Token.ARGUMENT, value)])
1113  tokens.append(Token(Token.EOL, eol))
1114  return cls(tokens)
1115 
1116  def validate(self, context):
1117  if not context.in_keyword:
1118  self.errorserrors += ('RETURN can only be used inside a user keyword.', )
1119  if context.in_keyword and context.in_finally:
1120  self.errorserrors += ('RETURN cannot be used in FINALLY branch.', )
1121 
1122 
1124 
1125  def validate(self, context):
1126  super(LoopControl, self).validate(context)
1127  if not (context.in_for or context.in_while):
1128  self.errorserrors += (f'{self.type} can only be used inside a loop.', )
1129  if context.in_finally:
1130  self.errorserrors += (f'{self.type} cannot be used in FINALLY branch.', )
1131 
1132 
1133 @Statement.register
1135  type = Token.CONTINUE
1136 
1137 
1138 @Statement.register
1140  type = Token.BREAK
1141 
1142 
1143 @Statement.register
1145  type = Token.COMMENT
1146 
1147  @classmethod
1148  def from_params(cls, comment, indent=FOUR_SPACES, eol=EOL):
1149  return cls([
1150  Token(Token.SEPARATOR, indent),
1151  Token(Token.COMMENT, comment),
1152  Token(Token.EOL, eol)
1153  ])
1154 
1155 
1156 @Statement.register
1158  type = Token.CONFIG
1159 
1160  @classmethod
1161  def from_params(cls, config, eol=EOL):
1162  return cls([
1163  Token(Token.CONFIG, config),
1164  Token(Token.EOL, eol)
1165  ])
1166 
1167  @property
1168  language = property
1169 
1170  def language(self):
1171  value = self.get_valueget_value(Token.CONFIG)
1172  return Language.from_name(value[len('language:'):]) if value else None
1173 
1174 
1175 @Statement.register
1177  type = Token.ERROR
1178  handles_types = (Token.ERROR, Token.FATAL_ERROR)
1179 
1182  _errors = ()
1183 
1184  @property
1185 
1190  errors = property
1191 
1192  def errors(self):
1193  tokens = self.get_tokensget_tokens(Token.ERROR, Token.FATAL_ERROR)
1194  return tuple(t.error for t in tokens) + self._errors_errors_errors
1195 
1196  @errors.setter
1197 
1198  def errors(self, errors):
1199  self._errors_errors_errors = tuple(errors)
1200 
1201 
1203  type = Token.EOL
1204 
1205  @classmethod
1206  def from_params(cls, eol=EOL):
1207  return cls([Token(Token.EOL, eol)])
Token representing piece of Robot Framework data.
Definition: tokens.py:39
def from_params(cls, args, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:742
def from_params(cls, comment, indent=FOUR_SPACES, eol=EOL)
Definition: statements.py:1148
def from_params(cls, config, eol=EOL)
Definition: statements.py:1161
def from_params(cls, values, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:453
def from_params(cls, value, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL, settings_section=True)
Definition: statements.py:368
def from_params(cls, indent=FOUR_SPACES, eol=EOL)
Definition: statements.py:951
errors
Errors got from the underlying ERROR and FATAL_ERROR tokens.
Definition: statements.py:1190
def from_params(cls, patterns=(), type=None, variable=None, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:997
def from_params(cls, variables, values, flavor='IN', indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:835
def from_params(cls, values, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:439
def from_params(cls, condition, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:904
def from_params(cls, name, assign=(), args=(), indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:778
def from_params(cls, values, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:467
def from_params(cls, name, args=(), alias=None, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:279
def from_params(cls, name, value, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:404
def from_params(cls, indent=FOUR_SPACES, eol=EOL)
Definition: statements.py:967
def from_params(cls, name, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:319
def from_params(cls, values=(), indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:1107
def from_params(cls, args, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:762
def from_params(cls, type, name=None, eol=EOL)
Definition: statements.py:247
def from_params(cls, name, args=(), indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:662
def get_tokens(self, *types)
Return tokens having any of the given types.
Definition: statements.py:128
def __init__(self, tokens, errors=())
Definition: statements.py:47
def get_token(self, *types)
Return a token with the given type.
Definition: statements.py:121
def from_params(cls, *args, **kwargs)
Create statement from passed parameters.
Definition: statements.py:107
def get_values(self, *types)
Return values of tokens having any of the given types.
Definition: statements.py:141
def get_value(self, type, default=None)
Return value of a token with the given type.
Definition: statements.py:136
def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:481
def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:497
def from_params(cls, values, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:697
def from_params(cls, name, args=(), indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:680
def from_params(cls, args, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:814
def from_params(cls, value, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:712
def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:513
def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:529
def from_params(cls, value, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:545
def from_params(cls, value, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:559
def from_params(cls, value, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:727
def from_params(cls, name, value, separator=FOUR_SPACES, eol=EOL)
value can be given either as a string or as a list of strings.
Definition: statements.py:574
def from_params(cls, name, args=(), separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:339
def from_params(cls, condition, limit=None, indent=FOUR_SPACES, separator=FOUR_SPACES, eol=EOL)
Definition: statements.py:1061
def split_from_equals(string)
Definition: escaping.py:105
def normalize_whitespace(string)
Definition: normalizing.py:45
def is_list_like(item)
Definition: robottypes.py:66
def search_variable(string, identifiers='$@&% *', ignore_errors=False)
Definition: search.py:22
def is_dict_variable(string)
Definition: search.py:46
def is_scalar_assign(string, allow_assign_mark=False)
Definition: search.py:55