Revision: 619578353fff
Author: Pekka Klärck
Date: Mon Jan 2 02:08:11 2012
Log: split importing logic to helper classes
http://code.google.com/p/robotframework/source/detail?r=619578353fff
Modified:
/src/robot/utils/importer.py
=======================================
--- /src/robot/utils/importer.py Sun Jan 1 15:10:26 2012
+++ /src/robot/utils/importer.py Mon Jan 2 02:08:11 2012
@@ -28,18 +28,46 @@
# TODO:
# - test and possibly prune tracebacks
# - test PYTHONPATH and CLASSPATH
-# - split logic related to different import types to helper classes
# - acceptance tests for issue 979
# - test can variable files be implemented with java/python classes
nowadays
# (possibly returning class when importing by path is bwic anyway)
class Importer(object):
- _import_path_endings = ('.py', '.java', '.class', '/', os.sep)
def __init__(self, type=None, logger=None):
self._type = type or ''
self._logger = logger
+ self._importers = [ByPathImporter(), NonDottedImporter(),
DottedImporter()]
+ self._by_path_importer = self._importers[0]
+
+ def import_class_or_module(self, name):
+ """Imports Python class/module or Java class with given name.
+
+ Class can either live in a module/package or be standalone Java
class.
+ In the former case the name is something like 'MyClass' and in the
+ latter it could be 'your.package.YourLibrary'. Python classes
always
+ live in a module, but if the module name is exactly same as the
class
+ name then simple 'MyLibrary' will import a class.
+
+ Python modules can be imported both using format 'MyModule' and
+ 'mymodule.submodule'.
+
+ `name` can also be a path to the imported file/directory. In that
case
+ importing is done using `import_class_or_module_by_path` method.
+ """
+ try:
+ imported, source = self._import_class_or_module(name)
+ except DataError, err:
+ self._raise_import_failed(name, err)
+ else:
+ self._log_import_succeeded(imported, name, source)
+ return imported
+
+ def _import_class_or_module(self, name):
+ for importer in self._importers:
+ if importer.handles(name):
+ return importer.import_(name)
def import_class_or_module_by_path(self, path):
"""Import a Python module or Java class using a file system path.
@@ -53,36 +81,12 @@
the source file must exist.
"""
try:
- self._verify_import_path(path)
- item = self._import_by_path(path)
- self._verify_type(item)
+ imported, source = self._by_path_importer.import_(path)
except DataError, err:
self._raise_import_failed(path, err)
else:
- self._log_import_succeeded(item, item.__name__, path)
- return item
-
- def _verify_import_path(self, path):
- if not os.path.exists(path):
- raise DataError('File or directory does not exist.')
- if not path.endswith(self._import_path_endings):
- raise DataError('Not a valid file or directory to import.')
-
- def _import_by_path(self, path):
- module_dir, module_name = self._split_path_to_module(path)
- sys.path.insert(0, module_dir)
- if module_name in sys.modules:
- del sys.modules[module_name]
- try:
- module = self._import(module_name)
- finally:
- sys.path.pop(0)
- return self._get_class_from_module(module) or module
-
- def _split_path_to_module(self, path):
- module_dir, module_file = os.path.split(abspath(path))
- module_name = os.path.splitext(module_file)[0]
- return module_dir, module_name
+ self._log_import_succeeded(imported, imported.__name__, source)
+ return imported
def _raise_import_failed(self, name, error):
import_type = '%s ' % self._type if self._type else ''
@@ -109,62 +113,8 @@
for item in items:
yield ' %s' % item
- def import_class_or_module(self, name):
- """Imports Python class/module or Java class with given name.
-
- Class can either live in a module/package or be standalone Java
class.
- In the former case the name is something like 'MyClass' and in the
- latter it could be 'your.package.YourLibrary'. Python classes
always
- live in a module, but if the module name is exactly same as the
class
- name then simple 'MyLibrary' will import a class.
-
- Python modules can be imported both using format 'MyModule' and
- 'mymodule.submodule'.
-
- `name` can also be a path to the imported file/directory. In that
case
- importing is done using `import_module_by_path` method.
- """
- try:
- imported, source = self._import_class_or_module(name)
- self._verify_type(imported)
- except DataError, err:
- self._raise_import_failed(name, err)
- else:
- self._log_import_succeeded(imported, name, source)
- return imported
-
- def _import_class_or_module(self, name):
- if self._is_valid_import_path(name):
- return self._import_by_path(name), name
- elif '.' not in name:
- return self._non_dotted_import(name)
- else:
- return self._dotted_import(name)
-
- def _is_valid_import_path(self, path):
- return os.path.exists(path) and
path.endswith(self._import_path_endings)
-
- def _non_dotted_import(self, name):
- module = self._import(name)
- source = self._get_source(module)
- return self._get_class_from_module(module) or module, source
-
- def _get_source(self, module):
- source = getattr(module, '__file__', None)
- return abspath(source) if source else None
-
- def _get_class_from_module(self, module):
- klass = getattr(module, module.__name__, None)
- return klass if inspect.isclass(klass) else None
-
- def _dotted_import(self, name):
- parentname, libname = name.rsplit('.', 1)
- parent = self._import(parentname, [str(libname)])
- try:
- item = getattr(parent, libname)
- except AttributeError:
- raise DataError("Module '%s' does not contain '%s'." %
(parentname, libname))
- return item, self._get_source(parent)
+
+class _Importer(object):
def _import(self, name, fromlist=None, retry=True):
try:
@@ -180,7 +130,76 @@
except:
raise DataError(*get_error_details())
- def _verify_type(self, item):
- if inspect.isclass(item) or inspect.ismodule(item):
- return item
- raise DataError('Expected class or module, got <%s>.' %
type(item).__name__)
+ def _verify_type(self, imported):
+ if inspect.isclass(imported) or inspect.ismodule(imported):
+ return imported
+ raise DataError('Expected class or module, got <%s>.' %
type(imported).__name__)
+
+ def _get_class_from_module(self, module):
+ klass = getattr(module, module.__name__, None)
+ return klass if inspect.isclass(klass) else None
+
+ def _get_source(self, module):
+ source = getattr(module, '__file__', None)
+ return abspath(source) if source else None
+
+
+class ByPathImporter(_Importer):
+ _import_path_endings = ('.py', '.java', '.class', '/', os.sep)
+
+ def handles(self, path):
+ return os.path.exists(path) and
path.endswith(self._import_path_endings)
+
+ def import_(self, path):
+ self._verify_import_path(path)
+ module = self._import_by_path(path)
+ imported = self._get_class_from_module(module) or module
+ return self._verify_type(imported), path
+
+ def _import_by_path(self, path):
+ module_dir, module_name = self._split_path_to_module(path)
+ sys.path.insert(0, module_dir)
+ if module_name in sys.modules:
+ del sys.modules[module_name]
+ try:
+ return self._import(module_name)
+ finally:
+ sys.path.pop(0)
+
+ def _verify_import_path(self, path):
+ if not os.path.exists(path):
+ raise DataError('File or directory does not exist.')
+ if not path.endswith(self._import_path_endings):
+ raise DataError('Not a valid file or directory to import.')
+
+ def _split_path_to_module(self, path):
+ module_dir, module_file = os.path.split(abspath(path))
+ module_name = os.path.splitext(module_file)[0]
+ return module_dir, module_name
+
+
+class NonDottedImporter(_Importer):
+
+ def handles(self, name):
+ return '.' not in name
+
+ def import_(self, name):
+ module = self._import(name)
+ imported = self._get_class_from_module(module) or module
+ return self._verify_type(imported), self._get_source(module)
+
+
+class DottedImporter(_Importer):
+
+ def handles(self, name):
+ return '.' in name
+
+ def import_(self, name):
+ parent_name, lib_name = name.rsplit('.', 1)
+ parent = self._import(parent_name, [str(lib_name)])
+ try:
+ imported = getattr(parent, lib_name)
+ except AttributeError:
+ raise DataError("Module '%s' does not contain '%s'."
+ % (parent_name, lib_name))
+ return self._verify_type(imported), self._get_source(parent)