Skip to content

Commit

Permalink
Make TestDefauls optional with custom parsers.
Browse files Browse the repository at this point in the history
Also add/enhance API docs. Part of #1283.
  • Loading branch information
pekkaklarck committed Apr 25, 2023
1 parent b35e709 commit 168ade4
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 11 deletions.
2 changes: 1 addition & 1 deletion atest/testdata/parsing/custom/CustomParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def parse(self, source: Path, defaults: TestDefaults) -> TestSuite:
raise TypeError('Ooops!')
if self.bad_return:
return 'bad'
suite = custom.parse(source, None)
suite = custom.parse(source)
for test in suite.tests:
test.tags += defaults.tags
test.setup = defaults.setup
Expand Down
2 changes: 1 addition & 1 deletion atest/testdata/parsing/custom/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
extension = 'ignored'


def parse(source, defaults):
def parse(source):
suite = TestSuite(source=source, metadata={'Parser': 'Custom'})
for line in source.read_text().splitlines():
if not line or line[0] in ('*', '#'):
Expand Down
2 changes: 1 addition & 1 deletion src/robot/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
via :mod:`robot.api` like ``from robot.api import SkipExecution``.
* :mod:`.interfaces` module containing optional base classes that can be used
when creating libraries or listeners. New in Robot Framework 6.1.
when creating libraries and other extensions. New in Robot Framework 6.1.
* :mod:`.parsing` module exposing the parsing APIs. This module is new in Robot
Framework 4.0. Various parsing related functions and classes were exposed
Expand Down
17 changes: 13 additions & 4 deletions src/robot/api/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ class Parser(ABC):
The mandatory :attr:`extension` attribute specifies what file extension or
extensions a parser supports. It can be set either as a class or instance
attribute, and it can be either a string or a list/tuple of strings. The
attribute, and it can be either a string or a sequence of strings. The
attribute can also be named ``EXTENSION``, which typically works better
when a parser is implemented as a module.
Expand All @@ -594,15 +594,24 @@ class Parser(ABC):
def parse(self, source: Path, defaults: TestDefaults) -> TestSuite:
"""Mandatory method for parsing suite files.
FIXME: PARSER: Better documentation (incl. parameter docs).
:param source: Path to the file to parse.
:param defaults: Default values set for test in init files.
The ``defaults`` argument is optional. It is possible to implement
this method also so that it accepts only ``source``.
"""
raise NotImplementedError

def parse_init(self, source: Path, defaults: TestDefaults) -> TestSuite:
"""Optional method for parsing suite initialization files.
FIXME: PARSER: Better documentation (incl. parameter docs).
:param source: Path to the file to parse.
:param defaults: Default values to used with tests in child suites.
The ``defaults`` argument is optional. It is possible to implement
this method also so that it accepts only ``source``.
If not implemented, possible initialization files cause an error.
If this method is not implemented, possible initialization files cause
an error.
"""
raise NotImplementedError
6 changes: 4 additions & 2 deletions src/robot/running/builder/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# limitations under the License.

from abc import ABC
from inspect import signature
from pathlib import Path

from robot.conf import LanguagesLike
Expand Down Expand Up @@ -140,11 +141,12 @@ def parse_init_file(self, source: Path, defaults: TestDefaults) -> TestSuite:
except NotImplementedError:
return super().parse_init_file(source, defaults) # Raises DataError

def _parse(self, method, *args, init=False) -> TestSuite:
def _parse(self, method, source, defaults, init=False) -> TestSuite:
if not method:
raise NotImplementedError
accepts_defaults = len(signature(method).parameters) == 2
try:
suite = method(*args)
suite = method(source, defaults) if accepts_defaults else method(source)
if not isinstance(suite, TestSuite):
raise TypeError(f"Return value should be 'robot.running.TestSuite', "
f"got '{type_name(suite)}'.")
Expand Down
6 changes: 4 additions & 2 deletions src/robot/running/builder/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def __init__(self, parent: 'TestDefaults|None' = None):

@property
def setup(self) -> 'Keyword|None':
"""Setup as a `Keyword` object or `None` when not set.
"""Default setup as a ``Keyword`` object or ``None`` when not set.
Can be set also using a dictionary.
"""
Expand All @@ -73,7 +73,7 @@ def setup(self, setup: 'Keyword|KeywordDict|None'):

@property
def teardown(self) -> 'Keyword|None':
"""Teardown as a `Keyword` object or `None` when not set.
"""Default teardown as a ``Keyword`` object or ``None`` when not set.
Can be set also using a dictionary.
"""
Expand All @@ -91,6 +91,7 @@ def teardown(self, teardown: 'Keyword|KeywordDict|None'):

@property
def tags(self) -> Tags:
"""Default tags. Can be set also as a sequence."""
return self._tags + self.parent.tags if self.parent else self._tags

@tags.setter
Expand All @@ -99,6 +100,7 @@ def tags(self, tags: 'Sequence[str]'):

@property
def timeout(self) -> 'str|None':
"""Default timeout."""
if self._timeout:
return self._timeout
if self.parent:
Expand Down

0 comments on commit 168ade4

Please sign in to comment.