Robot Framework Integrated Development Environment (RIDE)
importer.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 os
17 import sys
18 import inspect
19 
20 from robotide.lib.robot.errors import DataError
21 
22 from .encoding import system_decode
23 from .error import get_error_details
24 from .platform import JYTHON, IRONPYTHON, PY3
25 from .robotpath import abspath, normpath
26 from .robottypes import type_name, is_unicode
27 
28 if PY3:
29  from importlib import invalidate_caches as invalidate_import_caches
30 else:
31  invalidate_import_caches = lambda: None
32 if JYTHON:
33  from java.lang.System import getProperty
34 
35 
36 class Importer():
37 
38  def __init__(self, type=None, logger=None):
39  if not logger:
40  from robotide.lib.robot.output import LOGGER as logger
41  self._type_type = type or ''
42  self._logger_logger = logger
43  self._importers_importers = (ByPathImporter(logger),
44  NonDottedImporter(logger),
45  DottedImporter(logger))
46  self._by_path_importer_by_path_importer = self._importers_importers[0]
47 
48 
65  def import_class_or_module(self, name, instantiate_with_args=None,
66  return_source=False):
67  try:
68  imported, source = self._import_class_or_module_import_class_or_module(name)
69  self._log_import_succeeded_log_import_succeeded(imported, name, source)
70  imported = self._instantiate_if_needed_instantiate_if_needed(imported, instantiate_with_args)
71  except DataError as err:
72  self._raise_import_failed_raise_import_failed(name, err)
73  else:
74  return (imported, source) if return_source else imported
75 
76  def _import_class_or_module(self, name):
77  for importer in self._importers_importers:
78  if importer.handles(name):
79  return importer.import_(name)
80 
81 
91  def import_class_or_module_by_path(self, path, instantiate_with_args=None):
92  try:
93  imported, source = self._by_path_importer_by_path_importer.import_(path)
94  self._log_import_succeeded_log_import_succeeded(imported, imported.__name__, source)
95  return self._instantiate_if_needed_instantiate_if_needed(imported, instantiate_with_args)
96  except DataError as err:
97  self._raise_import_failed_raise_import_failed(path, err)
98 
99  def _raise_import_failed(self, name, error):
100  import_type = '%s ' % self._type_type if self._type_type else ''
101  msg = "Importing %s'%s' failed: %s" % (import_type, name, error.message)
102  if not error.details:
103  raise DataError(msg)
104  msg = [msg, error.details]
105  msg.extend(self._get_items_in_get_items_in('PYTHONPATH', sys.path))
106  if JYTHON:
107  classpath = getProperty('java.class.path').split(os.path.pathsep)
108  msg.extend(self._get_items_in_get_items_in('CLASSPATH', classpath))
109  raise DataError('\n'.join(msg))
110 
111  def _get_items_in(self, type, items):
112  yield '%s:' % type
113  for item in items:
114  if item:
115  yield ' %s' % (item if is_unicode(item)
116  else system_decode(item))
117 
118  def _instantiate_if_needed(self, imported, args):
119  if args is None:
120  return imported
121  if inspect.isclass(imported):
122  return self._instantiate_class_instantiate_class(imported, args)
123  if args:
124  raise DataError("Modules do not take arguments.")
125  return imported
126 
127  def _instantiate_class(self, imported, args):
128  try:
129  return imported(*args)
130  except:
131  raise DataError('Creating instance failed: %s\n%s' % get_error_details())
132 
133  def _log_import_succeeded(self, item, name, source):
134  import_type = '%s ' % self._type_type if self._type_type else ''
135  item_type = 'module' if inspect.ismodule(item) else 'class'
136  location = ("'%s'" % source) if source else 'unknown location'
137  self._logger_logger.info("Imported %s%s '%s' from %s."
138  % (import_type, item_type, name, location))
139 
140 
141 class _Importer():
142 
143  def __init__(self, logger):
144  self._logger_logger = logger
145 
146  def _import(self, name, fromlist=None, retry=True):
147  if name in sys.builtin_module_names:
148  raise DataError('Cannot import custom module with same name as '
149  'Python built-in module.')
151  try:
152  try:
153  return __import__(name, fromlist=fromlist)
154  except ImportError:
155  # Hack to support standalone Jython. For more information, see:
156  # https://github.com/robotframework/robotframework/issues/515
157  # http://bugs.jython.org/issue1778514
158  if JYTHON and fromlist and retry:
159  __import__('%s.%s' % (name, fromlist[0]))
160  return self._import_import(name, fromlist, retry=False)
161  # IronPython loses traceback when using plain raise.
162  # https://github.com/IronLanguages/main/issues/989
163  if IRONPYTHON:
164  exec('raise sys.exc_type, sys.exc_value, sys.exc_traceback')
165  raise
166  except:
167  raise DataError(*get_error_details())
168 
169  def _verify_type(self, imported):
170  if inspect.isclass(imported) or inspect.ismodule(imported):
171  return imported
172  raise DataError('Expected class or module, got %s.'
173  % type_name(imported))
174 
175  def _get_class_from_module(self, module, name=None):
176  klass = getattr(module, name or module.__name__, None)
177  return klass if inspect.isclass(klass) else None
178 
179  def _get_source(self, imported):
180  try:
181  return abspath(inspect.getfile(imported))
182  except TypeError:
183  return None
184 
185 
187 
190  _valid_import_extensions = ('.py', '.java', '.class', '')
191 
192  def handles(self, path):
193  return os.path.isabs(path)
194 
195  def import_(self, path):
196  self._verify_import_path_verify_import_path(path)
197  self._remove_wrong_module_from_sys_modules_remove_wrong_module_from_sys_modules(path)
198  module = self._import_by_path_import_by_path(path)
199  imported = self._get_class_from_module_get_class_from_module(module) or module
200  return self._verify_type_verify_type(imported), path
201 
202  def _verify_import_path(self, path):
203  if not os.path.exists(path):
204  raise DataError('File or directory does not exist.')
205  if not os.path.isabs(path):
206  raise DataError('Import path must be absolute.')
207  if not os.path.splitext(path)[1] in self._valid_import_extensions_valid_import_extensions:
208  raise DataError('Not a valid file or directory to import.')
209 
211  importing_from, name = self._split_path_to_module_split_path_to_module(path)
212  importing_package = os.path.splitext(path)[1] == ''
213  if self._wrong_module_imported_wrong_module_imported(name, importing_from, importing_package):
214  del sys.modules[name]
215  self._logger_logger.info("Removed module '%s' from sys.modules to import "
216  "fresh module." % name)
217 
218  def _split_path_to_module(self, path):
219  module_dir, module_file = os.path.split(abspath(path))
220  module_name = os.path.splitext(module_file)[0]
221  if module_name.endswith('$py'):
222  module_name = module_name[:-3]
223  return module_dir, module_name
224 
225  def _wrong_module_imported(self, name, importing_from, importing_package):
226  if name not in sys.modules:
227  return False
228  source = getattr(sys.modules[name], '__file__', None)
229  if not source: # play safe (occurs at least with java based modules)
230  return True
231  imported_from, imported_package = self._get_import_information_get_import_information(source)
232  return (normpath(importing_from, case_normalize=True) !=
233  normpath(imported_from, case_normalize=True) or
234  importing_package != imported_package)
235 
236  def _get_import_information(self, source):
237  imported_from, imported_file = self._split_path_to_module_split_path_to_module(source)
238  imported_package = imported_file == '__init__'
239  if imported_package:
240  imported_from = os.path.dirname(imported_from)
241  return imported_from, imported_package
242 
243  def _import_by_path(self, path):
244  module_dir, module_name = self._split_path_to_module_split_path_to_module(path)
245  sys.path.insert(0, module_dir)
246  try:
247  return self._import_import(module_name)
248  finally:
249  sys.path.remove(module_dir)
250 
251 
253 
254  def handles(self, name):
255  return '.' not in name
256 
257  def import_(self, name):
258  module = self._import_import(name)
259  imported = self._get_class_from_module_get_class_from_module(module) or module
260  return self._verify_type_verify_type(imported), self._get_source_get_source(imported)
261 
262 
264 
265  def handles(self, name):
266  return '.' in name
267 
268  def import_(self, name):
269  parent_name, lib_name = name.rsplit('.', 1)
270  parent = self._import_import(parent_name, fromlist=[str(lib_name)])
271  try:
272  imported = getattr(parent, lib_name)
273  except AttributeError:
274  raise DataError("Module '%s' does not contain '%s'."
275  % (parent_name, lib_name))
276  imported = self._get_class_from_module_get_class_from_module(imported, lib_name) or imported
277  return self._verify_type_verify_type(imported), self._get_source_get_source(imported)
Used when variable does not exist.
Definition: errors.py:67
def _wrong_module_imported(self, name, importing_from, importing_package)
Definition: importer.py:225
def import_class_or_module(self, name, instantiate_with_args=None, return_source=False)
Imports Python class/module or Java class with given name.
Definition: importer.py:66
def _raise_import_failed(self, name, error)
Definition: importer.py:99
def _instantiate_class(self, imported, args)
Definition: importer.py:127
def _instantiate_if_needed(self, imported, args)
Definition: importer.py:118
def _get_items_in(self, type, items)
Definition: importer.py:111
def _log_import_succeeded(self, item, name, source)
Definition: importer.py:133
def import_class_or_module_by_path(self, path, instantiate_with_args=None)
Import a Python module or Java class using a file system path.
Definition: importer.py:91
def __init__(self, type=None, logger=None)
Definition: importer.py:38
def _get_class_from_module(self, module, name=None)
Definition: importer.py:175
def _import(self, name, fromlist=None, retry=True)
Definition: importer.py:146
def info(msg, html=False, also_console=False)
Writes the message to the log file using the INFO level.
Definition: logger.py:115
def system_decode(string)
Decodes bytes from system (e.g.
Definition: encoding.py:81
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 abspath(path, case_normalize=False)
Replacement for os.path.abspath with some enhancements and bug fixes.
Definition: robotpath.py:87
def normpath(path, case_normalize=False)
Replacement for os.path.normpath with some enhancements.
Definition: robotpath.py:68