# 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 ast
import re
from robot.utils import normalize_whitespace
from ..lexer import Token
[docs]class Statement(ast.AST):
type = None
_fields = ('type', 'tokens')
_attributes = ('lineno', 'col_offset', 'end_lineno', 'end_col_offset')
_statement_handlers = {}
def __init__(self, tokens):
self.tokens = tuple(tokens)
@property
def lineno(self):
return self.tokens[0].lineno if self.tokens else -1
@property
def col_offset(self):
return self.tokens[0].col_offset if self.tokens else -1
@property
def end_lineno(self):
return self.tokens[-1].lineno if self.tokens else -1
@property
def end_col_offset(self):
return self.tokens[-1].end_col_offset if self.tokens else -1
[docs] @classmethod
def register(cls, subcls):
cls._statement_handlers[subcls.type] = subcls
if subcls.type == Token.KEYWORD:
cls._statement_handlers[Token.ASSIGN] = subcls
if subcls.type == Token.ERROR:
cls._statement_handlers[Token.FATAL_ERROR] = subcls
return subcls
[docs] @classmethod
def from_tokens(cls, tokens):
handlers = cls._statement_handlers
for token in tokens:
if token.type in handlers:
return handlers[token.type](tokens)
return EmptyLine(tokens)
@property
def data_tokens(self):
return [t for t in self.tokens if t.type not in Token.NON_DATA_TOKENS]
[docs] def get_token(self, type):
"""Return a token with the given ``type``.
If there are no matches, return ``None``. If there are multiple
matches, return the first match.
"""
for t in self.tokens:
if t.type == type:
return t
return None
[docs] def get_tokens(self, *types):
"""Return tokens having any of the given ``types``."""
return [t for t in self.tokens if t.type in types]
[docs] def get_value(self, type, default=None):
"""Return value of a token with the given ``type``.
If there are no matches, return ``default``. If there are multiple
matches, return the value of the first match.
"""
token = self.get_token(type)
return token.value if token else default
[docs] def get_values(self, *types):
"""Return values of tokens having any of the given ``types``."""
return tuple(t.value for t in self.tokens if t.type in types)
@property
def lines(self):
line = []
for token in self.tokens:
line.append(token)
if token.type == Token.EOL:
yield line
line = []
if line:
yield line
@property
def error(self):
tokens = self.get_tokens(Token.ERROR, Token.FATAL_ERROR)
if not tokens:
return None
if len(tokens) == 1:
return tokens[0].error
errors = ['%d) %s' % (i+1, t.error) for i, t in enumerate(tokens)]
return '\n\n'.join(['Multiple errors:'] + errors)
def __len__(self):
return len(self.tokens)
def __getitem__(self, item):
return self.tokens[item]
[docs]class SingleValue(Statement):
@property
def value(self):
values = self.get_values(Token.NAME, Token.ARGUMENT)
if values and values[0].upper() != 'NONE':
return values[0]
return None
[docs]class MultiValue(Statement):
@property
def values(self):
return self.get_values(Token.ARGUMENT)
[docs]class Fixture(Statement):
@property
def name(self):
return self.get_value(Token.NAME)
@property
def args(self):
return self.get_values(Token.ARGUMENT)
[docs]@Statement.register
class LibraryImport(Statement):
type = Token.LIBRARY
@property
def name(self):
return self.get_value(Token.NAME)
@property
def args(self):
return self.get_values(Token.ARGUMENT)
@property
def alias(self):
with_name = self.get_token(Token.WITH_NAME)
return self.get_tokens(Token.NAME)[-1].value if with_name else None
[docs]@Statement.register
class ResourceImport(Statement):
type = Token.RESOURCE
@property
def name(self):
return self.get_value(Token.NAME)
[docs]@Statement.register
class VariablesImport(Statement):
type = Token.VARIABLES
@property
def name(self):
return self.get_value(Token.NAME)
@property
def args(self):
return self.get_values(Token.ARGUMENT)
[docs]@Statement.register
class Documentation(DocumentationOrMetadata):
type = Token.DOCUMENTATION
@property
def value(self):
tokens = self.get_tokens(Token.ARGUMENT)
return self._join_value(tokens)
[docs]@Statement.register
class SuiteSetup(Fixture):
type = Token.SUITE_SETUP
[docs]@Statement.register
class SuiteTeardown(Fixture):
type = Token.SUITE_TEARDOWN
[docs]@Statement.register
class TestSetup(Fixture):
type = Token.TEST_SETUP
[docs]@Statement.register
class TestTeardown(Fixture):
type = Token.TEST_TEARDOWN
[docs]@Statement.register
class TestTemplate(SingleValue):
type = Token.TEST_TEMPLATE
[docs]@Statement.register
class TestTimeout(SingleValue):
type = Token.TEST_TIMEOUT
[docs]@Statement.register
class Variable(Statement):
type = Token.VARIABLE
@property
def name(self):
name = self.get_value(Token.VARIABLE)
if name.endswith('='):
return name[:-1].rstrip()
return name
@property
def value(self):
return self.get_values(Token.ARGUMENT)
[docs]@Statement.register
class TestCaseName(Statement):
type = Token.TESTCASE_NAME
@property
def name(self):
return self.get_value(Token.TESTCASE_NAME)
[docs]@Statement.register
class KeywordName(Statement):
type = Token.KEYWORD_NAME
@property
def name(self):
return self.get_value(Token.KEYWORD_NAME)
[docs]@Statement.register
class Setup(Fixture):
type = Token.SETUP
[docs]@Statement.register
class Teardown(Fixture):
type = Token.TEARDOWN
[docs]@Statement.register
class Template(SingleValue):
type = Token.TEMPLATE
[docs]@Statement.register
class Timeout(SingleValue):
type = Token.TIMEOUT
[docs]@Statement.register
class Arguments(MultiValue):
type = Token.ARGUMENTS
[docs]@Statement.register
class Return(MultiValue):
type = Token.RETURN
[docs]@Statement.register
class KeywordCall(Statement):
type = Token.KEYWORD
@property
def keyword(self):
return self.get_value(Token.KEYWORD)
@property
def args(self):
return self.get_values(Token.ARGUMENT)
@property
def assign(self):
return self.get_values(Token.ASSIGN)
[docs]@Statement.register
class TemplateArguments(Statement):
type = Token.ARGUMENT
@property
def args(self):
return self.get_values(self.type)
[docs]@Statement.register
class End(Statement):
type = Token.END
@property
def value(self):
return self.get_value(Token.END)
[docs]@Statement.register
class Error(Statement):
type = Token.ERROR
[docs]class EmptyLine(Statement):
type = Token.EOL
[docs] @classmethod
def from_value(cls, value):
return EmptyLine([Token(Token.EOL, value)])