Source code for robot.running.dynamicmethods

#  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 robot.errors import DataError
from robot.utils import get_error_message, is_list_like, type_name

from .arguments import PythonArgumentParser
from .context import EXECUTION_CONTEXTS


[docs] def no_dynamic_method(*args): return None
[docs] class DynamicMethod: _underscore_name = NotImplemented def __init__(self, instance): self.instance = instance self.method = self._get_method(instance) def _get_method(self, instance): for name in self._underscore_name, self._camelCaseName: method = getattr(instance, name, None) if callable(method): return method return no_dynamic_method @property def _camelCaseName(self): tokens = self._underscore_name.split("_") return "".join([tokens[0]] + [t.capitalize() for t in tokens[1:]]) @property def name(self): return self.method.__name__ def __call__(self, *args, **kwargs): try: ctx = EXECUTION_CONTEXTS.current result = self.method(*args, **kwargs) if ctx and ctx.asynchronous.is_loop_required(result): result = ctx.asynchronous.run_until_complete(result) return self._handle_return_value(result) except Exception: raise DataError( f"Calling dynamic method '{self.name}' failed: {get_error_message()}" ) def _handle_return_value(self, value): raise NotImplementedError def _to_string(self, value, allow_tuple=False, allow_none=False): if isinstance(value, str): return value if isinstance(value, bytes): return value.decode("UTF-8") if allow_tuple and is_list_like(value) and len(value) > 0: return tuple(value) if allow_none and value is None: return value allowed = "a string or a non-empty tuple" if allow_tuple else "a string" raise DataError(f"Return value must be {allowed}, got {type_name(value)}.") def _to_list(self, value): if value is None: return () if not is_list_like(value): raise DataError return value def _to_list_of_strings(self, value, allow_tuples=False): try: return [ self._to_string(item, allow_tuples) for item in self._to_list(value) ] except DataError: allowed = "strings or non-empty tuples" if allow_tuples else "strings" raise DataError( f"Return value must be a list of {allowed}, got {type_name(value)}." ) def __bool__(self): return self.method is not no_dynamic_method
[docs] class GetKeywordNames(DynamicMethod): _underscore_name = "get_keyword_names" def _handle_return_value(self, value): names = self._to_list_of_strings(value) return list(self._remove_duplicates(names)) def _remove_duplicates(self, names): seen = set() for name in names: if name not in seen: seen.add(name) yield name
[docs] class RunKeyword(DynamicMethod): _underscore_name = "run_keyword" def __init__( self, instance, keyword_name: "str|None" = None, supports_named_args: "bool|None" = None, ): super().__init__(instance) self.keyword_name = keyword_name self._supports_named_args = supports_named_args @property def supports_named_args(self) -> bool: if self._supports_named_args is None: spec = PythonArgumentParser().parse(self.method) self._supports_named_args = len(spec.positional) == 3 return self._supports_named_args def __call__(self, *positional, **named): if self.supports_named_args: args = (self.keyword_name, positional, named) elif named: # This should never happen. raise ValueError( f"'named' should not be used when named-argument support is " f"not enabled, got {named}." ) else: args = (self.keyword_name, positional) return self.method(*args)
[docs] class GetKeywordDocumentation(DynamicMethod): _underscore_name = "get_keyword_documentation" def _handle_return_value(self, value): return self._to_string(value or "")
[docs] class GetKeywordArguments(DynamicMethod): _underscore_name = "get_keyword_arguments" def __init__(self, instance, supports_named_args: "bool|None" = None): super().__init__(instance) if supports_named_args is None: self.supports_named_args = RunKeyword(instance).supports_named_args else: self.supports_named_args = supports_named_args def _handle_return_value(self, value): if value is None: if self.supports_named_args: return ["*varargs", "**kwargs"] return ["*varargs"] return self._to_list_of_strings(value, allow_tuples=True)
[docs] class GetKeywordTypes(DynamicMethod): _underscore_name = "get_keyword_types" def _handle_return_value(self, value): return value if self else {}
[docs] class GetKeywordTags(DynamicMethod): _underscore_name = "get_keyword_tags" def _handle_return_value(self, value): return self._to_list_of_strings(value)
[docs] class GetKeywordSource(DynamicMethod): _underscore_name = "get_keyword_source" def _handle_return_value(self, value): return self._to_string(value, allow_none=True)