1# Copyright 2008-2015 Nokia Networks 2# Copyright 2016- Robot Framework Foundation 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16from robot.errors import DataError 17from robot.utils import (get_error_message, is_java_method, is_bytes, 18 is_unicode, py2to3) 19 20from .arguments import JavaArgumentParser, PythonArgumentParser 21 22 23def no_dynamic_method(*args): 24 pass 25 26 27@py2to3 28class _DynamicMethod(object): 29 _underscore_name = NotImplemented 30 31 def __init__(self, lib): 32 self.method = self._get_method(lib) 33 34 def _get_method(self, lib): 35 for name in self._underscore_name, self._camelCaseName: 36 method = getattr(lib, name, None) 37 if callable(method): 38 return method 39 return no_dynamic_method 40 41 @property 42 def _camelCaseName(self): 43 tokens = self._underscore_name.split('_') 44 return ''.join([tokens[0]] + [t.capitalize() for t in tokens[1:]]) 45 46 @property 47 def name(self): 48 return self.method.__name__ 49 50 def __call__(self, *args): 51 try: 52 return self._handle_return_value(self.method(*args)) 53 except: 54 raise DataError("Calling dynamic method '%s' failed: %s" 55 % (self.method.__name__, get_error_message())) 56 57 def _handle_return_value(self, value): 58 raise NotImplementedError 59 60 def _to_string(self, value): 61 if is_unicode(value): 62 return value 63 if is_bytes(value): 64 return value.decode('UTF-8') 65 raise DataError('Return value must be string.') 66 67 def _to_list_of_strings(self, value): 68 try: 69 return [self._to_string(v) for v in value] 70 except (TypeError, DataError): 71 raise DataError('Return value must be list of strings.') 72 73 def __nonzero__(self): 74 return self.method is not no_dynamic_method 75 76 77class GetKeywordNames(_DynamicMethod): 78 _underscore_name = 'get_keyword_names' 79 80 def _handle_return_value(self, value): 81 names = self._to_list_of_strings(value or []) 82 return list(self._remove_duplicates(names)) 83 84 def _remove_duplicates(self, names): 85 seen = set() 86 for name in names: 87 if name not in seen: 88 seen.add(name) 89 yield name 90 91 92class RunKeyword(_DynamicMethod): 93 _underscore_name = 'run_keyword' 94 95 @property 96 def supports_kwargs(self): 97 if is_java_method(self.method): 98 return self._supports_java_kwargs(self.method) 99 return self._supports_python_kwargs(self.method) 100 101 def _supports_python_kwargs(self, method): 102 spec = PythonArgumentParser().parse(method) 103 return len(spec.positional) == 3 104 105 def _supports_java_kwargs(self, method): 106 func = self.method.im_func if hasattr(method, 'im_func') else method 107 signatures = func.argslist[:func.nargs] 108 spec = JavaArgumentParser().parse(signatures) 109 return (self._java_single_signature_kwargs(spec) or 110 self._java_multi_signature_kwargs(spec)) 111 112 def _java_single_signature_kwargs(self, spec): 113 return len(spec.positional) == 1 and spec.varargs and spec.kwargs 114 115 def _java_multi_signature_kwargs(self, spec): 116 return len(spec.positional) == 3 and not (spec.varargs or spec.kwargs) 117 118 119class GetKeywordDocumentation(_DynamicMethod): 120 _underscore_name = 'get_keyword_documentation' 121 122 def _handle_return_value(self, value): 123 return self._to_string(value or '') 124 125 126class GetKeywordArguments(_DynamicMethod): 127 _underscore_name = 'get_keyword_arguments' 128 129 def __init__(self, lib): 130 _DynamicMethod.__init__(self, lib) 131 self._supports_kwargs = RunKeyword(lib).supports_kwargs 132 133 def _handle_return_value(self, value): 134 if value is None: 135 if self._supports_kwargs: 136 return ['*varargs', '**kwargs'] 137 return ['*varargs'] 138 return self._to_list_of_strings(value) 139 140 141class GetKeywordTypes(_DynamicMethod): 142 _underscore_name = 'get_keyword_types' 143 144 def _handle_return_value(self, value): 145 return value 146 147 148class GetKeywordTags(_DynamicMethod): 149 _underscore_name = 'get_keyword_tags' 150 151 def _handle_return_value(self, value): 152 return self._to_list_of_strings(value or []) 153