Source code for robot.parsing.tablepopulators

#  Copyright 2008-2015 Nokia Networks
#  Copyright 2016-     Robot Framework Foundation
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import re

from robot.utils import py2to3

from .comments import CommentCache, Comments
from .settings import Documentation, MetadataList


[docs]class Populator(object): """Explicit interface for all populators."""
[docs] def add(self, row): raise NotImplementedError
[docs] def populate(self): raise NotImplementedError
[docs]@py2to3 class NullPopulator(Populator):
[docs] def add(self, row): pass
[docs] def populate(self): pass
def __nonzero__(self): return False
class _TablePopulator(Populator): def __init__(self, table): self._table = table self._populator = NullPopulator() self._comment_cache = CommentCache() def add(self, row): if self._is_cacheable_comment_row(row): self._comment_cache.add(row) else: self._add(row) def _is_cacheable_comment_row(self, row): return row.is_commented() def _add(self, row): if self._is_continuing(row): self._consume_comments() else: self._populator.populate() self._populator = self._get_populator(row) self._consume_standalone_comments() self._populator.add(row) def _is_continuing(self, row): return row.is_continuing() and self._populator def _get_populator(self, row): raise NotImplementedError def _consume_comments(self): self._comment_cache.consume_with(self._populator.add) def _consume_standalone_comments(self): self._consume_comments() def populate(self): self._consume_comments() self._populator.populate()
[docs]class SettingTablePopulator(_TablePopulator): def _get_populator(self, row): setter = self._table.get_setter(row.head) if not setter: return NullPopulator() if isinstance(setter.__self__, Documentation): return DocumentationPopulator(setter) if isinstance(setter.__self__, MetadataList): return MetadataPopulator(setter) return SettingPopulator(setter)
[docs]class VariableTablePopulator(_TablePopulator): def _get_populator(self, row): return VariablePopulator(self._table.add, row.head) def _consume_standalone_comments(self): self._comment_cache.consume_with(self._populate_standalone_comment) def _populate_standalone_comment(self, comment): populator = self._get_populator(comment) populator.add(comment) populator.populate()
[docs] def populate(self): self._populator.populate() self._consume_standalone_comments()
class _StepContainingTablePopulator(_TablePopulator): def _is_continuing(self, row): return row.is_indented() and self._populator or row.is_commented() def _is_cacheable_comment_row(self, row): return row.is_commented() and not self._populator
[docs]class TestTablePopulator(_StepContainingTablePopulator): def _get_populator(self, row): return TestCasePopulator(self._table.add)
[docs]class KeywordTablePopulator(_StepContainingTablePopulator): def _get_populator(self, row): return UserKeywordPopulator(self._table.add)
[docs]class ForLoopPopulator(Populator): def __init__(self, for_loop_creator): self._for_loop_creator = for_loop_creator self._loop = None self._populator = NullPopulator() self._declaration = [] self._declaration_comments = []
[docs] def add(self, row): dedented_row = row.dedent() if not self._loop: declaration_ready = self._populate_declaration(row) if not declaration_ready: return self._create_for_loop() if not row.is_continuing(): self._populator.populate() self._populator = StepPopulator(self._loop.add_step) self._populator.add(dedented_row)
def _populate_declaration(self, row): if row.starts_for_loop() or row.is_continuing(): self._declaration.extend(row.dedent().data) self._declaration_comments.extend(row.comments) return False return True def _create_for_loop(self): self._loop = self._for_loop_creator(self._declaration, self._declaration_comments)
[docs] def populate(self): if not self._loop: self._create_for_loop() self._populator.populate()
class _TestCaseUserKeywordPopulator(Populator): def __init__(self, test_or_uk_creator): self._test_or_uk_creator = test_or_uk_creator self._test_or_uk = None self._populator = NullPopulator() self._comment_cache = CommentCache() def add(self, row): if row.is_commented(): self._comment_cache.add(row) return if not self._test_or_uk: self._test_or_uk = self._test_or_uk_creator(row.head) dedented_row = row.dedent() if dedented_row: self._handle_data_row(dedented_row) def _handle_data_row(self, row): ending_for_loop = False if not self._continues(row): self._populator.populate() if row.all == ['END']: ending_for_loop = self._end_for_loop() self._populator = self._get_populator(row) self._comment_cache.consume_with(self._populate_comment_row) else: self._comment_cache.consume_with(self._populator.add) if not ending_for_loop: self._populator.add(row) def _end_for_loop(self): if self._populating_for_loop(): return True return self._test_or_uk.end_for_loop() def _populating_for_loop(self): return isinstance(self._populator, ForLoopPopulator) def _continues(self, row): return (row.is_continuing() and self._populator or self._populating_for_loop() and row.is_indented()) def _populate_comment_row(self, crow): populator = StepPopulator(self._test_or_uk.add_step) populator.add(crow) populator.populate() def populate(self): self._populator.populate() self._comment_cache.consume_with(self._populate_comment_row) def _get_populator(self, row): if row.starts_test_or_user_keyword_setting(): setter = self._setting_setter(row) if not setter: return NullPopulator() if isinstance(setter.__self__, Documentation): return DocumentationPopulator(setter) return SettingPopulator(setter) if row.starts_for_loop(): return ForLoopPopulator(self._test_or_uk.add_for_loop) return StepPopulator(self._test_or_uk.add_step) def _setting_setter(self, row): setting_name = row.test_or_user_keyword_setting_name() return self._test_or_uk.get_setter(setting_name)
[docs]class TestCasePopulator(_TestCaseUserKeywordPopulator): _item_type = 'test case'
[docs]class UserKeywordPopulator(_TestCaseUserKeywordPopulator): _item_type = 'keyword'
class _PropertyPopulator(Populator): def __init__(self, setter): self._setter = setter self._value = [] self._comments = Comments() self._data_added = False def add(self, row): if not row.is_commented(): self._add(row) self._comments.add(row) def _add(self, row): if row.cells == ['...']: self._deprecate_continuation_without_values() self._value.extend(row.tail if not self._data_added else row.data) self._data_added = True def _deprecate_continuation_without_values(self): location = self._get_deprecation_location() message = ("%sIgnoring lines with only continuation marker '...' is " "deprecated." % ('In %s: ' % location if location else '')) self._setter.__self__.report_invalid_syntax(message, level='WARN') def _get_deprecation_location(self): return ''
[docs]class VariablePopulator(_PropertyPopulator): def __init__(self, setter, name): _PropertyPopulator.__init__(self, setter) self._name = name
[docs] def populate(self): self._setter(self._name, self._value, self._comments.value)
def _get_deprecation_location(self): return "'Variables' section"
[docs]class SettingPopulator(_PropertyPopulator):
[docs] def populate(self): self._setter(self._value, self._comments.value)
def _get_deprecation_location(self): return "'%s' setting" % self._setter.__self__.setting_name
[docs]class DocumentationPopulator(_PropertyPopulator): _end_of_line_escapes = re.compile(r'(\\+)n?$')
[docs] def populate(self): self._setter(self._value, self._comments.value)
def _add(self, row): self._add_to_value(row.dedent().data) def _add_to_value(self, data): joiner = self._row_joiner() if joiner: self._value.append(joiner) self._value.append(' '.join(data)) def _row_joiner(self): if self._is_empty(): return None return self._joiner_based_on_eol_escapes() def _is_empty(self): return not self._value or \ (len(self._value) == 1 and self._value[0] == '') def _joiner_based_on_eol_escapes(self): match = self._end_of_line_escapes.search(self._value[-1]) if not match or len(match.group(1)) % 2 == 0: return '\\n' if not match.group(0).endswith('n'): return ' ' return None
[docs]class MetadataPopulator(DocumentationPopulator): def __init__(self, setter): _PropertyPopulator.__init__(self, setter) self._name = None
[docs] def populate(self): self._setter(self._name, self._value, self._comments.value)
def _add(self, row): data = row.dedent().data if self._name is None: self._name = data[0] if data else '' data = data[1:] self._add_to_value(data)
[docs]class StepPopulator(_PropertyPopulator): def _add(self, row): if row.cells == ['...']: self._deprecate_continuation_without_values() self._value.extend(row.data)
[docs] def populate(self): if self._value or self._comments.value: self._setter(self._value, self._comments.value)