Robot Framework
settings.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 glob
17 import os
18 import random
19 import string
20 import sys
21 import time
22 import warnings
23 from pathlib import Path
24 
25 from robot.errors import DataError, FrameworkError
26 from robot.output import LOGGER, loggerhelper
27 from robot.result.keywordremover import KeywordRemover
28 from robot.result.flattenkeywordmatcher import validate_flatten_keyword
29 from robot.utils import (abspath, create_destination_directory, escape, format_time,
30  get_link_path, html_escape, is_list_like, plural_or_not as s,
31  seq2str, split_args_from_name_or_path)
32 
33 from .gatherfailed import gather_failed_tests, gather_failed_suites
34 from .languages import Languages
35 
36 
38 
41  _cli_opts = {'RPA' : ('rpa', None),
42  'Name' : ('name', None),
43  'Doc' : ('doc', None),
44  'Metadata' : ('metadata', []),
45  'TestNames' : ('test', []),
46  'TaskNames' : ('task', []),
47  'SuiteNames' : ('suite', []),
48  'SetTag' : ('settag', []),
49  'Include' : ('include', []),
50  'Exclude' : ('exclude', []),
51  'OutputDir' : ('outputdir', abspath('.')),
52  'Log' : ('log', 'log.html'),
53  'Report' : ('report', 'report.html'),
54  'XUnit' : ('xunit', None),
55  'SplitLog' : ('splitlog', False),
56  'TimestampOutputs' : ('timestampoutputs', False),
57  'LogTitle' : ('logtitle', None),
58  'ReportTitle' : ('reporttitle', None),
59  'ReportBackground' : ('reportbackground',
60  ('#9e9', '#f66', '#fed84f')),
61  'SuiteStatLevel' : ('suitestatlevel', -1),
62  'TagStatInclude' : ('tagstatinclude', []),
63  'TagStatExclude' : ('tagstatexclude', []),
64  'TagStatCombine' : ('tagstatcombine', []),
65  'TagDoc' : ('tagdoc', []),
66  'TagStatLink' : ('tagstatlink', []),
67  'RemoveKeywords' : ('removekeywords', []),
68  'ExpandKeywords' : ('expandkeywords', []),
69  'FlattenKeywords' : ('flattenkeywords', []),
70  'PreRebotModifiers': ('prerebotmodifier', []),
71  'StatusRC' : ('statusrc', True),
72  'ConsoleColors' : ('consolecolors', 'AUTO'),
73  'PythonPath' : ('pythonpath', []),
74  'StdOut' : ('stdout', None),
75  'StdErr' : ('stderr', None)}
76 
79  _output_opts = ['Output', 'Log', 'Report', 'XUnit', 'DebugFile']
80 
81  def __init__(self, options=None, **extra_options):
82  self.start_timestampstart_timestamp = format_time(time.time(), '', '-', '')
83  self._opts_opts = {}
84  self._cli_opts_cli_opts_cli_opts = self._cli_opts_cli_opts_cli_opts.copy()
85  self._cli_opts_cli_opts_cli_opts.update(self._extra_cli_opts)
86  self._process_cli_opts_process_cli_opts(dict(options or {}, **extra_options))
87 
88  def _process_cli_opts(self, opts):
89  for name, (cli_name, default) in self._cli_opts_cli_opts_cli_opts.items():
90  value = opts.pop(cli_name) if cli_name in opts else default
91  if isinstance(default, list):
92  # Copy mutable values and support list values as scalars.
93  value = list(value) if is_list_like(value) else [value]
94  self[name] = self._process_value_process_value(name, value)
95  if opts:
96  raise DataError(f'Invalid option{s(opts)} {seq2str(opts)}.')
97 
98  def __setitem__(self, name, value):
99  if name not in self._cli_opts_cli_opts_cli_opts:
100  raise KeyError(f"Non-existing option '{name}'.")
101  self._opts_opts[name] = value
102 
103  def _process_value(self, name, value):
104  if name == 'LogLevel':
105  return self._process_log_level_process_log_level(value)
106  if value == self._get_default_value_get_default_value(name):
107  return value
108  if name == 'Doc':
109  return self._process_doc_process_doc(value)
110  if name == 'Metadata':
111  return [self._process_metadata_process_metadata(v) for v in value]
112  if name == 'TagDoc':
113  return [self._process_tagdoc_process_tagdoc(v) for v in value]
114  if name in ['Include', 'Exclude']:
115  return [self._format_tag_patterns_format_tag_patterns(v) for v in value]
116  if name in self._output_opts_output_opts or name in ['ReRunFailed', 'ReRunFailedSuites']:
117  if isinstance(value, Path):
118  return str(value)
119  return value if value and value.upper() != 'NONE' else None
120  if name == 'OutputDir':
121  return abspath(value)
122  if name in ['SuiteStatLevel', 'ConsoleWidth']:
123  return self._convert_to_positive_integer_or_default_convert_to_positive_integer_or_default(name, value)
124  if name == 'VariableFiles':
125  return [split_args_from_name_or_path(item) for item in value]
126  if name == 'ReportBackground':
127  return self._process_report_background_process_report_background(value)
128  if name == 'TagStatCombine':
129  return [self._process_tag_stat_combine_process_tag_stat_combine(v) for v in value]
130  if name == 'TagStatLink':
131  return [v for v in [self._process_tag_stat_link_process_tag_stat_link(v) for v in value] if v]
132  if name == 'Randomize':
133  return self._process_randomize_value_process_randomize_value(value)
134  if name == 'MaxErrorLines':
135  return self._process_max_error_lines_process_max_error_lines(value)
136  if name == 'MaxAssignLength':
137  return self._process_max_assign_length_process_max_assign_length(value)
138  if name == 'PythonPath':
139  return self._process_pythonpath_process_pythonpath(value)
140  if name == 'RemoveKeywords':
141  self._validate_remove_keywords_validate_remove_keywords(value)
142  if name == 'FlattenKeywords':
143  self._validate_flatten_keywords_validate_flatten_keywords(value)
144  if name == 'ExpandKeywords':
145  self._validate_expandkeywords_validate_expandkeywords(value)
146  if name == 'Extension':
147  return tuple(ext.lower().lstrip('.') for ext in value.split(':'))
148  return value
149 
150  def _process_doc(self, value):
151  if isinstance(value, Path) or (os.path.isfile(value) and value.strip() == value):
152  try:
153  with open(value) as f:
154  value = f.read()
155  except (OSError, IOError) as err:
156  self._raise_invalid_raise_invalid('Doc', f"Reading documentation from '{value}' "
157  f"failed: {err}")
158  return self._escape_doc_escape_doc(value).strip()
159 
160  def _escape_doc(self, value):
161  return value
162 
163  def _process_log_level(self, level):
164  level, visible_level = self._split_log_level_split_log_level(level.upper())
165  self._opts_opts['VisibleLogLevel'] = visible_level
166  return level
167 
168  def _split_log_level(self, level):
169  if ':' in level:
170  log_level, visible_level = level.split(':', 1)
171  else:
172  log_level = visible_level = level
173  for level in log_level, visible_level:
174  if level not in loggerhelper.LEVELS:
175  self._raise_invalid_raise_invalid('LogLevel', f"Invalid level '{level}'.")
176  if not loggerhelper.IsLogged(log_level)(visible_level):
177  self._raise_invalid_raise_invalid('LogLevel', f"Level in log '{visible_level}' is lower "
178  f"than execution level '{log_level}'.")
179  return log_level, visible_level
180 
181  def _process_max_error_lines(self, value):
182  if not value or value.upper() == 'NONE':
183  return None
184  value = self._convert_to_integer_convert_to_integer('MaxErrorLines', value)
185  if value < 10:
186  self._raise_invalid_raise_invalid('MaxErrorLines',
187  f"Expected integer greater than 10, got {value}.")
188  return value
189 
190  def _process_max_assign_length(self, value):
191  value = self._convert_to_integer_convert_to_integer('MaxAssignLength', value)
192  return max(value, 0)
193 
194  def _process_randomize_value(self, original):
195  value = original.upper()
196  if ':' in value:
197  value, seed = value.split(':', 1)
198  else:
199  seed = random.randint(0, sys.maxsize)
200  if value in ('TEST', 'SUITE'):
201  value += 'S'
202  valid = ('TESTS', 'SUITES', 'ALL', 'NONE')
203  if value not in valid:
204  valid = seq2str(valid, lastsep=' or ')
205  self._raise_invalid_raise_invalid('Randomize', f"Expected {valid}, got '{value}'.")
206  try:
207  seed = int(seed)
208  except ValueError:
209  self._raise_invalid_raise_invalid('Randomize', f"Seed should be integer, got '{seed}'.")
210  return value, seed
211 
212  def __getitem__(self, name):
213  if name not in self._opts_opts:
214  raise KeyError(f"Non-existing option '{name}'.")
215  if name in self._output_opts_output_opts:
216  return self._get_output_file_get_output_file(name)
217  return self._opts_opts[name]
218 
219 
225  def _get_output_file(self, option):
226  name = self._opts_opts[option]
227  if not name:
228  return None
229  if option == 'Log' and self._output_disabled():
230  self['Log'] = None
231  LOGGER.error('Log file cannot be created if output.xml is disabled.')
232  return None
233  name = self._process_output_name_process_output_name(option, name)
234  path = abspath(os.path.join(self['OutputDir'], name))
235  create_destination_directory(path, f'{option.lower()} file')
236  return path
237 
238  def _process_output_name(self, option, name):
239  base, ext = os.path.splitext(name)
240  if self['TimestampOutputs']:
241  base = f'{base}-{self.start_timestamp}'
242  ext = self._get_output_extension_get_output_extension(ext, option)
243  return base + ext
244 
245  def _get_output_extension(self, extension, file_type):
246  if extension:
247  return extension
248  if file_type in ['Output', 'XUnit']:
249  return '.xml'
250  if file_type in ['Log', 'Report']:
251  return '.html'
252  if file_type == 'DebugFile':
253  return '.txt'
254  raise FrameworkError(f"Invalid output file type '{file_type}'.")
255 
256  def _process_metadata(self, value):
257  name, value = self._split_from_colon_split_from_colon(value)
258  return name, self._process_doc_process_doc(value)
259 
260  def _split_from_colon(self, value):
261  if ':' in value:
262  return value.split(':', 1)
263  return value, ''
264 
265  def _process_tagdoc(self, value):
266  return self._split_from_colon_split_from_colon(value)
267 
268  def _process_report_background(self, colors):
269  if colors.count(':') not in [1, 2]:
270  self._raise_invalid_raise_invalid('ReportBackground', f"Expected format 'pass:fail:skip' "
271  f"or 'pass:fail', got '{colors}'.")
272  colors = colors.split(':')
273  if len(colors) == 2:
274  return colors[0], colors[1], '#fed84f'
275  return tuple(colors)
276 
277  def _process_tag_stat_combine(self, pattern):
278  if ':' in pattern:
279  pattern, title = pattern.rsplit(':', 1)
280  else:
281  title = ''
282  return self._format_tag_patterns_format_tag_patterns(pattern), title
283 
284  def _format_tag_patterns(self, pattern):
285  for search, replace in [('&', 'AND'), ('AND', ' AND '), ('OR', ' OR '),
286  ('NOT', ' NOT '), ('_', ' ')]:
287  if search in pattern:
288  pattern = pattern.replace(search, replace)
289  while ' ' in pattern:
290  pattern = pattern.replace(' ', ' ')
291  if pattern.startswith(' NOT'):
292  pattern = pattern[1:]
293  return pattern
294 
295  def _process_tag_stat_link(self, value):
296  tokens = value.split(':')
297  if len(tokens) >= 3:
298  return tokens[0], ':'.join(tokens[1:-1]), tokens[-1]
299  self._raise_invalid_raise_invalid('TagStatLink',
300  f"Expected format 'tag:link:title', got '{value}'.")
301 
303  value = self._convert_to_integer_convert_to_integer(name, value)
304  return value if value > 0 else self._get_default_value_get_default_value(name)
305 
306  def _convert_to_integer(self, name, value):
307  try:
308  return int(value)
309  except ValueError:
310  self._raise_invalid_raise_invalid(name, f"Expected integer, got '{value}'.")
311 
312  def _get_default_value(self, name):
313  return self._cli_opts_cli_opts_cli_opts[name][1]
314 
315  def _process_pythonpath(self, paths):
316  return [os.path.abspath(globbed)
317  for path in paths
318  for split in self._split_pythonpath_split_pythonpath(path)
319  for globbed in glob.glob(split) or [split]]
320 
321  def _split_pythonpath(self, path):
322  path = path.replace('/', os.sep)
323  if ';' in path:
324  yield from path.split(';')
325  elif os.sep == '/':
326  yield from path.split(':')
327  else:
328  drive = ''
329  for item in path.split(':'):
330  if drive:
331  if item.startswith('\\'):
332  yield f'{drive}:{item}'
333  drive = ''
334  continue
335  yield drive
336  drive = ''
337  if len(item) == 1 and item in string.ascii_letters:
338  drive = item
339  else:
340  yield item
341  if drive:
342  yield drive
343 
344  def _validate_remove_keywords(self, values):
345  for value in values:
346  try:
347  KeywordRemover(value)
348  except DataError as err:
349  self._raise_invalid_raise_invalid('RemoveKeywords', err)
350 
351  def _validate_flatten_keywords(self, values):
352  try:
354  except DataError as err:
355  self._raise_invalid_raise_invalid('FlattenKeywords', err)
356 
357  def _validate_expandkeywords(self, values):
358  for opt in values:
359  if not opt.lower().startswith(('name:', 'tag:')):
360  self._raise_invalid_raise_invalid('ExpandKeywords', f"Expected 'TAG:<pattern>' or "
361  f"'NAME:<pattern>', got '{opt}'.")
362 
363  def _raise_invalid(self, option, error):
364  raise DataError(f"Invalid value for option '--{option.lower()}': {error}")
365 
366  def __contains__(self, setting):
367  return setting in self._opts_opts
368 
369  def __str__(self):
370  return '\n'.join(f'{name}: {self._opts[name]}' for name in sorted(self._opts_opts))
371 
372  @property
373  output_directory = property
374 
375  def output_directory(self):
376  return self['OutputDir']
377 
378  @property
379  output = property
380 
381  def output(self):
382  return self['Output']
383 
384  @property
385  log = property
386 
387  def log(self):
388  return self['Log']
389 
390  @property
391  report = property
392 
393  def report(self):
394  return self['Report']
395 
396  @property
397  xunit = property
398 
399  def xunit(self):
400  return self['XUnit']
401 
402  @property
403  log_level = property
404 
405  def log_level(self):
406  return self['LogLevel']
407 
408  @property
409  split_log = property
410 
411  def split_log(self):
412  return self['SplitLog']
413 
414  @property
415  suite_names = property
416 
417  def suite_names(self):
418  return self._filter_empty_filter_empty(self['SuiteNames'])
419 
420  def _filter_empty(self, items):
421  return [i for i in items if i] or None
422 
423  @property
424  test_names = property
425 
426  def test_names(self):
427  return self._filter_empty_filter_empty(self['TestNames'] + self['TaskNames'])
428 
429  @property
430  include = property
431 
432  def include(self):
433  return self._filter_empty_filter_empty(self['Include'])
434 
435  @property
436  exclude = property
437 
438  def exclude(self):
439  return self._filter_empty_filter_empty(self['Exclude'])
440 
441  @property
442  pythonpath = property
443 
444  def pythonpath(self):
445  return self['PythonPath']
446 
447  @property
448  status_rc = property
449 
450  def status_rc(self):
451  return self['StatusRC']
452 
453  @property
454  statistics_config = property
455 
456  def statistics_config(self):
457  return {
458  'suite_stat_level': self['SuiteStatLevel'],
459  'tag_stat_include': self['TagStatInclude'],
460  'tag_stat_exclude': self['TagStatExclude'],
461  'tag_stat_combine': self['TagStatCombine'],
462  'tag_stat_link': self['TagStatLink'],
463  'tag_doc': self['TagDoc'],
464  }
465 
466  @property
467  remove_keywords = property
468 
469  def remove_keywords(self):
470  return self['RemoveKeywords']
471 
472  @property
473  flatten_keywords = property
474 
475  def flatten_keywords(self):
476  return self['FlattenKeywords']
477 
478  @property
479  pre_rebot_modifiers = property
480 
482  return self['PreRebotModifiers']
483 
484  @property
485  console_colors = property
486 
487  def console_colors(self):
488  return self['ConsoleColors']
489 
490  @property
491  rpa = property
492 
493  def rpa(self):
494  return self['RPA']
495 
496  @rpa.setter
497 
498  def rpa(self, value):
499  self['RPA'] = value
500 
501 
503 
506  _extra_cli_opts = {'Extension' : ('extension', ('robot',)),
507  'Output' : ('output', 'output.xml'),
508  'LogLevel' : ('loglevel', 'INFO'),
509  'MaxErrorLines' : ('maxerrorlines', 40),
510  'MaxAssignLength' : ('maxassignlength', 200),
511  'DryRun' : ('dryrun', False),
512  'ExitOnFailure' : ('exitonfailure', False),
513  'ExitOnError' : ('exitonerror', False),
514  'Skip' : ('skip', []),
515  'SkipOnFailure' : ('skiponfailure', []),
516  'SkipTeardownOnExit' : ('skipteardownonexit', False),
517  'ReRunFailed' : ('rerunfailed', None),
518  'ReRunFailedSuites' : ('rerunfailedsuites', None),
519  'Randomize' : ('randomize', 'NONE'),
520  'RunEmptySuite' : ('runemptysuite', False),
521  'Variables' : ('variable', []),
522  'VariableFiles' : ('variablefile', []),
523  'PreRunModifiers' : ('prerunmodifier', []),
524  'Listeners' : ('listener', []),
525  'ConsoleType' : ('console', 'verbose'),
526  'ConsoleTypeDotted' : ('dotted', False),
527  'ConsoleTypeQuiet' : ('quiet', False),
528  'ConsoleWidth' : ('consolewidth', 78),
529  'ConsoleMarkers' : ('consolemarkers', 'AUTO'),
530  'DebugFile' : ('debugfile', None),
531  'Language' : ('language', [])}
532 
535  _languages = None
536 
538  settings = RebotSettings()
539  settings.start_timestamp = self.start_timestampstart_timestamp
540  not_copied = {'Include', 'Exclude', 'TestNames', 'SuiteNames', 'Name', 'Doc',
541  'Metadata', 'SetTag', 'Output', 'LogLevel', 'TimestampOutputs'}
542  for opt in settings._opts:
543  if opt in self and opt not in not_copied:
544  settings._opts[opt] = self[opt]
545  settings._opts['ProcessEmptySuite'] = self['RunEmptySuite']
546  return settings
547 
548  def _output_disabled(self):
549  return self.outputoutputoutput is None
550 
551  def _escape_doc(self, value):
552  return escape(value)
553 
554  @property
555  listeners = property
556 
557  def listeners(self):
558  return self['Listeners']
559 
560  @property
561  debug_file = property
562 
563  def debug_file(self):
564  return self['DebugFile']
565 
566  @property
567  languages = property
568 
569  def languages(self):
570  if self._languages_languages is None:
571  try:
572  self._languages_languages = Languages(self['Language'])
573  except DataError as err:
574  self._raise_invalid_raise_invalid('Language', err)
575  return self._languages_languages
576 
577  @property
578  suite_config = property
579 
580  def suite_config(self):
581  return {
582  'name': self['Name'],
583  'doc': self['Doc'],
584  'metadata': dict(self['Metadata']),
585  'set_tags': self['SetTag'],
586  'include_tags': self.includeincludeinclude,
587  'exclude_tags': self.excludeexcludeexclude,
588  'include_suites': self.suite_namessuite_namessuite_namessuite_namessuite_names,
589  'include_tests': self.test_namestest_namestest_namestest_namestest_names,
590  'empty_suite_ok': self.run_empty_suiterun_empty_suiterun_empty_suite,
591  'randomize_suites': self.randomize_suitesrandomize_suitesrandomize_suites,
592  'randomize_tests': self.randomize_testsrandomize_testsrandomize_tests,
593  'randomize_seed': self.randomize_seedrandomize_seedrandomize_seed,
594  }
595 
596  @property
597  suite_names = property
598 
599  def suite_names(self):
600  return self._names_and_rerun_names_and_rerun()
601 
602  @property
603  test_names = property
604 
605  def test_names(self):
606  return self._names_and_rerun_names_and_rerun(for_test=True)
607 
608  def _names_and_rerun(self, for_test=False):
609  if for_test:
610  names = self['TestNames'] + self['TaskNames']
611  rerun = gather_failed_tests(self['ReRunFailed'], self['RunEmptySuite'])
612  else:
613  names = self['SuiteNames']
614  rerun = gather_failed_suites(self['ReRunFailedSuites'], self['RunEmptySuite'])
615  # `rerun` is None if `--rerunfailed(suites)` wasn't used and a list otherwise.
616  # The list is empty all tests passed and running empty suite is allowed.
617  if rerun:
618  return names + rerun
619  return names or rerun
620 
621  @property
622  randomize_seed = property
623 
624  def randomize_seed(self):
625  return self['Randomize'][1]
626 
627  @property
628  randomize_suites = property
629 
630  def randomize_suites(self):
631  return self['Randomize'][0] in ('SUITES', 'ALL')
632 
633  @property
634  randomize_tests = property
635 
636  def randomize_tests(self):
637  return self['Randomize'][0] in ('TESTS', 'ALL')
638 
639  @property
640  dry_run = property
641 
642  def dry_run(self):
643  return self['DryRun']
644 
645  @property
646  exit_on_failure = property
647 
648  def exit_on_failure(self):
649  return self['ExitOnFailure']
650 
651  @property
652  exit_on_error = property
653 
654  def exit_on_error(self):
655  return self['ExitOnError']
656 
657  @property
658  skip = property
659 
660  def skip(self):
661  return self['Skip']
662 
663  @property
664  skipped_tags = property
665 
666  def skipped_tags(self):
667  warnings.warn("'RobotSettings.skipped_tags' is deprecated. Use 'skip' instead.")
668  return self.skipskipskip
669 
670  @property
671  skip_on_failure = property
672 
673  def skip_on_failure(self):
674  return self['SkipOnFailure']
675 
676  @property
677  skip_teardown_on_exit = property
678 
680  return self['SkipTeardownOnExit']
681 
682  @property
683  console_output_config = property
684 
686  return {
687  'type': self.console_typeconsole_typeconsole_type,
688  'width': self.console_widthconsole_widthconsole_width,
689  'colors': self.console_colorsconsole_colorsconsole_colors,
690  'markers': self.console_markersconsole_markersconsole_markers,
691  'stdout': self['StdOut'],
692  'stderr': self['StdErr']
693  }
694 
695  @property
696  console_type = property
697 
698  def console_type(self):
699  if self['ConsoleTypeQuiet']:
700  return 'quiet'
701  if self['ConsoleTypeDotted']:
702  return 'dotted'
703  return self['ConsoleType']
704 
705  @property
706  console_width = property
707 
708  def console_width(self):
709  return self['ConsoleWidth']
710 
711  @property
712  console_markers = property
713 
714  def console_markers(self):
715  return self['ConsoleMarkers']
716 
717  @property
718  max_error_lines = property
719 
720  def max_error_lines(self):
721  return self['MaxErrorLines']
722 
723  @property
724  max_assign_length = property
725 
726  def max_assign_length(self):
727  return self['MaxAssignLength']
728 
729  @property
730  pre_run_modifiers = property
731 
732  def pre_run_modifiers(self):
733  return self['PreRunModifiers']
734 
735  @property
736  run_empty_suite = property
737 
738  def run_empty_suite(self):
739  return self['RunEmptySuite']
740 
741  @property
742  variables = property
743 
744  def variables(self):
745  return self['Variables']
746 
747  @property
748  variable_files = property
749 
750  def variable_files(self):
751  return self['VariableFiles']
752 
753  @property
754  extension = property
755 
756  def extension(self):
757  return self['Extension']
758 
759 
761 
764  _extra_cli_opts = {'Output' : ('output', None),
765  'LogLevel' : ('loglevel', 'TRACE'),
766  'ProcessEmptySuite' : ('processemptysuite', False),
767  'StartTime' : ('starttime', None),
768  'EndTime' : ('endtime', None),
769  'Merge' : ('merge', False)}
770 
771  def _output_disabled(self):
772  return False
773 
774  @property
775  suite_config = property
776 
777  def suite_config(self):
778  return {
779  'name': self['Name'],
780  'doc': self['Doc'],
781  'metadata': dict(self['Metadata']),
782  'set_tags': self['SetTag'],
783  'include_tags': self.includeincludeinclude,
784  'exclude_tags': self.excludeexcludeexclude,
785  'include_suites': self.suite_namessuite_namessuite_names,
786  'include_tests': self.test_namestest_namestest_names,
787  'empty_suite_ok': self.process_empty_suiteprocess_empty_suiteprocess_empty_suite,
788  'remove_keywords': self.remove_keywordsremove_keywordsremove_keywords,
789  'log_level': self['LogLevel'],
790  'start_time': self['StartTime'],
791  'end_time': self['EndTime']
792  }
793 
794  @property
795  log_config = property
796 
797  def log_config(self):
798  if not self.logloglog:
799  return {}
800  return {
801  'rpa': self.rparparparpa,
802  'title': html_escape(self['LogTitle'] or ''),
803  'reportURL': self._url_from_path_url_from_path(self.logloglog, self.reportreportreport),
804  'splitLogBase': os.path.basename(os.path.splitext(self.logloglog)[0]),
805  'defaultLevel': self['VisibleLogLevel']
806  }
807 
808  @property
809  report_config = property
810 
811  def report_config(self):
812  if not self.reportreportreport:
813  return {}
814  return {
815  'rpa': self.rparparparpa,
816  'title': html_escape(self['ReportTitle'] or ''),
817  'logURL': self._url_from_path_url_from_path(self.reportreportreport, self.logloglog),
818  'background' : self._resolve_background_colors_resolve_background_colors()
819  }
820 
821  def _url_from_path(self, source, destination):
822  if not destination:
823  return None
824  return get_link_path(destination, os.path.dirname(source))
825 
827  colors = self['ReportBackground']
828  return {'pass': colors[0], 'fail': colors[1], 'skip': colors[2]}
829 
830  @property
831  merge = property
832 
833  def merge(self):
834  return self['Merge']
835 
836  @property
837  console_output_config = property
838 
840  return {
841  'colors': self.console_colorsconsole_colorsconsole_colors,
842  'stdout': self['StdOut'],
843  'stderr': self['StdErr']
844  }
845 
846  @property
847  process_empty_suite = property
848 
850  return self['ProcessEmptySuite']
851 
852  @property
853  expand_keywords = property
854 
855  def expand_keywords(self):
856  return self['ExpandKeywords']
Keeps a list of languages and unifies the translations in the properties.
Definition: languages.py:34
def _url_from_path(self, source, destination)
Definition: settings.py:821
def _names_and_rerun(self, for_test=False)
Definition: settings.py:608
def _escape_doc(self, value)
Definition: settings.py:551
def _raise_invalid(self, option, error)
Definition: settings.py:363
def _process_metadata(self, value)
Definition: settings.py:256
def _process_max_assign_length(self, value)
Definition: settings.py:190
def _process_tagdoc(self, value)
Definition: settings.py:265
def _get_default_value(self, name)
Definition: settings.py:312
def _process_doc(self, value)
Definition: settings.py:150
def _convert_to_integer(self, name, value)
Definition: settings.py:306
def _process_cli_opts(self, opts)
Definition: settings.py:88
def _process_pythonpath(self, paths)
Definition: settings.py:315
def _split_pythonpath(self, path)
Definition: settings.py:321
def _process_report_background(self, colors)
Definition: settings.py:268
def _convert_to_positive_integer_or_default(self, name, value)
Definition: settings.py:302
def __setitem__(self, name, value)
Definition: settings.py:98
def _validate_expandkeywords(self, values)
Definition: settings.py:357
def __init__(self, options=None, **extra_options)
Definition: settings.py:81
def _escape_doc(self, value)
Definition: settings.py:160
def _process_tag_stat_link(self, value)
Definition: settings.py:295
def _process_randomize_value(self, original)
Definition: settings.py:194
def __contains__(self, setting)
Definition: settings.py:366
def _process_tag_stat_combine(self, pattern)
Definition: settings.py:277
def _validate_flatten_keywords(self, values)
Definition: settings.py:351
def _process_output_name(self, option, name)
Definition: settings.py:238
def _filter_empty(self, items)
Definition: settings.py:420
def _split_log_level(self, level)
Definition: settings.py:168
def _format_tag_patterns(self, pattern)
Definition: settings.py:284
def _process_value(self, name, value)
Definition: settings.py:103
def _process_max_error_lines(self, value)
Definition: settings.py:181
def _get_output_file(self, option)
Returns path of the requested output file and creates needed dirs.
Definition: settings.py:225
def _split_from_colon(self, value)
Definition: settings.py:260
def _get_output_extension(self, extension, file_type)
Definition: settings.py:245
def _process_log_level(self, level)
Definition: settings.py:163
def _validate_remove_keywords(self, values)
Definition: settings.py:344
Can be used when the core framework goes to unexpected state.
Definition: errors.py:53
def gather_failed_tests(output, empty_suite_ok=False)
Definition: gatherfailed.py:51
def gather_failed_suites(output, empty_suite_ok=False)
Definition: gatherfailed.py:68
def escape(item)
Definition: escaping.py:31
def html_escape(text, linkify=True)
Definition: markuputils.py:44
def seq2str(sequence, quote="'", sep=', ', lastsep=' and ')
Returns sequence in format ‘'item 1’, 'item 2' and 'item 3'`.
Definition: misc.py:79
def create_destination_directory(path, usage=None)
Definition: robotio.py:50
def abspath(path, case_normalize=False)
Replacement for os.path.abspath with some enhancements and bug fixes.
Definition: robotpath.py:65
def get_link_path(target, base)
Returns a relative path to target from base.
Definition: robotpath.py:78
def format_time(timetuple_or_epochsecs, daysep='', daytimesep=' ', timesep=':', millissep=None)
Returns a timestamp formatted from given time using separators.
Definition: robottime.py:208
def is_list_like(item)
Definition: robottypes.py:66
def split_args_from_name_or_path(name)
Split arguments embedded to name or path like Example:arg1:arg2.
Definition: text.py:141