Robot Framework Integrated Development Environment (RIDE)
namespace.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 copy
17 import os
18 from collections import OrderedDict
19 from itertools import chain
20 
21 from robotide.lib.robot.errors import DataError, KeywordError
22 from robotide.lib.robot.libraries import STDLIBS
23 from robotide.lib.robot.output import LOGGER, Message
24 from robotide.lib.robot.parsing.settings import Library, Resource, Variables
25 from robotide.lib.robot.utils import (RecommendationFinder, eq, find_file, is_string,
26  printable_name, seq2str2)
27 
28 from .importer import ImportCache, Importer
29 from .runkwregister import RUN_KW_REGISTER
30 from .usererrorhandler import UserErrorHandler
31 from .userkeyword import UserLibrary
32 
33 IMPORTER = Importer()
34 
35 
36 class Namespace():
37 
40  _default_libraries = ('BuiltIn', 'Reserved', 'Easter')
41 
44  _library_import_by_path_endings = ('.py', '.java', '.class', '/', os.sep)
45 
46  def __init__(self, variables, suite, resource):
47  LOGGER.info("Initializing namespace for test suite '%s'" % suite.longname)
48  self.variablesvariables = variables
49  self._imports_imports = resource.imports
50  self._kw_store_kw_store = KeywordStore(resource)
51  self._imported_variable_files_imported_variable_files = ImportCache()
52  self._suite_name_suite_name = suite.longname
53  self._running_test_running_test = False
54 
55  @property
56  libraries = property
57 
58  def libraries(self):
59  return self._kw_store_kw_store.libraries.values()
60 
61  def handle_imports(self):
62  self._import_default_libraries_import_default_libraries()
63  self._handle_imports_handle_imports(self._imports_imports)
64 
66  for name in self._default_libraries_default_libraries:
67  self.import_libraryimport_library(name, notify=name == 'BuiltIn')
68 
69  def _handle_imports(self, import_settings):
70  for item in import_settings:
71  try:
72  if not item.name:
73  raise DataError('%s setting requires a name' % item.type)
74  self._import_import(item)
75  except DataError as err:
76  item.report_invalid_syntax(err.message)
77 
78  def _import(self, import_setting):
79  action = {'Library': self._import_library_import_library,
80  'Resource': self._import_resource_import_resource,
81  'Variables': self._import_variables_import_variables}[import_setting.type]
82  action(import_setting)
83 
84  def import_resource(self, name, overwrite=True):
85  self._import_resource_import_resource(Resource(None, name), overwrite=overwrite)
86 
87  def _import_resource(self, import_setting, overwrite=False):
88  path = self._resolve_name_resolve_name(import_setting)
89  self._validate_not_importing_init_file_validate_not_importing_init_file(path)
90  if overwrite or path not in self._kw_store_kw_store.resources:
91  resource = IMPORTER.import_resource(path)
92  self.variablesvariables.set_from_variable_table(resource.variables, overwrite)
93  user_library = UserLibrary(resource)
94  self._kw_store_kw_store.resources[path] = user_library
95  self._handle_imports_handle_imports(resource.imports)
96  LOGGER.imported("Resource", user_library.name,
97  importer=import_setting.source,
98  source=path)
99  else:
100  LOGGER.info("Resource file '%s' already imported by suite '%s'"
101  % (path, self._suite_name_suite_name))
102 
104  name = os.path.splitext(os.path.basename(path))[0]
105  if name.lower() == '__init__':
106  raise DataError("Initialization file '%s' cannot be imported as "
107  "a resource file." % path)
108 
109  def import_variables(self, name, args, overwrite=False):
110  self._import_variables_import_variables(Variables(None, name, args), overwrite)
111 
112  def _import_variables(self, import_setting, overwrite=False):
113  path = self._resolve_name_resolve_name(import_setting)
114  args = self._resolve_args_resolve_args(import_setting)
115  if overwrite or (path, args) not in self._imported_variable_files_imported_variable_files:
116  self._imported_variable_files_imported_variable_files.add((path, args))
117  self.variablesvariables.set_from_file(path, args, overwrite)
118  LOGGER.imported("Variables", os.path.basename(path),
119  args=list(args),
120  importer=import_setting.source,
121  source=path)
122  else:
123  msg = "Variable file '%s'" % path
124  if args:
125  msg += " with arguments %s" % seq2str2(args)
126  LOGGER.info("%s already imported by suite '%s'"
127  % (msg, self._suite_name_suite_name))
128 
129  def import_library(self, name, args=None, alias=None, notify=True):
130  self._import_library_import_library(Library(None, name, args=args, alias=alias),
131  notify=notify)
132 
133  def _import_library(self, import_setting, notify=True):
134  name = self._resolve_name_resolve_name(import_setting)
135  lib = IMPORTER.import_library(name, import_setting.args,
136  import_setting.alias, self.variablesvariables)
137  if lib.name in self._kw_store_kw_store.libraries:
138  LOGGER.info("Test library '%s' already imported by suite '%s'"
139  % (lib.name, self._suite_name_suite_name))
140  return
141  if notify:
142  LOGGER.imported("Library", lib.name,
143  args=list(import_setting.args),
144  originalname=lib.orig_name,
145  importer=import_setting.source,
146  source=lib.source)
147  self._kw_store_kw_store.libraries[lib.name] = lib
148  lib.start_suite()
149  if self._running_test_running_test:
150  lib.start_test()
151 
152  def _resolve_name(self, import_setting):
153  name = import_setting.name
154  try:
155  name = self.variablesvariables.replace_string(name)
156  except DataError as err:
157  self._raise_replacing_vars_failed_raise_replacing_vars_failed(import_setting, err)
158  return self._get_name_get_name(name, import_setting)
159 
160  def _raise_replacing_vars_failed(self, import_setting, err):
161  raise DataError("Replacing variables from setting '%s' failed: %s"
162  % (import_setting.type, err.message))
163 
164  def _get_name(self, name, import_setting):
165  if import_setting.type == 'Library' and not self._is_library_by_path_is_library_by_path(name):
166  return name
167  return find_file(name, import_setting.directory,
168  file_type=import_setting.type)
169 
170  def _is_library_by_path(self, path):
171  return path.lower().endswith(self._library_import_by_path_endings_library_import_by_path_endings)
172 
173  def _resolve_args(self, import_setting):
174  try:
175  return self.variablesvariables.replace_list(import_setting.args)
176  except DataError as err:
177  self._raise_replacing_vars_failed_raise_replacing_vars_failed(import_setting, err)
178 
179  def set_search_order(self, new_order):
180  old_order = self._kw_store_kw_store.search_order
181  self._kw_store_kw_store.search_order = new_order
182  return old_order
183 
184  def start_test(self):
185  self._running_test_running_test = True
186  self.variablesvariables.start_test()
187  for lib in self.librarieslibrarieslibraries:
188  lib.start_test()
189 
190  def end_test(self):
191  self.variablesvariables.end_test()
192  for lib in self.librarieslibrarieslibraries:
193  lib.end_test()
194  self._running_test_running_test = True
195 
196  def start_suite(self):
197  self.variablesvariables.start_suite()
198 
199  def end_suite(self, suite):
200  for lib in self.librarieslibrarieslibraries:
201  lib.end_suite()
202  if not suite.parent:
203  IMPORTER.close_global_library_listeners()
204  self.variablesvariables.end_suite()
205 
207  self.variablesvariables.start_keyword()
208 
209  def end_user_keyword(self):
210  self.variablesvariables.end_keyword()
211 
212  def get_library_instance(self, libname):
213  return self._kw_store_kw_store.get_library(libname).get_instance()
214 
216  return dict((name, lib.get_instance())
217  for name, lib in self._kw_store_kw_store.libraries.items())
218 
219  def reload_library(self, libname_or_instance):
220  library = self._kw_store_kw_store.get_library(libname_or_instance)
221  library.reload()
222  return library
223 
224  def get_runner(self, name):
225  try:
226  return self._kw_store_kw_store.get_runner(name)
227  except DataError as error:
228  return UserErrorHandler(error, name)
229 
230 
231 class KeywordStore():
232 
233  def __init__(self, resource):
234  self.user_keywordsuser_keywords = UserLibrary(resource,
235  UserLibrary.TEST_CASE_FILE_TYPE)
236  self.librarieslibraries = OrderedDict()
237  self.resourcesresources = ImportCache()
238  self.search_ordersearch_order = ()
239 
240  def get_library(self, name_or_instance):
241  if name_or_instance is None:
242  raise DataError("Library can not be None.")
243  if is_string(name_or_instance):
244  return self._get_lib_by_name_get_lib_by_name(name_or_instance)
245  return self._get_lib_by_instance_get_lib_by_instance(name_or_instance)
246 
247  def _get_lib_by_name(self, name):
248  if name in self.librarieslibraries:
249  return self.librarieslibraries[name]
250  matches = [lib for lib in self.librarieslibraries.values() if eq(lib.name, name)]
251  if len(matches) == 1:
252  return matches[0]
253  self._no_library_found_no_library_found(name, multiple=bool(matches))
254 
255  def _no_library_found(self, name, multiple=False):
256  if multiple:
257  raise DataError("Multiple libraries matching '%s' found." % name)
258  raise DataError("No library '%s' found." % name)
259 
260  def _get_lib_by_instance(self, instance):
261  for lib in self.librarieslibraries.values():
262  if lib.get_instance(create=False) is instance:
263  return lib
264  self._no_library_found_no_library_found(instance)
265 
266  def get_runner(self, name):
267  runner = self._get_runner_get_runner(name)
268  if runner is None:
269  self._raise_no_keyword_found_raise_no_keyword_found(name)
270  return runner
271 
272  def _raise_no_keyword_found(self, name):
273  msg = "No keyword with name '%s' found." % name
274  finder = KeywordRecommendationFinder(self.user_keywordsuser_keywords,
275  self.librarieslibraries,
276  self.resourcesresources)
277  recommendations = finder.recommend_similar_keywords(name)
278  msg = finder.format_recommendations(msg, recommendations)
279  raise KeywordError(msg)
280 
281  def _get_runner(self, name):
282  if not name:
283  raise DataError('Keyword name cannot be empty.')
284  if not is_string(name):
285  raise DataError('Keyword name must be a string.')
286  runner = self._get_runner_from_test_case_file_get_runner_from_test_case_file(name)
287  if not runner and '.' in name:
288  runner = self._get_explicit_runner_get_explicit_runner(name)
289  if not runner:
290  runner = self._get_implicit_runner_get_implicit_runner(name)
291  if not runner:
292  runner = self._get_bdd_style_runner_get_bdd_style_runner(name)
293  return runner
294 
295  def _get_bdd_style_runner(self, name):
296  lower = name.lower()
297  for prefix in ['given ', 'when ', 'then ', 'and ', 'but ']:
298  if lower.startswith(prefix):
299  runner = self._get_runner_get_runner(name[len(prefix):])
300  if runner:
301  runner = copy.copy(runner)
302  runner.name = name
303  return runner
304  return None
305 
306  def _get_implicit_runner(self, name):
307  runner = self._get_runner_from_resource_files_get_runner_from_resource_files(name)
308  if not runner:
309  runner = self._get_runner_from_libraries_get_runner_from_libraries(name)
310  return runner
311 
313  if name in self.user_keywordsuser_keywords.handlers:
314  return self.user_keywordsuser_keywords.handlers.create_runner(name)
315 
317  found = [lib.handlers.create_runner(name)
318  for lib in self.resourcesresources.values()
319  if name in lib.handlers]
320  if not found:
321  return None
322  if len(found) > 1:
323  found = self._get_runner_based_on_search_order_get_runner_based_on_search_order(found)
324  if len(found) == 1:
325  return found[0]
326  self._raise_multiple_keywords_found_raise_multiple_keywords_found(name, found)
327 
328  def _get_runner_from_libraries(self, name):
329  found = [lib.handlers.create_runner(name) for lib in self.librarieslibraries.values()
330  if name in lib.handlers]
331  if not found:
332  return None
333  if len(found) > 1:
334  found = self._get_runner_based_on_search_order_get_runner_based_on_search_order(found)
335  if len(found) == 2:
336  found = self._filter_stdlib_runner_filter_stdlib_runner(*found)
337  if len(found) == 1:
338  return found[0]
339  self._raise_multiple_keywords_found_raise_multiple_keywords_found(name, found)
340 
342  for libname in self.search_ordersearch_order:
343  for runner in runners:
344  if eq(libname, runner.libname):
345  return [runner]
346  return runners
347 
348  def _filter_stdlib_runner(self, runner1, runner2):
349  stdlibs_without_remote = STDLIBS - {'Remote'}
350  if runner1.library.orig_name in stdlibs_without_remote:
351  standard, custom = runner1, runner2
352  elif runner2.library.orig_name in stdlibs_without_remote:
353  standard, custom = runner2, runner1
354  else:
355  return [runner1, runner2]
356  if not RUN_KW_REGISTER.is_run_keyword(custom.library.orig_name, custom.name):
357  self._custom_and_standard_keyword_conflict_warning_custom_and_standard_keyword_conflict_warning(custom, standard)
358  return [custom]
359 
361  custom_with_name = standard_with_name = ''
362  if custom.library.name != custom.library.orig_name:
363  custom_with_name = " imported as '%s'" % custom.library.name
364  if standard.library.name != standard.library.orig_name:
365  standard_with_name = " imported as '%s'" % standard.library.name
366  warning = Message("Keyword '%s' found both from a custom test library "
367  "'%s'%s and a standard library '%s'%s. The custom "
368  "keyword is used. To select explicitly, and to get "
369  "rid of this warning, use either '%s' or '%s'."
370  % (standard.name,
371  custom.library.orig_name, custom_with_name,
372  standard.library.orig_name, standard_with_name,
373  custom.longname, standard.longname), level='WARN')
374  if custom.pre_run_messages:
375  custom.pre_run_messages.append(warning)
376  else:
377  custom.pre_run_messages = [warning]
378 
379  def _get_explicit_runner(self, name):
380  found = []
381  for owner_name, kw_name in self._yield_owner_and_kw_names_yield_owner_and_kw_names(name):
382  found.extend(self._find_keywords_find_keywords(owner_name, kw_name))
383  if len(found) > 1:
384  self._raise_multiple_keywords_found_raise_multiple_keywords_found(name, found, implicit=False)
385  return found[0] if found else None
386 
387  def _yield_owner_and_kw_names(self, full_name):
388  tokens = full_name.split('.')
389  for i in range(1, len(tokens)):
390  yield '.'.join(tokens[:i]), '.'.join(tokens[i:])
391 
392  def _find_keywords(self, owner_name, name):
393  return [owner.handlers.create_runner(name)
394  for owner in chain(self.librarieslibraries.values(), self.resourcesresources.values())
395  if eq(owner.name, owner_name) and name in owner.handlers]
396 
397  def _raise_multiple_keywords_found(self, name, found, implicit=True):
398  error = "Multiple keywords with name '%s' found" % name
399  if implicit:
400  error += ". Give the full name of the keyword you want to use"
401  names = sorted(runner.longname for runner in found)
402  raise KeywordError('\n '.join([error+':'] + names))
403 
404 
406 
407  def __init__(self, user_keywords, libraries, resources):
408  self.user_keywordsuser_keywords = user_keywords
409  self.librarieslibraries = libraries
410  self.resourcesresources = resources
411 
412 
413  def recommend_similar_keywords(self, name):
414  candidates = self._get_candidates_get_candidates('.' in name)
415  normalizer = lambda name: candidates.get(name, name).lower().replace(
416  '_', ' ')
417  finder = RecommendationFinder(normalizer)
418  return finder.find_recommendations(name, candidates)
419 
420  @staticmethod
421  def format_recommendations(msg, recommendations):
422  return RecommendationFinder.format_recommendations(
423  msg, recommendations)
424 
425  def _get_candidates(self, use_full_name):
426  names = {}
427  for owner, name in self._get_all_handler_names_get_all_handler_names():
428  full_name = '%s.%s' % (owner, name) if owner else name
429  names[full_name] = full_name if use_full_name else name
430  return names
431 
432 
436  handlers = [('', printable_name(handler.name, True))
437  for handler in self.user_keywordsuser_keywords.handlers]
438  for library in chain(self.librarieslibraries.values(), self.resourcesresources.values()):
439  if library.name != 'Reserved':
440  handlers.extend(
441  ((library.name or '',
442  printable_name(handler.name, code_style=True))
443  for handler in library.handlers))
444  # sort handlers to ensure consistent ordering between Jython and Python
445  return sorted(handlers)
Used when variable does not exist.
Definition: errors.py:67
Used when a test or keyword timeout occurs.
Definition: errors.py:83
Keeps track on and optionally caches imported items.
Definition: importer.py:110
def recommend_similar_keywords(self, name)
Return keyword names similar to name.
Definition: namespace.py:413
def _get_all_handler_names(self)
Return a list of (library_name, handler_name) tuples.
Definition: namespace.py:435
def __init__(self, user_keywords, libraries, resources)
Definition: namespace.py:407
def _no_library_found(self, name, multiple=False)
Definition: namespace.py:255
def _raise_multiple_keywords_found(self, name, found, implicit=True)
Definition: namespace.py:397
def _custom_and_standard_keyword_conflict_warning(self, custom, standard)
Definition: namespace.py:360
def _find_keywords(self, owner_name, name)
Definition: namespace.py:392
def _filter_stdlib_runner(self, runner1, runner2)
Definition: namespace.py:348
def import_variables(self, name, args, overwrite=False)
Definition: namespace.py:109
def _handle_imports(self, import_settings)
Definition: namespace.py:69
def _import_variables(self, import_setting, overwrite=False)
Definition: namespace.py:112
def import_library(self, name, args=None, alias=None, notify=True)
Definition: namespace.py:129
def __init__(self, variables, suite, resource)
Definition: namespace.py:46
def _resolve_name(self, import_setting)
Definition: namespace.py:152
def _import_library(self, import_setting, notify=True)
Definition: namespace.py:133
def _import_resource(self, import_setting, overwrite=False)
Definition: namespace.py:87
def _get_name(self, name, import_setting)
Definition: namespace.py:164
def _resolve_args(self, import_setting)
Definition: namespace.py:173
def _raise_replacing_vars_failed(self, import_setting, err)
Definition: namespace.py:160
def reload_library(self, libname_or_instance)
Definition: namespace.py:219
def import_resource(self, name, overwrite=True)
Definition: namespace.py:84
Created if creating handlers fail – running raises DataError.
def eq(str1, str2, ignore=(), caseless=True, spaceless=True)
Definition: match.py:26
def printable_name(string, code_style=False)
Generates and returns printable name from the given string.
Definition: misc.py:76
def seq2str2(sequence)
Returns sequence in format [ item 1 | item 2 | ...
Definition: misc.py:126
def find_file(path, basedir='.', file_type=None)
Definition: robotpath.py:146