Source code for robot.running.builder.parsers

#  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.

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

from robot.conf import LanguagesLike
from robot.errors import DataError
from robot.parsing import File, get_init_model, get_model, get_resource_model
from robot.utils import FileReader, get_error_message, read_rest_data, type_name

from .settings import FileSettings, InitFileSettings, TestDefaults
from .transformers import ResourceBuilder, SuiteBuilder
from ..model import ResourceFile, TestSuite


[docs]class Parser(ABC): @property def name(self) -> str: return type(self).__name__
[docs] def parse_suite_file(self, source: Path, defaults: TestDefaults) -> TestSuite: raise DataError(f"'{self.name}' does not support parsing suite files.")
[docs] def parse_init_file(self, source: Path, defaults: TestDefaults) -> TestSuite: raise DataError(f"'{self.name}' does not support parsing initialization files.")
[docs] def parse_resource_file(self, source: Path) -> ResourceFile: raise DataError(f"'{self.name}' does not support parsing resource files.")
[docs]class RobotParser(Parser): extensions = () def __init__(self, lang: LanguagesLike = None, process_curdir: bool = True): self.lang = lang self.process_curdir = process_curdir
[docs] def parse_suite_file(self, source: Path, defaults: TestDefaults) -> TestSuite: model = get_model(self._get_source(source), data_only=True, curdir=self._get_curdir(source), lang=self.lang) suite = TestSuite(name=TestSuite.name_from_source(source, self.extensions), source=source) SuiteBuilder(suite, FileSettings(defaults)).build(model) return suite
[docs] def parse_init_file(self, source: Path, defaults: TestDefaults) -> TestSuite: model = get_init_model(self._get_source(source), data_only=True, curdir=self._get_curdir(source), lang=self.lang) directory = source.parent suite = TestSuite(name=TestSuite.name_from_source(directory), source=directory) SuiteBuilder(suite, InitFileSettings(defaults)).build(model) return suite
[docs] def parse_model(self, model: File, defaults: 'TestDefaults|None' = None) -> TestSuite: source = model.source suite = TestSuite(name=TestSuite.name_from_source(source), source=source) SuiteBuilder(suite, FileSettings(defaults)).build(model) return suite
def _get_curdir(self, source: Path) -> 'str|None': return str(source.parent).replace('\\', '\\\\') if self.process_curdir else None def _get_source(self, source: Path) -> 'Path|str': return source
[docs] def parse_resource_file(self, source: Path) -> ResourceFile: model = get_resource_model(self._get_source(source), data_only=True, curdir=self._get_curdir(source), lang=self.lang) resource = ResourceFile(source=source) ResourceBuilder(resource).build(model) return resource
[docs]class RestParser(RobotParser): extensions = ('.robot.rst', '.rst', '.rest') def _get_source(self, source: Path) -> str: with FileReader(source) as reader: return read_rest_data(reader)
[docs]class JsonParser(Parser):
[docs] def parse_suite_file(self, source: Path, defaults: TestDefaults) -> TestSuite: return TestSuite.from_json(source)
[docs] def parse_init_file(self, source: Path, defaults: TestDefaults) -> TestSuite: return TestSuite.from_json(source)
[docs] def parse_resource_file(self, source: Path) -> ResourceFile: try: return ResourceFile.from_json(source) except DataError as err: raise DataError(f"Parsing JSON resource file '{source}' failed: {err}")
[docs]class NoInitFileDirectoryParser(Parser):
[docs] def parse_init_file(self, source: Path, defaults: TestDefaults) -> TestSuite: return TestSuite(name=TestSuite.name_from_source(source), source=source)
[docs]class CustomParser(Parser): def __init__(self, parser): self.parser = parser if not getattr(parser, 'parse', None): raise TypeError(f"'{self.name}' does not have mandatory 'parse' method.") if not self.extensions: raise TypeError(f"'{self.name}' does not have mandatory 'EXTENSION' " f"or 'extension' attribute.") @property def name(self) -> str: return type_name(self.parser) @property def extensions(self) -> 'tuple[str, ...]': ext = (getattr(self.parser, 'EXTENSION', None) or getattr(self.parser, 'extension', None)) extensions = [ext] if isinstance(ext, str) else list(ext or ()) return tuple(ext.lower().lstrip('.') for ext in extensions)
[docs] def parse_suite_file(self, source: Path, defaults: TestDefaults) -> TestSuite: return self._parse(self.parser.parse, source, defaults)
[docs] def parse_init_file(self, source: Path, defaults: TestDefaults) -> TestSuite: parse_init = getattr(self.parser, 'parse_init', None) try: return self._parse(parse_init, source, defaults, init=True) except NotImplementedError: return super().parse_init_file(source, defaults) # Raises DataError
def _parse(self, method, source, defaults, init=False) -> TestSuite: if not method: raise NotImplementedError accepts_defaults = len(signature(method).parameters) == 2 try: 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)}'.") except Exception: method_name = 'parse' if not init else 'parse_init' raise DataError(f"Calling '{self.name}.{method_name}()' failed: " f"{get_error_message()}") return suite