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 copy import copy
17
18from robot.utils import (getdoc, getshortdoc, is_java_init, is_java_method,
19                         is_list_like, printable_name, split_tags_from_doc,
20                         type_name)
21from robot.errors import DataError
22from robot.model import Tags
23
24from .arguments import (ArgumentSpec, DynamicArgumentParser,
25                        JavaArgumentCoercer, JavaArgumentParser,
26                        PythonArgumentParser)
27from .dynamicmethods import GetKeywordTypes
28from .librarykeywordrunner import (EmbeddedArgumentsRunner,
29                                   LibraryKeywordRunner, RunKeywordRunner)
30from .runkwregister import RUN_KW_REGISTER
31
32
33def Handler(library, name, method):
34    if RUN_KW_REGISTER.is_run_keyword(library.orig_name, name):
35        return _RunKeywordHandler(library, name, method)
36    if is_java_method(method):
37        return _JavaHandler(library, name, method)
38    else:
39        return _PythonHandler(library, name, method)
40
41
42def DynamicHandler(library, name, method, doc, argspec, tags=None):
43    if RUN_KW_REGISTER.is_run_keyword(library.orig_name, name):
44        return _DynamicRunKeywordHandler(library, name, method, doc, argspec, tags)
45    return _DynamicHandler(library, name, method, doc, argspec, tags)
46
47
48def InitHandler(library, method, docgetter=None):
49    Init = _PythonInitHandler if not is_java_init(method) else _JavaInitHandler
50    return Init(library, '__init__', method, docgetter)
51
52
53class _RunnableHandler(object):
54
55    def __init__(self, library, handler_name, handler_method, doc='', tags=None):
56        self.library = library
57        self._handler_name = handler_name
58        self.name = self._get_name(handler_name, handler_method)
59        self.arguments = self._parse_arguments(handler_method)
60        self._method = self._get_initial_handler(library, handler_name,
61                                                 handler_method)
62        doc, tags_from_doc = split_tags_from_doc(doc or '')
63        tags_from_attr = self._get_tags_from_attribute(handler_method)
64        self._doc = doc
65        self.tags = Tags(tuple(tags_from_doc) +
66                         tuple(tags_from_attr) +
67                         tuple(tags or ()))
68
69    def _get_name(self, handler_name, handler_method):
70        robot_name = getattr(handler_method, 'robot_name', None)
71        name = robot_name or printable_name(handler_name, code_style=True)
72        if not name:
73            raise DataError('Keyword name cannot be empty.')
74        return name
75
76    def _parse_arguments(self, handler_method):
77        raise NotImplementedError
78
79    def _get_tags_from_attribute(self, handler_method):
80        tags = getattr(handler_method, 'robot_tags', ())
81        if not is_list_like(tags):
82            raise DataError("Expected tags to be list-like, got %s."
83                            % type_name(tags))
84        return tags
85
86    def _get_initial_handler(self, library, name, method):
87        if library.scope.is_global:
88            return self._get_global_handler(method, name)
89        return None
90
91    def resolve_arguments(self, args, variables=None):
92        return self.arguments.resolve(args, variables)
93
94    @property
95    def doc(self):
96        return self._doc
97
98    @property
99    def longname(self):
100        return '%s.%s' % (self.library.name, self.name)
101
102    @property
103    def shortdoc(self):
104        return getshortdoc(self.doc)
105
106    @property
107    def libname(self):
108        return self.library.name
109
110    def create_runner(self, name):
111        return LibraryKeywordRunner(self)
112
113    def current_handler(self):
114        if self._method:
115            return self._method
116        return self._get_handler(self.library.get_instance(), self._handler_name)
117
118    def _get_global_handler(self, method, name):
119        return method
120
121    def _get_handler(self, lib_instance, handler_name):
122        return getattr(lib_instance, handler_name)
123
124
125class _PythonHandler(_RunnableHandler):
126
127    def __init__(self, library, handler_name, handler_method):
128        _RunnableHandler.__init__(self, library, handler_name, handler_method,
129                                  getdoc(handler_method))
130
131    def _parse_arguments(self, handler_method):
132        return PythonArgumentParser().parse(handler_method, self.longname)
133
134
135class _JavaHandler(_RunnableHandler):
136
137    def __init__(self, library, handler_name, handler_method):
138        _RunnableHandler.__init__(self, library, handler_name, handler_method)
139        signatures = self._get_signatures(handler_method)
140        self._arg_coercer = JavaArgumentCoercer(signatures, self.arguments)
141
142    def _parse_arguments(self, handler_method):
143        signatures = self._get_signatures(handler_method)
144        return JavaArgumentParser().parse(signatures, self.longname)
145
146    def _get_signatures(self, handler):
147        code_object = getattr(handler, 'im_func', handler)
148        return code_object.argslist[:code_object.nargs]
149
150    def resolve_arguments(self, args, variables=None):
151        positional, named = self.arguments.resolve(args, variables,
152                                                   dict_to_kwargs=True)
153        arguments = self._arg_coercer.coerce(positional, named,
154                                             dryrun=not variables)
155        return arguments, []
156
157
158class _DynamicHandler(_RunnableHandler):
159
160    def __init__(self, library, handler_name, dynamic_method, doc='',
161                 argspec=None, tags=None):
162        self._argspec = argspec
163        self._run_keyword_method_name = dynamic_method.name
164        self._supports_kwargs = dynamic_method.supports_kwargs
165        _RunnableHandler.__init__(self, library, handler_name,
166                                  dynamic_method.method, doc, tags)
167
168    def _parse_arguments(self, handler_method):
169        spec = DynamicArgumentParser().parse(self._argspec, self.longname)
170        if not self._supports_kwargs:
171            if spec.kwargs:
172                raise DataError("Too few '%s' method parameters for **kwargs "
173                                "support." % self._run_keyword_method_name)
174            if spec.kwonlyargs:
175                raise DataError("Too few '%s' method parameters for "
176                                "keyword-only arguments support."
177                                % self._run_keyword_method_name)
178        spec.types = GetKeywordTypes(self.library.get_instance())(self._handler_name)
179        return spec
180
181    def resolve_arguments(self, arguments, variables=None):
182        positional, named = self.arguments.resolve(arguments, variables)
183        if not self._supports_kwargs:
184            positional, named = self.arguments.map(positional, named)
185        return positional, named
186
187    def _get_handler(self, lib_instance, handler_name):
188        runner = getattr(lib_instance, self._run_keyword_method_name)
189        return self._get_dynamic_handler(runner, handler_name)
190
191    def _get_global_handler(self, method, name):
192        return self._get_dynamic_handler(method, name)
193
194    def _get_dynamic_handler(self, runner, name):
195        def handler(*positional, **kwargs):
196            if self._supports_kwargs:
197                return runner(name, positional, kwargs)
198            else:
199                return runner(name, positional)
200        return handler
201
202
203class _RunKeywordHandler(_PythonHandler):
204
205    def create_runner(self, name):
206        default_dry_run_keywords = ('name' in self.arguments.positional and
207                                    self._args_to_process)
208        return RunKeywordRunner(self, default_dry_run_keywords)
209
210    @property
211    def _args_to_process(self):
212        return RUN_KW_REGISTER.get_args_to_process(self.library.orig_name,
213                                                   self.name)
214
215    def resolve_arguments(self, args, variables=None):
216        args_to_process = self._args_to_process
217        return self.arguments.resolve(args, variables, resolve_named=False,
218                                      resolve_variables_until=args_to_process)
219
220
221class _DynamicRunKeywordHandler(_DynamicHandler, _RunKeywordHandler):
222    _parse_arguments = _RunKeywordHandler._parse_arguments
223    resolve_arguments = _RunKeywordHandler.resolve_arguments
224
225
226class _PythonInitHandler(_PythonHandler):
227
228    def __init__(self, library, handler_name, handler_method, docgetter):
229        _PythonHandler.__init__(self, library, handler_name, handler_method)
230        self._docgetter = docgetter
231
232    @property
233    def doc(self):
234        if self._docgetter:
235            self._doc = self._docgetter() or self._doc
236            self._docgetter = None
237        return self._doc
238
239    def _parse_arguments(self, handler_method):
240        parser = PythonArgumentParser(type='Test Library')
241        return parser.parse(handler_method, self.library.name)
242
243
244class _JavaInitHandler(_JavaHandler):
245
246    def __init__(self, library, handler_name, handler_method, docgetter):
247        _JavaHandler.__init__(self, library, handler_name, handler_method)
248        self._docgetter = docgetter
249
250    @property
251    def doc(self):
252        if self._docgetter:
253            self._doc = self._docgetter() or self._doc
254            self._docgetter = None
255        return self._doc
256
257    def _parse_arguments(self, handler_method):
258        parser = JavaArgumentParser(type='Test Library')
259        signatures = self._get_signatures(handler_method)
260        return parser.parse(signatures, self.library.name)
261
262
263class EmbeddedArgumentsHandler(object):
264
265    def __init__(self, name_regexp, orig_handler):
266        self.arguments = ArgumentSpec()  # Show empty argument spec for Libdoc
267        self.name_regexp = name_regexp
268        self._orig_handler = orig_handler
269
270    def __getattr__(self, item):
271        return getattr(self._orig_handler, item)
272
273    @property
274    def library(self):
275        return self._orig_handler.library
276
277    @library.setter
278    def library(self, library):
279        self._orig_handler.library = library
280
281    def matches(self, name):
282        return self.name_regexp.match(name) is not None
283
284    def create_runner(self, name):
285        return EmbeddedArgumentsRunner(self, name)
286
287    def __copy__(self):
288        orig_handler = copy(self._orig_handler)
289        return EmbeddedArgumentsHandler(self.name_regexp, orig_handler)
290