27 from .comments
import Comment
28 from .populators
import FromFilePopulator, FromDirectoryPopulator, NoTestsFound
29 from .settings
import (Documentation, Fixture, Timeout, Tags, Metadata,
30 Library, Resource, Variables, Arguments, Return,
31 Template, MetadataList, ImportList)
33 re_set_var = re.compile(
r"(?i)^set[ ](\S.*)+variable$")
46 def TestData(parent=None, source=None, include_suites=None,
47 warn_on_skipped='DEPRECATED', extensions=None, settings=None):
49 if warn_on_skipped !=
'DEPRECATED':
50 warnings.warn(
"Option 'warn_on_skipped' is deprecated and has no "
51 "effect.", DeprecationWarning)
52 if os.path.isdir(source):
62 _setting_table_names =
'Setting',
'Settings'
66 _variable_table_names =
'Variable',
'Variables'
70 _testcase_table_names =
'Test Case',
'Test Cases',
'Task',
'Tasks'
74 _keyword_table_names =
'Keyword',
'Keywords'
78 _comment_table_names =
'Comment',
'Comments'
100 table.set_header(header_row)
121 name = header_row[0]
if header_row
else ''
123 if title
not in self.
_tables_tables:
128 return self.
_tables_tables[title]
142 "Section name '%s' is deprecated. Use '%s' instead."
143 % (deprecated, name), level=
'WARN'
148 "Unrecognized table header '%s'. Available headers for data: "
149 "'Setting(s)', 'Variable(s)', 'Test Case(s)', 'Task(s)' and "
150 "'Keyword(s)'. Use 'Comment(s)' to embedded additional data."
171 return os.path.splitext(os.path.basename(self.
sourcesource))[0]
175 name = name.replace(
'_',
' ').strip()
176 return name.title()
if name.islower()
else name
179 return name.split(
'__', 1)[-1]
185 return self.keyword_table.keywords
191 return self.setting_table.imports
194 initfile = getattr(self,
'initfile',
None)
195 path = os.path.join(self.
sourcesource, initfile)
if initfile
else self.
sourcesource
196 LOGGER.write(
"Error in file '%s': %s" % (path, message), level)
207 return DataFileWriter(**options).
write(self)
218 def __init__(self, parent=None, source=None, settings=None):
219 self.
directorydirectory = os.path.dirname(source)
if source
else None
226 _TestData.__init__(self, parent, source)
247 return any(table
for table
in self)
257 self.
directorydirectory = os.path.dirname(source)
if source
else None
264 _TestData.__init__(self, source=source)
273 LOGGER.info(
"Imported resource file '%s' (%d keywords)."
276 LOGGER.warn(
"Imported resource file '%s' is empty." % self.
sourcesource)
280 raise DataError(
"Resource file '%s' cannot contain tests or "
281 "tasks." % self.
sourcesource)
298 def __init__(self, parent=None, source=None, settings=None):
307 _TestData.__init__(self, parent, source)
309 def populate(self, include_suites=None, extensions=None, recurse=True):
311 extensions, recurse, self.
_tab_size_tab_size)
316 return os.path.basename(self.
sourcesource)
320 LOGGER.error(
"Test suite initialization file in '%s' cannot "
321 "contain tests or tasks." % self.
sourcesource)
325 def add_child(self, path, include_suites, extensions=None):
328 include_suites=include_suites,
329 extensions=extensions, settings=self.
_settings_settings))
352 if self._old_header_matcher.match(header):
360 return self.
_header_header
or [self.type.title() +
's']
372 return self.
parentparent.source
378 return self.
parentparent.directory
384 return bool(self.
_header_header
or len(self))
387 return sum(1
for item
in self)
404 if setter
is not None:
407 if setter
is not None:
417 return self.
_setters_setters[title](self)
430 "Setting '%s' is deprecated. Use '%s' instead."
431 % (deprecated, correct), level=
'WARN'
435 raise NotImplementedError
442 _Table.__init__(self, parent)
456 _old_header_matcher = property
466 self.
importsimports.add(
Library(self, name, args, comment=comment))
470 self.
importsimports.add(
Resource(self, name, invalid_args, comment=comment))
478 return sum(1
for setting
in self
if setting.is_set())
485 _setters = {
'Documentation':
lambda s: s.doc.populate,
486 'Suite Setup':
lambda s: s.suite_setup.populate,
487 'Suite Teardown':
lambda s: s.suite_teardown.populate,
488 'Test Setup':
lambda s: s.test_setup.populate,
489 'Test Teardown':
lambda s: s.test_teardown.populate,
490 'Force Tags':
lambda s: s.force_tags.populate,
491 'Default Tags':
lambda s: s.default_tags.populate,
492 'Test Template':
lambda s: s.test_template.populate,
493 'Test Timeout':
lambda s: s.test_timeout.populate,
494 'Library':
lambda s: s.imports.populate_library,
495 'Resource':
lambda s: s.imports.populate_resource,
496 'Variables':
lambda s: s.imports.populate_variables,
497 'Metadata':
lambda s: s.metadata.populate}
501 _aliases = {
'Task Setup':
'Test Setup',
502 'Task Teardown':
'Test Teardown',
503 'Task Template':
'Test Template',
504 'Task Timeout':
'Test Timeout'}
518 _setters = {
'Documentation':
lambda s: s.doc.populate,
519 'Library':
lambda s: s.imports.populate_library,
520 'Resource':
lambda s: s.imports.populate_resource,
521 'Variables':
lambda s: s.imports.populate_variables}
524 for setting
in [self.
docdoc] + self.
importsimports.data:
532 _setters = {
'Documentation':
lambda s: s.doc.populate,
533 'Suite Setup':
lambda s: s.suite_setup.populate,
534 'Suite Teardown':
lambda s: s.suite_teardown.populate,
535 'Test Setup':
lambda s: s.test_setup.populate,
536 'Test Teardown':
lambda s: s.test_teardown.populate,
537 'Test Timeout':
lambda s: s.test_timeout.populate,
538 'Force Tags':
lambda s: s.force_tags.populate,
539 'Library':
lambda s: s.imports.populate_library,
540 'Resource':
lambda s: s.imports.populate_resource,
541 'Variables':
lambda s: s.imports.populate_variables,
542 'Metadata':
lambda s: s.metadata.populate}
555 _Table.__init__(self, parent)
559 _old_header_matcher = property
564 def add(self, name, value, comment=None):
575 _Table.__init__(self, parent)
579 if self.
_header_header
and header:
581 _Table.set_header(self, header)
584 tasks1 =
normalize(name1)
in (
'task',
'tasks')
585 tasks2 =
normalize(name2)
in (
'task',
'tasks')
586 if tasks1
is not tasks2:
587 raise DataError(
'One file cannot have both tests and tasks.')
590 _old_header_matcher = property
597 return self.
teststests[-1]
600 return iter(self.
teststests)
603 return bool(self.
_header_header)
610 _Table.__init__(self, parent)
614 _old_header_matcher = property
630 def __init__(self, parent, name, value, comment=None):
632 self.
namename = name.rstrip(
'= ')
633 if name.startswith(
'$')
and value == []:
652 return bool(self.
namename
or ''.join(self.
valuevalue))
659 % (self.
namename, message), level)
666 self.steps.append(
Step(content, comment))
667 return self.steps[-1]
670 new = copy.deepcopy(self)
672 self._add_to_parent(new)
690 "Using '...' as test case name is deprecated. It will be "
691 "considered line continuation in Robot Framework 3.2.",
698 _setters = {
'Documentation':
lambda s: s.doc.populate,
699 'Template':
lambda s: s.template.populate,
700 'Setup':
lambda s: s.setup.populate,
701 'Teardown':
lambda s: s.teardown.populate,
702 'Tags':
lambda s: s.tags.populate,
703 'Timeout':
lambda s: s.timeout.populate}
709 return self.
parentparent.source
715 return self.
parentparent.directory
721 self.
stepssteps.append(
Step([
'FOR'] + declaration, comment))
723 return self.
stepssteps[-1]
729 loop.steps.extend(steps)
730 self.
stepssteps[-len(steps):] = []
735 for step
in reversed(self.
stepssteps):
736 if isinstance(step, ForLoop):
745 type_ =
'test case' if type(self)
is TestCase
else 'keyword'
746 message =
"Invalid syntax in %s '%s': %s" % (type_, self.
namename, message)
750 self.
parentparent.tests.append(test)
760 for element
in [self.
docdoc, self.
tagstags, self.
setupsetup,
780 "Using '...' as keyword name is deprecated. It will be "
781 "considered line continuation in Robot Framework 3.2.",
788 _setters = {
'Documentation':
lambda s: s.doc.populate,
789 'Arguments':
lambda s: s.args.populate,
790 'Return':
lambda s: s.return_.populate,
791 'Timeout':
lambda s: s.timeout.populate,
792 'Teardown':
lambda s: s.teardown.populate,
793 'Tags':
lambda s: s.tags.populate}
823 flavors = {
'IN',
'IN RANGE',
'IN ZIP',
'IN ENUMERATE'}
824 normalized_flavors = NormalizedDict((f, f)
for f
in flavors)
827 def __init__(self, parent, declaration, indentation=[], comment=None):
829 self.
indentindent = indentation
if isinstance(indentation, list)
else [indentation]
831 print(f
"\nDEBUG: ForLoop init ENTER declaration={declaration[:]}")
832 if declaration[0] ==
'':
834 for idx
in range(0, len(declaration)):
835 if declaration[idx] ==
'':
842 print(f
"\nDEBUG: ForLoop init indent {isize} self.inner_kw_pos={self.inner_kw_pos}\ndeclaration={declaration[:]}")
848 self.
itemsitems = declaration[index+1:]
854 for index, item
in enumerate(declaration):
855 if item
in self.
flavorsflavors:
860 return correct, index
862 return item.upper(), index
863 return 'IN', len(declaration)
866 self.
parentparent.report_invalid_syntax(
867 "Using '%s' as a FOR loop separator is deprecated. "
868 "Use '%s' instead." % (deprecated, correct), level=
'WARN'
877 def as_list(self, indent=True, include_comment=True):
878 comments = self.
commentcomment.
as_list()
if include_comment
else []
881 return self.
indentindent + [self.
first_kwfirst_kw] + self.
varsvars + [self.flavor] + self.
itemsitems + comments
884 return iter(self.
stepssteps)
890 self.
indentindent.append(
'')
891 return len(self.
indentindent)
895 return len(self.
indentindent)
911 if isinstance(content, Step):
914 elif isinstance(content, list):
921 if isinstance(comment, list):
923 elif isinstance(comment, str):
939 for _
in range(0, index):
942 self.
argsargsargs = content[index + 1:]
if content
and index <= len(content) - 1
else []
945 if index < len(content):
946 self.
namename = content[index]
if content
else None
954 return content.lower()
in [
'comment',
'builtin.comment']
or content.startswith(
'#')
961 if cells
and cells != [
'']:
963 if index < len(cells)
and is_var(cells[index].rstrip(
'=')):
965 if 0 <= index < len(cells)
and self.
is_kind_of_commentis_kind_of_comment(cells[index]):
972 while idx < len(cells):
973 if is_var(cells[idx].rstrip(
'=')):
974 assign.append(cells.pop(idx))
985 while idx < len(cells)
and positional:
994 if idx + 2 < len(cells):
997 if cells[idx + 1] ==
'IN ENUMERATE' or cells[idx + 2] ==
'IN ENUMERATE':
1011 if is_var(cells[idx].rstrip(
'='))
and positional:
1012 assign.append(cells.pop(idx))
1035 def as_list(self, indent=False, include_comment=True):
1046 while index < size
and self.
cellscellscells[index] ==
'':
1048 return index
if 0 <= index < size
else index - 1
if index - 1 > 0
else 0
1067 self.
__init____init__(content, comment)
1071 kw = [self.
namename]
if self.
namename
is not None and self.
namename[0] !=
'#' else []
1073 if self.
namename ==
'FOR':
1100 return all(value.lower() ==
'value' for value
in header[1:])
1106 if header[1].
lower() !=
'action':
1108 for arg
in header[2:]:
1109 if not arg.lower().startswith(
'arg'):
Used when variable does not exist.
The parsed representation of a for-loop.
def _get_flavor_and_index(self, declaration)
def _report_deprecated_flavor_syntax(self, deprecated, correct)
def decrease_indent(self)
def increase_indent(self)
def as_list(self, indent=True, include_comment=True)
def __init__(self, parent, declaration, indentation=[], comment=None)
def __init__(self, parent)
The parsed resource file object.
def _table_is_allowed(self, table)
def __init__(self, source=None, settings=None)
def as_list(self, indent=False, include_comment=True)
import inspect stack = inspect.stack() the_class = stack[1][0].f_locals["self"].__class__....
def first_empty_cell(self)
def __init__(self, content, comment=None)
def increase_indent(self)
def add_step(self, content, comment=None)
def first_non_empty_cell(self, content=None)
def is_kind_of_comment(content)
def decrease_indent(self)
The parsed test case file object.
def __init__(self, parent=None, source=None, settings=None)
def _validate_mode(self, name1, name2)
def __init__(self, parent)
def set_header(self, header)
def report_invalid_syntax(self, message, level='ERROR')
def add_for_loop(self, declaration, comment=None)
def __init__(self, parent, name)
def _add_to_parent(self, test)
def _find_last_empty_for_and_steps_after(self)
The parsed test data directory object.
def _table_is_allowed(self, table)
def __init__(self, parent=None, source=None, settings=None)
def add_child(self, path, include_suites, extensions=None)
def populate(self, include_suites=None, extensions=None, recurse=True)
def __init__(self, parent, name)
def _add_to_parent(self, test)
def add(self, name, value, comment=None)
def __init__(self, parent)
def report_invalid_syntax(self, message, level='ERROR')
def __init__(self, parent, name, value, comment=None)
def __init__(self, parent)
def add_metadata(self, name, value='', comment=None)
def add_library(self, name, args=None, comment=None)
def add_resource(self, name, invalid_args=None, comment=None)
def add_variables(self, name, args=None, comment=None)
def set_header(self, header)
def __init__(self, parent)
def report_invalid_syntax(self, message, level='ERROR')
def _prune_old_style_headers(self, header)
def add_preamble(self, row)
def _strip_possible_prefix_from_name(self, name)
string _setting_table_names
string _variable_table_names
def start_table(self, header_row)
def __init__(self, parent=None, source=None)
def _find_table(self, header_row)
string _testcase_table_names
def _format_name(self, name)
def _report_deprecated_table(self, deprecated, name)
def report_invalid_syntax(self, message, level='ERROR')
def _table_is_allowed(self, table)
string _comment_table_names
def _resolve_deprecated_table(self, used_name)
def _report_unrecognized_table(self, name)
string _keyword_table_names
def save(self, **options)
Writes this datafile to disk.
def _get_setter(self, name)
def report_invalid_syntax(self, message, level='ERROR')
def _get_deprecated_setter(self, name)
def get_setter(self, name)
def _report_deprecated_setting(self, deprecated, correct)
def add_step(self, content, comment=None)
def write(msg, level='INFO', html=False)
Writes the message to the log file using the given level.
def TestData(parent=None, source=None, include_suites=None, warn_on_skipped='DEPRECATED', extensions=None, settings=None)
Parses a file or directory to a corresponding model object.
def normalize(string, ignore=(), caseless=True, spaceless=True)
Normalizes given string according to given spec.
def abspath(path, case_normalize=False)
Replacement for os.path.abspath with some enhancements and bug fixes.
def is_var(string, identifiers='$@&')