2 new revisions:
Revision: 6c8fb623fb76
Author: Pekka Klärck
Date: Tue Jan 3 14:32:16 2012
Log: Importing by path: Only remove modules from sys.modules when
absolute ...
http://code.google.com/p/robotframework/source/detail?r=6c8fb623fb76
Revision: 72654cec0803
Author: Pekka Klärck
Date: Tue Jan 3 14:41:00 2012
Log: cleanup and ooops fix
http://code.google.com/p/robotframework/source/detail?r=72654cec0803
==============================================================================
Revision: 6c8fb623fb76
Author: Pekka Klärck
Date: Tue Jan 3 14:32:16 2012
Log: Importing by path: Only remove modules from sys.modules when
absolute must to prevent corrupting imported modules.
Update issue 979
Now modules are removed from sys.modules only when needed. The logic works
similarly as with RF 2.6.x but now also modules implemented as directories
are handled correctly.
http://code.google.com/p/robotframework/source/detail?r=6c8fb623fb76
Modified:
/src/robot/utils/importer.py
/utest/utils/test_importer_util.py
=======================================
--- /src/robot/utils/importer.py Tue Jan 3 06:07:16 2012
+++ /src/robot/utils/importer.py Tue Jan 3 14:32:16 2012
@@ -22,11 +22,10 @@
from robot.errors import DataError
from .error import get_error_details
-from .robotpath import abspath
+from .robotpath import abspath, normpath
# TODO:
-# - 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)
@@ -157,6 +156,7 @@
def import_(self, path):
self._verify_import_path(path)
+ self._remove_wrong_module_from_sys_modules(path)
module = self._import_by_path(path)
imported = self._get_class_from_module(module) or module
return self._verify_type(imported), path
@@ -169,25 +169,42 @@
if not os.path.splitext(path)[1] in self._valid_import_extensions:
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)
- try:
- return self._import_fresh_module(module_name)
- finally:
- sys.path.pop(0)
+ def _remove_wrong_module_from_sys_modules(self, path):
+ importing_from, name = self._split_path_to_module(path)
+ importing_package = os.path.splitext(path)[1] == ''
+ if self._wrong_module_imported(name, importing_from,
importing_package):
+ del sys.modules[name]
+ self._logger.info("Removed module '%s' from sys.modules to
import "
+ "fresh module." % name)
def _split_path_to_module(self, path):
module_dir, module_file = os.path.split(abspath(path))
module_name = os.path.splitext(module_file)[0]
+ if module_name.endswith('$py'):
+ module_name = module_name[:-3]
return module_dir, module_name
- def _import_fresh_module(self, name):
- if name in sys.modules:
- del sys.modules[name]
- self._logger.info("Removed module '%s' from sys.modules to
import "
- "fresh module." % name)
- return self._import(name)
+ def _wrong_module_imported(self, name, importing_from,
importing_package):
+ module = sys.modules.get(name)
+ if not module:
+ return False
+ source = getattr(module, '__file__', None)
+ if not source: # play safe (occurs at least with java based
modules)
+ return True
+ imported_from, imported_file = self._split_path_to_module(source)
+ imported_package = imported_file == '__init__'
+ if importing_package:
+ imported_from = os.path.dirname(imported_from)
+ return (normpath(importing_from) != normpath(imported_from) or
+ importing_package != imported_package)
+
+ def _import_by_path(self, path):
+ module_dir, module_name = self._split_path_to_module(path)
+ sys.path.insert(0, module_dir)
+ try:
+ return self._import(module_name)
+ finally:
+ sys.path.pop(0)
class NonDottedImporter(_Importer):
=======================================
--- /utest/utils/test_importer_util.py Tue Jan 3 06:05:18 2012
+++ /utest/utils/test_importer_util.py Tue Jan 3 14:32:16 2012
@@ -6,10 +6,10 @@
import sys
import os
import re
-from os.path import abspath, dirname, exists, isabs, join, normpath
+from os.path import abspath, basename, dirname, exists, join, normpath
from robot.errors import DataError
-from robot.utils.importer import Importer
+from robot.utils.importer import Importer, ByPathImporter
from robot.utils.asserts import (assert_equals, assert_true, assert_raises,
assert_raises_with_msg)
@@ -49,7 +49,7 @@
msg = msg.replace(ext, '')
self.messages.append(msg)
- def assert_message(self, msg, index=-1):
+ def assert_message(self, msg, index=0):
assert_equals(self.messages[index], msg)
@@ -64,77 +64,90 @@
def test_python_file(self):
path = create_temp_file('test.py')
- self._import_and_verify(path)
+ self._import_and_verify(path, remove='test')
+ self._assert_imported_message('test', path)
def test_python_directory(self):
create_temp_file('__init__.py')
- self._import_and_verify(TESTDIR + os.sep)
+ module_name = basename(TESTDIR)
+ self._import_and_verify(TESTDIR, remove=module_name)
+ self._assert_imported_message(module_name, TESTDIR)
+
+ def test_import_same_file_multiple_times(self):
+ path = create_temp_file('test.py')
+ self._import_and_verify(path, remove='test')
+ self._assert_imported_message('test', path)
+ self._import_and_verify(path)
+ self._assert_imported_message('test', path)
+ self._import_and_verify(path, name='library')
+ self._assert_imported_message('test', path, type='library module')
def test_import_different_file_and_directory_with_same_name(self):
- removed = "Removed module 'test' from sys.modules to import fresh
module."
- imported = "Imported module 'test' from '%s'."
path1 = create_temp_file('test.py', attr=1)
- self._import_and_verify(path1, attr=1)
- self.logger.assert_message(imported % path1)
+ self._import_and_verify(path1, attr=1, remove='test')
+ self._assert_imported_message('test', path1)
path2 = join(TESTDIR, 'test')
os.mkdir(path2)
create_temp_file(join(path2, '__init__.py'), attr=2)
self._import_and_verify(path2, attr=2, directory=path2)
- self.logger.assert_message(removed, index=0)
- self.logger.assert_message(imported % path2, index=1)
+ self._assert_removed_message('test')
+ self._assert_imported_message('test', path2, index=1)
path3 = create_temp_file(join(path2, 'test.py'), attr=3)
self._import_and_verify(path3, attr=3, directory=path2)
- self.logger.assert_message(removed, index=0)
- self.logger.assert_message(imported % path3, index=1)
+ self._assert_removed_message('test')
+ self._assert_imported_message('test', path3, index=1)
def test_import_class_from_file(self):
path = create_temp_file('test.py', extra_content='class test:\n
def m(s): return 1')
- klass = Importer().import_class_or_module_by_path(path)
+ klass = self._import(path, remove='test')
+ self._assert_imported_message('test', path, type='class')
assert_true(inspect.isclass(klass))
assert_equals(klass.__name__, 'test')
assert_equals(klass().m(), 1)
def test_invalid_python_file(self):
path = create_temp_file('test.py', extra_content='invalid content')
- error = assert_raises(DataError, self._import_and_verify, path)
+ error = assert_raises(DataError, self._import_and_verify, path,
remove='test')
assert_prefix(error, "Importing '%s' failed: SyntaxError:" % path)
- def test_logging_when_importing_module(self):
- path = join(LIBDIR, 'classes.py')
- self._import(path, name='lib')
- self.logger.assert_message("Imported lib module 'classes'
from '%s'." % path)
-
- def test_logging_when_importing_python_class(self):
- path = join(LIBDIR, 'ExampleLibrary.py')
- self._import(path)
- self.logger.assert_message("Imported class 'ExampleLibrary'
from '%s'." % path)
-
if sys.platform.startswith('java'):
- def test_java_class(self):
- self._import_and_verify(join(CURDIR, 'ImportByPath.java'))
- self._import_and_verify(join(CURDIR, 'ImportByPath.class'))
+ def test_java_class_with_java_extension(self):
+ path = join(CURDIR, 'ImportByPath.java')
+ self._import_and_verify(path, remove='ImportByPath')
+ self._assert_imported_message('ImportByPath', path,
type='class')
+
+ def test_java_class_with_class_extension(self):
+ path = join(CURDIR, 'ImportByPath.class')
+ self._import_and_verify(path, remove='ImportByPath',
name='java')
+ self._assert_imported_message('ImportByPath', path, type='java
class')
def test_importing_java_package_fails(self):
- path = join(LIBDIR, 'javapkg') + os.sep
+ path = join(LIBDIR, 'javapkg')
assert_raises_with_msg(DataError,
"Importing '%s' failed: Expected class
or "
"module, got <javapackage>." % path,
- self._import, path)
-
- def test_logging_when_importing_java_class(self):
+ self._import, path, remove='javapkg')
+
+ def
test_removing_from_sys_modules_when_importing_multiple_times(self):
path = join(CURDIR, 'ImportByPath.java')
- self._import(path, name='java')
- self.logger.assert_message("Imported java class 'ImportByPath'
from '%s'." % path)
-
- def _import_and_verify(self, path, attr=42, directory=TESTDIR):
- module = self._import(path)
+ self._import(path, name='java', remove='ImportByPath')
+ self._assert_imported_message('ImportByPath', path, 'java
class')
+ self._import(path)
+ self._assert_removed_message('ImportByPath')
+ self._assert_imported_message('ImportByPath', path, 'class',
index=1)
+
+ def _import_and_verify(self, path, attr=42, directory=TESTDIR,
+ name=None, remove=None):
+ module = self._import(path, name, remove)
assert_equals(module.attr, attr)
assert_equals(module.func(), attr)
if hasattr(module, '__file__'):
assert_equals(dirname(abspath(module.__file__)), directory)
- def _import(self, path, name=None):
+ def _import(self, path, name=None, remove=None):
+ if remove and remove in sys.modules:
+ del sys.modules[remove]
self.logger = LoggerStub()
importer = Importer(name, self.logger)
sys_path_before = sys.path[:]
@@ -143,6 +156,14 @@
finally:
assert_equals(sys.path, sys_path_before)
+ def _assert_imported_message(self, name, source, type='module',
index=0):
+ msg = "Imported %s '%s' from '%s'." % (type, name, source)
+ self.logger.assert_message(msg, index=index)
+
+ def _assert_removed_message(self, name, index=0):
+ msg = "Removed module '%s' from sys.modules to import fresh
module." % name
+ self.logger.assert_message(msg, index=index)
+
class TestInvalidImportPath(unittest.TestCase):
@@ -307,7 +328,7 @@
return klass
def _import(self, name, type=None, logger=None):
- return Importer(type, logger).import_class_or_module(name)
+ return Importer(type, logger or
LoggerStub()).import_class_or_module(name)
class TestErrorDetails(unittest.TestCase):
@@ -376,5 +397,26 @@
yield line
+class TestSplitPathToModule(unittest.TestCase):
+
+ def _verify(self, file_name, expected_name):
+ path = abspath(file_name)
+ actual = ByPathImporter(None)._split_path_to_module(path)
+ assert_equals(actual, (dirname(path), expected_name))
+
+ def test_normal_file(self):
+ self._verify('hello.py', 'hello')
+ self._verify('hello.class', 'hello')
+ self._verify('hello.world.java', 'hello.world')
+
+ def test_jython_class_file(self):
+ self._verify('hello$py.class', 'hello')
+ self._verify('__init__$py.class', '__init__')
+
+ def test_directory(self):
+ self._verify('hello', 'hello')
+ self._verify('hello'+os.sep, 'hello')
+
+
if __name__ == '__main__':
unittest.main()
==============================================================================
Revision: 72654cec0803
Author: Pekka Klärck
Date: Tue Jan 3 14:41:00 2012
Log: cleanup and ooops fix
http://code.google.com/p/robotframework/source/detail?r=72654cec0803
Modified:
/src/robot/utils/importer.py
=======================================
--- /src/robot/utils/importer.py Tue Jan 3 14:32:16 2012
+++ /src/robot/utils/importer.py Tue Jan 3 14:41:00 2012
@@ -26,6 +26,7 @@
# TODO:
+# - don't import test library modules if library is in cache
# - test can variable files be implemented with java/python classes
nowadays
# (possibly returning class when importing by path is bwic anyway)
@@ -191,12 +192,16 @@
source = getattr(module, '__file__', None)
if not source: # play safe (occurs at least with java based
modules)
return True
+ imported_from, imported_package =
self._get_import_information(source)
+ return ((normpath(importing_from), importing_package) !=
+ (normpath(imported_from), imported_package))
+
+ def _get_import_information(self, source):
imported_from, imported_file = self._split_path_to_module(source)
imported_package = imported_file == '__init__'
- if importing_package:
+ if imported_package:
imported_from = os.path.dirname(imported_from)
- return (normpath(importing_from) != normpath(imported_from) or
- importing_package != imported_package)
+ return imported_from, imported_package
def _import_by_path(self, path):
module_dir, module_name = self._split_path_to_module(path)