Code

Ticket #13464: django-importer.diff

File django-importer.diff, 5.9 KB (added by Alex, 4 years ago)
Line 
1diff --git a/django/utils/module_loading.py b/django/utils/module_loading.py
2index 9bcdd27..f251035 100644
3--- a/django/utils/module_loading.py
4+++ b/django/utils/module_loading.py
5@@ -1,26 +1,60 @@
6-import os
7 import imp
8+import os
9+import sys
10 
11-def module_has_submodule(mod, submod_name):
12-    # If the module was loaded from an egg, __loader__ will be set and
13-    # its find_module must be used to search for submodules.
14-    loader = getattr(mod, '__loader__', None)
15-    if loader:
16-        mod_path = "%s.%s" % (mod.__name__.rsplit('.',1)[-1], submod_name)
17-        x = loader.find_module(mod_path)
18-        if x is None:
19-            # zipimport.zipimporter.find_module is documented to take
20-            # dotted paths but in fact through Python 2.7 is observed
21-            # to require os.sep in place of dots...so try using os.sep
22-            # if the dotted path version failed to find the requested
23-            # submodule.
24-            x = loader.find_module(mod_path.replace('.', os.sep))
25-        return x is not None
26 
27-    try:
28-        imp.find_module(submod_name, mod.__path__)
29+def module_has_submodule(package, module_name):
30+    """See if 'module' is in 'package'."""
31+    name = ".".join([package.__name__, module_name])
32+    if name in sys.modules:
33         return True
34-    except ImportError:
35+    for finder in sys.meta_path:
36+        if finder.find_module(name):
37+            return True
38+    for entry in package.__path__:  # No __path__, then not a package.
39+        try:
40+            # Try the cached finder.
41+            finder = sys.path_importer_cache[entry]
42+            if finder is None:
43+                # Implicit import machinery should be used.
44+                try:
45+                    file_, _, _ = imp.find_module(module_name, [entry])
46+                    if file_:
47+                        file_.close()
48+                    return True
49+                except ImportError:
50+                    continue
51+            # Else see if the finder knows of a loader.
52+            elif finder.find_module(name):
53+                return True
54+            else:
55+                continue
56+        except KeyError:
57+            # No cached finder, so try and make one.
58+            for hook in sys.path_hooks:
59+                try:
60+                    finder = hook(entry)
61+                    # XXX Could cache in sys.path_importer_cache
62+                    if finder.find_module(name):
63+                        return True
64+                    else:
65+                        # Once a finder is found, stop the search.
66+                        break
67+                except ImportError:
68+                    # Continue the search for a finder.
69+                    continue
70+            else:
71+                # No finder found.
72+                # Try the implicit import machinery if searching a directory.
73+                if os.path.isdir(entry):
74+                    try:
75+                        file_, _, _ = imp.find_module(module_name, [entry])
76+                        if file_:
77+                            file_.close()
78+                        return True
79+                    except ImportError:
80+                        pass
81+                # XXX Could insert None or NullImporter
82+    else:
83+        # Exhausted the search, so the module cannot be found.
84         return False
85-
86-
87diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
88index 5902e8d..cafb2f5 100644
89--- a/tests/regressiontests/templates/tests.py
90+++ b/tests/regressiontests/templates/tests.py
91@@ -1,19 +1,15 @@
92 # -*- coding: utf-8 -*-
93-from django.conf import settings
94-
95-if __name__ == '__main__':
96-    # When running this file in isolation, we need to set up the configuration
97-    # before importing 'template'.
98-    settings.configure()
99 
100-from datetime import datetime, timedelta
101-import time
102 import os
103 import sys
104+import time
105 import traceback
106 import unittest
107+from datetime import datetime, timedelta
108+from zipimport import zipimporter
109 
110 from django import template
111+from django.conf import settings
112 from django.core import urlresolvers
113 from django.template import loader
114 from django.template.loaders import app_directories, filesystem, cached
115@@ -1317,8 +1313,27 @@ class Templates(unittest.TestCase):
116         }
117 
118 
119-class TemplateTagLoading(unittest.TestCase):
120+class TestFinder(object):
121+    def __init__(self, *args, **kwargs):
122+        self.importer = zipimporter(*args, **kwargs)
123+   
124+    def find_module(self, path):
125+        importer = self.importer.find_module(path)
126+        if importer is None:
127+            return
128+        return TestLoader(importer)
129+
130+class TestLoader(object):
131+    def __init__(self, importer):
132+        self.importer = importer
133+   
134+    def load_module(self, name):
135+        mod = self.importer.load_module(name)
136+        mod.__loader__ = self
137+        return mod
138 
139+
140+class TemplateTagLoading(unittest.TestCase):
141     def setUp(self):
142         self.old_path = sys.path
143         self.old_apps = settings.INSTALLED_APPS
144@@ -1351,6 +1366,23 @@ class TemplateTagLoading(unittest.TestCase):
145         except template.TemplateSyntaxError, e:
146             self.assertTrue('ImportError' in e.args[0])
147             self.assertTrue('Xtemplate' in e.args[0])
148+   
149+    def test_custom_loader_error(self):
150+        sys.path_hooks.insert(0, TestFinder)
151+        sys.path_importer_cache.clear()
152+
153+        ttext = "{% load broken_egg %}"
154+        egg_name = '%s/tagsegg.egg' % self.egg_dir
155+        sys.path.append(egg_name)
156+        settings.INSTALLED_APPS = ('tagsegg',)
157+
158+        self.assertRaises(template.TemplateSyntaxError, template.Template, ttext)
159+        try:
160+            template.Template(ttext)
161+        except template.TemplateSyntaxError, e:
162+            self.assertTrue('ImportError' in e.args[0])
163+            self.assertTrue('Xtemplate' in e.args[0])
164+        sys.path_hooks.pop(0)
165 
166     def test_load_working_egg(self):
167         ttext = "{% load working_egg %}"