Fixed #13464 -- Reworked module_has_submodule to break the requirement for loader and finder to be the same class. Thanks to Alex Gaynor for the report and patch, and Brett Cannon for suggesting the approach.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@13082 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
7fc25715d6
commit
4ca7c4e34d
|
@ -1,26 +1,60 @@
|
||||||
import os
|
|
||||||
import imp
|
import imp
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
def module_has_submodule(mod, submod_name):
|
|
||||||
# If the module was loaded from an egg, __loader__ will be set and
|
|
||||||
# its find_module must be used to search for submodules.
|
|
||||||
loader = getattr(mod, '__loader__', None)
|
|
||||||
if loader:
|
|
||||||
mod_path = "%s.%s" % (mod.__name__.rsplit('.',1)[-1], submod_name)
|
|
||||||
x = loader.find_module(mod_path)
|
|
||||||
if x is None:
|
|
||||||
# zipimport.zipimporter.find_module is documented to take
|
|
||||||
# dotted paths but in fact through Python 2.7 is observed
|
|
||||||
# to require os.sep in place of dots...so try using os.sep
|
|
||||||
# if the dotted path version failed to find the requested
|
|
||||||
# submodule.
|
|
||||||
x = loader.find_module(mod_path.replace('.', os.sep))
|
|
||||||
return x is not None
|
|
||||||
|
|
||||||
|
def module_has_submodule(package, module_name):
|
||||||
|
"""See if 'module' is in 'package'."""
|
||||||
|
name = ".".join([package.__name__, module_name])
|
||||||
|
if name in sys.modules:
|
||||||
|
return True
|
||||||
|
for finder in sys.meta_path:
|
||||||
|
if finder.find_module(name):
|
||||||
|
return True
|
||||||
|
for entry in package.__path__: # No __path__, then not a package.
|
||||||
try:
|
try:
|
||||||
imp.find_module(submod_name, mod.__path__)
|
# Try the cached finder.
|
||||||
|
finder = sys.path_importer_cache[entry]
|
||||||
|
if finder is None:
|
||||||
|
# Implicit import machinery should be used.
|
||||||
|
try:
|
||||||
|
file_, _, _ = imp.find_module(module_name, [entry])
|
||||||
|
if file_:
|
||||||
|
file_.close()
|
||||||
return True
|
return True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
continue
|
||||||
|
# Else see if the finder knows of a loader.
|
||||||
|
elif finder.find_module(name):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
except KeyError:
|
||||||
|
# No cached finder, so try and make one.
|
||||||
|
for hook in sys.path_hooks:
|
||||||
|
try:
|
||||||
|
finder = hook(entry)
|
||||||
|
# XXX Could cache in sys.path_importer_cache
|
||||||
|
if finder.find_module(name):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# Once a finder is found, stop the search.
|
||||||
|
break
|
||||||
|
except ImportError:
|
||||||
|
# Continue the search for a finder.
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# No finder found.
|
||||||
|
# Try the implicit import machinery if searching a directory.
|
||||||
|
if os.path.isdir(entry):
|
||||||
|
try:
|
||||||
|
file_, _, _ = imp.find_module(module_name, [entry])
|
||||||
|
if file_:
|
||||||
|
file_.close()
|
||||||
|
return True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
# XXX Could insert None or NullImporter
|
||||||
|
else:
|
||||||
|
# Exhausted the search, so the module cannot be found.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,139 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from unittest import TestCase
|
||||||
|
from zipimport import zipimporter
|
||||||
|
|
||||||
|
from django.utils.importlib import import_module
|
||||||
|
from django.utils.module_loading import module_has_submodule
|
||||||
|
|
||||||
|
class DefaultLoader(TestCase):
|
||||||
|
def test_loader(self):
|
||||||
|
"Normal module existence can be tested"
|
||||||
|
test_module = import_module('regressiontests.utils.test_module')
|
||||||
|
|
||||||
|
# An importable child
|
||||||
|
self.assertTrue(module_has_submodule(test_module, 'good_module'))
|
||||||
|
mod = import_module('regressiontests.utils.test_module.good_module')
|
||||||
|
self.assertEqual(mod.content, 'Good Module')
|
||||||
|
|
||||||
|
# A child that exists, but will generate an import error if loaded
|
||||||
|
self.assertTrue(module_has_submodule(test_module, 'bad_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'regressiontests.utils.test_module.bad_module')
|
||||||
|
|
||||||
|
# A child that doesn't exist
|
||||||
|
self.assertFalse(module_has_submodule(test_module, 'no_such_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'regressiontests.utils.test_module.no_such_module')
|
||||||
|
|
||||||
|
class EggLoader(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.old_path = sys.path
|
||||||
|
self.egg_dir = '%s/eggs' % os.path.dirname(__file__)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
sys.path = self.old_path
|
||||||
|
|
||||||
|
def test_shallow_loader(self):
|
||||||
|
"Module existence can be tested inside eggs"
|
||||||
|
egg_name = '%s/test_egg.egg' % self.egg_dir
|
||||||
|
sys.path.append(egg_name)
|
||||||
|
egg_module = import_module('egg_module')
|
||||||
|
|
||||||
|
# An importable child
|
||||||
|
self.assertTrue(module_has_submodule(egg_module, 'good_module'))
|
||||||
|
mod = import_module('egg_module.good_module')
|
||||||
|
self.assertEqual(mod.content, 'Good Module')
|
||||||
|
|
||||||
|
# A child that exists, but will generate an import error if loaded
|
||||||
|
self.assertTrue(module_has_submodule(egg_module, 'bad_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'egg_module.bad_module')
|
||||||
|
|
||||||
|
# A child that doesn't exist
|
||||||
|
self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'egg_module.no_such_module')
|
||||||
|
|
||||||
|
def test_deep_loader(self):
|
||||||
|
"Modules deep inside an egg can still be tested for existence"
|
||||||
|
egg_name = '%s/test_egg.egg' % self.egg_dir
|
||||||
|
sys.path.append(egg_name)
|
||||||
|
egg_module = import_module('egg_module.sub1.sub2')
|
||||||
|
|
||||||
|
# An importable child
|
||||||
|
self.assertTrue(module_has_submodule(egg_module, 'good_module'))
|
||||||
|
mod = import_module('egg_module.sub1.sub2.good_module')
|
||||||
|
self.assertEqual(mod.content, 'Deep Good Module')
|
||||||
|
|
||||||
|
# A child that exists, but will generate an import error if loaded
|
||||||
|
self.assertTrue(module_has_submodule(egg_module, 'bad_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.bad_module')
|
||||||
|
|
||||||
|
# A child that doesn't exist
|
||||||
|
self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.no_such_module')
|
||||||
|
|
||||||
|
class TestFinder(object):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.importer = zipimporter(*args, **kwargs)
|
||||||
|
|
||||||
|
def find_module(self, path):
|
||||||
|
importer = self.importer.find_module(path)
|
||||||
|
if importer is None:
|
||||||
|
return
|
||||||
|
return TestLoader(importer)
|
||||||
|
|
||||||
|
class TestLoader(object):
|
||||||
|
def __init__(self, importer):
|
||||||
|
self.importer = importer
|
||||||
|
|
||||||
|
def load_module(self, name):
|
||||||
|
mod = self.importer.load_module(name)
|
||||||
|
mod.__loader__ = self
|
||||||
|
return mod
|
||||||
|
|
||||||
|
class CustomLoader(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.egg_dir = '%s/eggs' % os.path.dirname(__file__)
|
||||||
|
self.old_path = sys.path
|
||||||
|
sys.path_hooks.insert(0, TestFinder)
|
||||||
|
sys.path_importer_cache.clear()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
sys.path = self.old_path
|
||||||
|
sys.path_hooks.pop(0)
|
||||||
|
|
||||||
|
def test_shallow_loader(self):
|
||||||
|
"Module existence can be tested with a custom loader"
|
||||||
|
egg_name = '%s/test_egg.egg' % self.egg_dir
|
||||||
|
sys.path.append(egg_name)
|
||||||
|
egg_module = import_module('egg_module')
|
||||||
|
|
||||||
|
# An importable child
|
||||||
|
self.assertTrue(module_has_submodule(egg_module, 'good_module'))
|
||||||
|
mod = import_module('egg_module.good_module')
|
||||||
|
self.assertEqual(mod.content, 'Good Module')
|
||||||
|
|
||||||
|
# A child that exists, but will generate an import error if loaded
|
||||||
|
self.assertTrue(module_has_submodule(egg_module, 'bad_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'egg_module.bad_module')
|
||||||
|
|
||||||
|
# A child that doesn't exist
|
||||||
|
self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'egg_module.no_such_module')
|
||||||
|
|
||||||
|
def test_deep_loader(self):
|
||||||
|
"Modules existence can be tested deep inside a custom loader"
|
||||||
|
egg_name = '%s/test_egg.egg' % self.egg_dir
|
||||||
|
sys.path.append(egg_name)
|
||||||
|
egg_module = import_module('egg_module.sub1.sub2')
|
||||||
|
|
||||||
|
# An importable child
|
||||||
|
self.assertTrue(module_has_submodule(egg_module, 'good_module'))
|
||||||
|
mod = import_module('egg_module.sub1.sub2.good_module')
|
||||||
|
self.assertEqual(mod.content, 'Deep Good Module')
|
||||||
|
|
||||||
|
# A child that exists, but will generate an import error if loaded
|
||||||
|
self.assertTrue(module_has_submodule(egg_module, 'bad_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.bad_module')
|
||||||
|
|
||||||
|
# A child that doesn't exist
|
||||||
|
self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
|
||||||
|
self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.no_such_module')
|
|
@ -0,0 +1,3 @@
|
||||||
|
import a_package_name_that_does_not_exist
|
||||||
|
|
||||||
|
content = 'Bad Module'
|
|
@ -0,0 +1 @@
|
||||||
|
content = 'Good Module'
|
|
@ -34,6 +34,7 @@ __test__ = {
|
||||||
|
|
||||||
from dateformat import *
|
from dateformat import *
|
||||||
from feedgenerator import *
|
from feedgenerator import *
|
||||||
|
from module_loading import *
|
||||||
from termcolors import *
|
from termcolors import *
|
||||||
|
|
||||||
class TestUtilsHtml(TestCase):
|
class TestUtilsHtml(TestCase):
|
||||||
|
|
Loading…
Reference in New Issue