Robot Framework Integrated Development Environment (RIDE)
testlibraries.py
Go to the documentation of this file.
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 
16 import inspect
17 import os
18 
19 from robotide.lib.robot.errors import DataError
20 from robotide.lib.robot.libraries import STDLIBS
21 from robotide.lib.robot.output import LOGGER
22 from robotide.lib.robot.utils import (getdoc, get_error_details, Importer, is_java_init,
23  is_java_method, JYTHON, normalize, seq2str2, unic,
24  is_list_like, PY2, PYPY, type_name)
25 
26 from .arguments import EmbeddedArguments
27 from .context import EXECUTION_CONTEXTS
28 from .dynamicmethods import (GetKeywordArguments, GetKeywordDocumentation,
29  GetKeywordNames, GetKeywordTags, RunKeyword)
30 from .handlers import Handler, InitHandler, DynamicHandler, EmbeddedArgumentsHandler
31 from .handlerstore import HandlerStore
32 from .libraryscopes import LibraryScope
33 from .outputcapture import OutputCapturer
34 
35 
36 if JYTHON:
37  from java.lang import Object
38 else:
39  Object = None
40 
41 
42 def TestLibrary(name, args=None, variables=None, create_handlers=True):
43  if name in STDLIBS:
44  import_name = 'robot.libraries.' + name
45  else:
46  import_name = name
47  with OutputCapturer(library_import=True):
48  importer = Importer('test library')
49  libcode, source = importer.import_class_or_module(import_name,
50  return_source=True)
51  libclass = _get_lib_class(libcode)
52  lib = libclass(libcode, name, args or [], source, variables)
53  if create_handlers:
54  lib.create_handlers()
55  return lib
56 
57 
58 def _get_lib_class(libcode):
59  if inspect.ismodule(libcode):
60  return _ModuleLibrary
61  if GetKeywordNames(libcode):
62  if RunKeyword(libcode):
63  return _DynamicLibrary
64  else:
65  return _HybridLibrary
66  return _ClassLibrary
67 
68 
70 
73  _failure_level = 'INFO'
74 
75  def __init__(self, libcode, name, args, source, variables):
76  if os.path.exists(name):
77  name = os.path.splitext(os.path.basename(os.path.abspath(name)))[0]
78  self.versionversion = self._get_version_get_version(libcode)
79  self.namename = name
80  self.orig_nameorig_name = name # Stores original name when importing WITH NAME
81  self.sourcesource = source
82  self.handlershandlers = HandlerStore(self.namename, HandlerStore.TEST_LIBRARY_TYPE)
83  self.has_listenerhas_listener = None # Set when first instance is created
84  self._doc_doc = None
85  self.doc_formatdoc_format = self._get_doc_format_get_doc_format(libcode)
86  self.scopescope = LibraryScope(libcode, self)
87  self.initinit = self._create_init_handler_create_init_handler(libcode)
88  self.positional_args, self.named_args \
89  = self.initinit.resolve_arguments(args, variables)
90  self._libcode_libcode = libcode
91  self._libinst_libinst = None
92 
93  def __len__(self):
94  return len(self.handlershandlers)
95 
96  @property
97  doc = property
98 
99  def doc(self):
100  if self._doc_doc is None:
101  self._doc_doc = getdoc(self.get_instanceget_instance())
102  return self._doc_doc
103 
104  def create_handlers(self):
105  self._create_handlers_create_handlers(self.get_instanceget_instance())
106  self.reset_instancereset_instance()
107 
108  def reload(self):
109  self.handlershandlers = HandlerStore(self.namename, HandlerStore.TEST_LIBRARY_TYPE)
110  self._create_handlers_create_handlers(self.get_instanceget_instance())
111 
112  def start_suite(self):
113  self.scopescope.start_suite()
114 
115  def end_suite(self):
116  self.scopescope.end_suite()
117 
118  def start_test(self):
119  self.scopescope.start_test()
120 
121  def end_test(self):
122  self.scopescope.end_test()
123 
124  def _get_version(self, libcode):
125  return self._get_attr_get_attr(libcode, 'ROBOT_LIBRARY_VERSION') \
126  or self._get_attr_get_attr(libcode, '__version__')
127 
128  def _get_attr(self, object, attr, default='', upper=False):
129  value = unic(getattr(object, attr, default))
130  if upper:
131  value = normalize(value, ignore='_').upper()
132  return value
133 
134  def _get_doc_format(self, libcode):
135  return self._get_attr_get_attr(libcode, 'ROBOT_LIBRARY_DOC_FORMAT', upper=True)
136 
137  def _create_init_handler(self, libcode):
138  return InitHandler(self, self._resolve_init_method_resolve_init_method(libcode))
139 
140  def _resolve_init_method(self, libcode):
141  init = getattr(libcode, '__init__', None)
142  return init if init and self._valid_init_valid_init(init) else lambda: None
143 
144  def _valid_init(self, method):
145  # https://bitbucket.org/pypy/pypy/issues/2462/
146  if PYPY:
147  if PY2:
148  return method.__func__ is not object.__init__.__func__
149  return method is not object.__init__
150  return (inspect.ismethod(method) or # PY2
151  inspect.isfunction(method) or # PY3
152  is_java_init(method))
153 
154  def reset_instance(self, instance=None):
155  prev = self._libinst_libinst
156  if not self.scopescope.is_global:
157  self._libinst_libinst = instance
158  return prev
159 
160  def get_instance(self, create=True):
161  if not create:
162  return self._libinst_libinst
163  if self._libinst_libinst is None:
164  self._libinst_libinst = self._get_instance_get_instance(self._libcode_libcode)
165  if self.has_listenerhas_listener is None:
166  self.has_listenerhas_listener = bool(self.get_listenersget_listeners(self._libinst_libinst))
167  return self._libinst_libinst
168 
169  def _get_instance(self, libcode):
170  with OutputCapturer(library_import=True):
171  try:
172  return libcode(*self.positional_args, **dict(self.named_args))
173  except:
174  self._raise_creating_instance_failed_raise_creating_instance_failed()
175 
176  def get_listeners(self, libinst=None):
177  if libinst is None:
178  libinst = self.get_instanceget_instance()
179  listeners = getattr(libinst, 'ROBOT_LIBRARY_LISTENER', None)
180  if listeners is None:
181  return []
182  if is_list_like(listeners):
183  return listeners
184  return [listeners]
185 
187  if self.has_listenerhas_listener:
188  try:
189  listeners = EXECUTION_CONTEXTS.current.output.library_listeners
190  listeners.register(self.get_listenersget_listeners(), self)
191  except DataError as err:
192  self.has_listenerhas_listener = False
193  # Error should have information about suite where the
194  # problem occurred but we don't have such info here.
195  LOGGER.error("Registering listeners for library '%s' failed: %s"
196  % (self.namename, err))
197 
198  def unregister_listeners(self, close=False):
199  if self.has_listenerhas_listener:
200  listeners = EXECUTION_CONTEXTS.current.output.library_listeners
201  listeners.unregister(self, close)
202 
204  if self.scopescope.is_global:
205  for listener in self.get_listenersget_listeners():
206  self._close_listener_close_listener(listener)
207 
208  def _close_listener(self, listener):
209  method = (getattr(listener, 'close', None) or
210  getattr(listener, '_close', None))
211  try:
212  if method:
213  method()
214  except:
215  message, details = get_error_details()
216  name = getattr(listener, '__name__', None) or type_name(listener)
217  LOGGER.error("Calling method '%s' of listener '%s' failed: %s"
218  % (method.__name__, name, message))
219  LOGGER.info("Details:\n%s" % details)
220 
221  def _create_handlers(self, libcode):
222  try:
223  names = self._get_handler_names_get_handler_names(libcode)
224  except:
225  message, details = get_error_details()
226  raise DataError("Getting keyword names from library '%s' failed: %s"
227  % (self.namename, message), details)
228  for name in names:
229  method = self._try_to_get_handler_method_try_to_get_handler_method(libcode, name)
230  if method:
231  handler, embedded = self._try_to_create_handler_try_to_create_handler(name, method)
232  if handler:
233  try:
234  self.handlershandlers.add(handler, embedded)
235  except DataError as err:
236  LOGGER.error("Error in test library '%s': "
237  "Creating keyword '%s' failed: %s"
238  % (self.namename, handler.name, err.message))
239  else:
240  LOGGER.debug("Created keyword '%s'" % handler.name)
241 
242  def _get_handler_names(self, libcode):
243  return [name for name in dir(libcode)
244  if name[:1] != '_' or self._has_robot_name_has_robot_name(name, libcode)]
245 
246  def _has_robot_name(self, name, libcode):
247  try:
248  handler = self._get_handler_method_get_handler_method(libcode, name)
249  except DataError:
250  return False
251  return hasattr(handler, 'robot_name')
252 
253  def _try_to_get_handler_method(self, libcode, name):
254  try:
255  return self._get_handler_method_get_handler_method(libcode, name)
256  # TODO: RF 3.1: Catch only DataError or at least consider others errors.
257  # Don't want to do that in a minor release.
258  except:
259  self._report_adding_keyword_failed_report_adding_keyword_failed(name)
260  return None
261 
262  def _report_adding_keyword_failed(self, name, message=None, details=None,
263  level=None):
264  if not message:
265  message, details = get_error_details()
266  LOGGER.write("Adding keyword '%s' to library '%s' failed: %s"
267  % (name, self.namename, message), level or self._failure_level_failure_level)
268  if details:
269  LOGGER.debug('Details:\n%s' % details)
270 
271  def _get_handler_method(self, libcode, name):
272  method = getattr(libcode, name)
273  if not inspect.isroutine(method):
274  raise DataError('Not a method or function')
275  return method
276 
277  def _try_to_create_handler(self, name, method):
278  try:
279  handler = self._create_handler_create_handler(name, method)
280  except DataError as err:
281  self._report_adding_keyword_failed_report_adding_keyword_failed(name, err.message, level='ERROR')
282  return None, False
283  # TODO: RF 3.1: Catch only DataError or at least consider others errors.
284  # Don't want to do that in a minor release.
285  except:
286  self._report_adding_keyword_failed_report_adding_keyword_failed(name)
287  return None, False
288  try:
289  return self._get_possible_embedded_args_handler_get_possible_embedded_args_handler(handler)
290  except DataError as err:
291  self._report_adding_keyword_failed_report_adding_keyword_failed(handler.name, err.message,
292  level='ERROR')
293  return None, False
294 
295  def _create_handler(self, handler_name, handler_method):
296  return Handler(self, handler_name, handler_method)
297 
299  embedded = EmbeddedArguments(handler.name)
300  if embedded:
301  self._validate_embedded_count_validate_embedded_count(embedded, handler.arguments)
302  return EmbeddedArgumentsHandler(embedded.name, handler), True
303  return handler, False
304 
305  def _validate_embedded_count(self, embedded, arguments):
306  if not (arguments.minargs <= len(embedded.args) <= arguments.maxargs):
307  raise DataError('Embedded argument count does not match number of '
308  'accepted arguments.')
309 
311  msg, details = get_error_details()
312  if self.positional_args or self.named_args:
313  args = self.positional_args \
314  + ['%s=%s' % item for item in self.named_args]
315  args_text = 'arguments %s' % seq2str2(args)
316  else:
317  args_text = 'no arguments'
318  raise DataError("Initializing test library '%s' with %s failed: %s\n%s"
319  % (self.namename, args_text, msg, details))
320 
321 
323 
324  def _get_handler_method(self, libinst, name):
325  # Type is checked before using getattr to avoid calling properties,
326  # most importantly bean properties generated by Jython (issue 188).
327  for item in (libinst,) + inspect.getmro(libinst.__class__):
328  if item in (object, Object):
329  continue
330  if hasattr(item, '__dict__') and name in item.__dict__:
331  self._validate_handler_validate_handler(item.__dict__[name])
332  return getattr(libinst, name)
333  raise DataError('No non-implicit implementation found')
334 
335  def _validate_handler(self, handler):
336  if not inspect.isroutine(handler):
337  raise DataError('Not a method or function')
338  if self._is_implicit_java_or_jython_method_is_implicit_java_or_jython_method(handler):
339  raise DataError('Implicit methods are ignored')
340 
342  if not is_java_method(handler):
343  return False
344  for signature in handler.argslist[:handler.nargs]:
345  cls = signature.declaringClass
346  if not (cls is Object or cls.__module____module__ == 'org.python.proxies'):
347  return False
348  return True
349 
350 
352 
353  def _get_handler_method(self, libcode, name):
354  method = _BaseTestLibrary._get_handler_method(self, libcode, name)
355  if hasattr(libcode, '__all__') and name not in libcode.__all__:
356  raise DataError('Not exposed as a keyword')
357  return method
358 
359  def get_instance(self, create=True):
360  if not create:
361  return self._libcode_libcode
362  if self.has_listenerhas_listenerhas_listener is None:
363  self.has_listenerhas_listenerhas_listener = bool(self.get_listenersget_listeners(self._libcode_libcode))
364  return self._libcode_libcode
365 
366  def _create_init_handler(self, libcode):
367  return InitHandler(self, lambda: None)
368 
369 
371 
374  _failure_level = 'ERROR'
375 
376  def _get_handler_names(self, instance):
377  return GetKeywordNames(instance)()
378 
379 
381 
384  _failure_level = 'ERROR'
385 
386  def __init__(self, libcode, name, args, source, variables=None):
387  _BaseTestLibrary.__init__(self, libcode, name, args, source, variables)
388 
389  @property
390  doc = property
391 
392  def doc(self):
393  if self._doc_doc_doc is None:
394  self._doc_doc_doc = (self._get_kw_doc_get_kw_doc('__intro__') or
395  _BaseTestLibrary.doc.fget(self))
396  return self._doc_doc_doc
397 
398  def _get_kw_doc(self, name):
399  getter = GetKeywordDocumentation(self.get_instanceget_instance())
400  return getter(name)
401 
402  def _get_kw_args(self, name):
403  getter = GetKeywordArguments(self.get_instanceget_instance())
404  return getter(name)
405 
406  def _get_kw_tags(self, name):
407  getter = GetKeywordTags(self.get_instanceget_instance())
408  return getter(name)
409 
410  def _get_handler_names(self, instance):
411  return GetKeywordNames(instance)()
412 
413  def _get_handler_method(self, instance, name):
414  return RunKeyword(instance)
415 
416  def _create_handler(self, name, method):
417  argspec = self._get_kw_args_get_kw_args(name)
418  tags = self._get_kw_tags_get_kw_tags(name)
419  doc = self._get_kw_doc_get_kw_doc(name)
420  return DynamicHandler(self, name, method, doc, argspec, tags)
421 
422  def _create_init_handler(self, libcode):
423  docgetter = lambda: self._get_kw_doc_get_kw_doc('__init__')
424  return InitHandler(self, self._resolve_init_method_resolve_init_method(libcode), docgetter)
Used when variable does not exist.
Definition: errors.py:67
def _report_adding_keyword_failed(self, name, message=None, details=None, level=None)
def __init__(self, libcode, name, args, source, variables)
def _create_handler(self, handler_name, handler_method)
def _get_attr(self, object, attr, default='', upper=False)
def __init__(self, libcode, name, args, source, variables=None)
def Handler(library, name, method)
Definition: handlers.py:33
def InitHandler(library, method, docgetter=None)
Definition: handlers.py:48
def DynamicHandler(library, name, method, doc, argspec, tags=None)
Definition: handlers.py:42
def TestLibrary(name, args=None, variables=None, create_handlers=True)
def get_error_details(exclude_robot_traces=EXCLUDE_ROBOT_TRACES)
Returns error message and details of the last occurred exception.
Definition: error.py:46
def seq2str2(sequence)
Returns sequence in format [ item 1 | item 2 | ...
Definition: misc.py:126
def normalize(string, ignore=(), caseless=True, spaceless=True)
Normalizes given string according to given spec.
Definition: normalizing.py:30