Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 11 commits
  • 52 files changed
  • 0 comments
  • 1 contributor

Showing 52 changed files with 663 additions and 335 deletions. Show diff stats Hide diff stats

  1. 0  app1/__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/__init__.py
  2. 0  ...anagement/__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/__init__.py
  3. 0  .../__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/__init__.py
  4. 0  app1/models.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/models.py
  5. 0  app2/__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/__init__.py
  6. 0  ...anagement/__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/__init__.py
  7. 0  .../__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/__init__.py
  8. 0  app2/models.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/models.py
  9. 175  django/contrib/staticfiles/finders.py
  10. 4  django/contrib/staticfiles/management/commands/findstatic.py
  11. 15  django/contrib/staticfiles/views.py
  12. 128  django/core/files/storage.py
  13. 61  django/core/management/__init__.py
  14. 91  django/core/management/commands/loaddata.py
  15. 12  django/db/utils.py
  16. 34  django/template/loader.py
  17. 66  django/template/loaders/app_directories.py
  18. 13  django/template/loaders/cached.py
  19. 35  django/template/loaders/eggs.py
  20. 17  django/template/loaders/filesystem.py
  21. 87  django/utils/importlib.py
  22. 37  django/utils/translation/trans_real.py
  23. 4  docs/ref/templates/api.txt
  24. 0  tests/regressiontests/admin_scripts/lib1/nons_app/__init__.py
  25. 0  tests/regressiontests/admin_scripts/lib1/nons_app/management/__init__.py
  26. 0  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/__init__.py
  27. 9  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/nons_app_command1.py
  28. 0  tests/regressiontests/admin_scripts/lib1/nons_app/models.py
  29. 0  tests/regressiontests/admin_scripts/lib1/npapp/__init__.py
  30. 0  tests/regressiontests/admin_scripts/lib1/npapp/management.py
  31. 0  tests/regressiontests/admin_scripts/lib1/npapp/models.py
  32. 6  tests/regressiontests/admin_scripts/lib1/nsapps/__init__.py
  33. 6  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/__init__.py
  34. 9  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/app1_command1.py
  35. 6  tests/regressiontests/admin_scripts/lib2/nsapps/__init__.py
  36. 6  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/__init__.py
  37. 9  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/app2_command1.py
  38. 1  tests/regressiontests/admin_scripts/lib3/_addsitedir.py
  39. 1  tests/regressiontests/admin_scripts/lib3/egg_module.pth
  40. 1  tests/regressiontests/admin_scripts/lib3/exapps-nspkg.pth
  41. 0  tests/regressiontests/admin_scripts/lib3/exapps/app3/__init__.py
  42. 0  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/__init__.py
  43. 0  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/commands/__init__.py
  44. 9  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/commands/app3_command1.py
  45. 0  tests/regressiontests/admin_scripts/lib3/exapps/app3/models.py
  46. BIN  tests/regressiontests/admin_scripts/lib3/test_egg.egg
  47. 100  tests/regressiontests/admin_scripts/tests.py
  48. BIN  tests/regressiontests/i18n/eggs/localeegg.egg
  49. 28  tests/regressiontests/i18n/tests.py
  50. 6  tests/regressiontests/staticfiles_tests/tests.py
  51. 6  tests/regressiontests/templates/loaders.py
  52. 16  tests/regressiontests/templates/tests.py
0  app1/__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/__init__.py
No changes.
0  ...gement/__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/__init__.py
No changes.
0  ...init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/__init__.py
No changes.
0  app1/models.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/models.py
No changes.
0  app2/__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/__init__.py
No changes.
0  ...gement/__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/__init__.py
No changes.
0  ...init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/__init__.py
No changes.
0  app2/models.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/models.py
No changes.
175  django/contrib/staticfiles/finders.py
... ...
@@ -1,7 +1,7 @@
1 1
 import os
2 2
 from django.conf import settings
3 3
 from django.core.exceptions import ImproperlyConfigured
4  
-from django.core.files.storage import default_storage, Storage, FileSystemStorage
  4
+from django.core.files.storage import default_storage, Storage, FileSystemStorage, AppDirectoryStorage
5 5
 from django.utils.datastructures import SortedDict
6 6
 from django.utils.functional import empty, memoize, LazyObject
7 7
 from django.utils.importlib import import_module
@@ -38,16 +38,56 @@ def list(self, ignore_patterns):
38 38
         raise NotImplementedError()
39 39
 
40 40
 
41  
-class FileSystemFinder(BaseFinder):
  41
+class BaseStoragesFinder(BaseFinder):
  42
+
  43
+    def __init__(self):
  44
+        self.storages = SortedDict()
  45
+
  46
+    def _find_location(self, storage, path):
  47
+        if getattr(storage, 'prefix', False):
  48
+            prefix = '%s%s' % (storage.prefix, os.sep)
  49
+            if not path.startswith(prefix):
  50
+                return None
  51
+
  52
+            path = path[len(prefix):]
  53
+
  54
+        if storage.exists(path):
  55
+            return path
  56
+
  57
+    def find(self, path, all=False):
  58
+        """
  59
+        Looks for files in storages.
  60
+        """
  61
+        matches = []
  62
+        for storage in self.storages.itervalues():
  63
+            matched_path = self._find_location(storage, path)
  64
+
  65
+            if matched_path:
  66
+                if not all:
  67
+                    return (storage, matched_path)
  68
+                matches.append((storage, matched_path))
  69
+
  70
+        return matches
  71
+
  72
+    def list(self, ignore_patterns):
  73
+        """
  74
+        List all files in all storages.
  75
+        """
  76
+        for storage in self.storages.itervalues():
  77
+            if storage.exists(''):
  78
+                for path in utils.get_files(storage, ignore_patterns):
  79
+                    yield path, storage
  80
+
  81
+
  82
+class FileSystemFinder(BaseStoragesFinder):
42 83
     """
43 84
     A static files finder that uses the ``STATICFILES_DIRS`` setting
44 85
     to locate files.
45 86
     """
46  
-    def __init__(self, apps=None, *args, **kwargs):
47  
-        # List of locations with static files
48  
-        self.locations = []
49  
-        # Maps dir paths to an appropriate storage instance
50  
-        self.storages = SortedDict()
  87
+    def __init__(self):
  88
+        super(FileSystemFinder, self).__init__()
  89
+        locations = []
  90
+
51 91
         if not isinstance(settings.STATICFILES_DIRS, (list, tuple)):
52 92
             raise ImproperlyConfigured(
53 93
                 "Your STATICFILES_DIRS setting is not a tuple or list; "
@@ -61,112 +101,27 @@ def __init__(self, apps=None, *args, **kwargs):
61 101
                 raise ImproperlyConfigured(
62 102
                     "The STATICFILES_DIRS setting should "
63 103
                     "not contain the STATIC_ROOT setting")
64  
-            if (prefix, root) not in self.locations:
65  
-                self.locations.append((prefix, root))
66  
-        for prefix, root in self.locations:
  104
+            if (prefix, root) not in locations:
  105
+                locations.append((prefix, root))
  106
+        for prefix, root in locations:
67 107
             filesystem_storage = FileSystemStorage(location=root)
68 108
             filesystem_storage.prefix = prefix
69 109
             self.storages[root] = filesystem_storage
70  
-        super(FileSystemFinder, self).__init__(*args, **kwargs)
71 110
 
72  
-    def find(self, path, all=False):
73  
-        """
74  
-        Looks for files in the extra locations
75  
-        as defined in ``STATICFILES_DIRS``.
76  
-        """
77  
-        matches = []
78  
-        for prefix, root in self.locations:
79  
-            matched_path = self.find_location(root, path, prefix)
80  
-            if matched_path:
81  
-                if not all:
82  
-                    return matched_path
83  
-                matches.append(matched_path)
84  
-        return matches
85  
-
86  
-    def find_location(self, root, path, prefix=None):
87  
-        """
88  
-        Finds a requested static file in a location, returning the found
89  
-        absolute path (or ``None`` if no match).
90  
-        """
91  
-        if prefix:
92  
-            prefix = '%s%s' % (prefix, os.sep)
93  
-            if not path.startswith(prefix):
94  
-                return None
95  
-            path = path[len(prefix):]
96  
-        path = safe_join(root, path)
97  
-        if os.path.exists(path):
98  
-            return path
99  
-
100  
-    def list(self, ignore_patterns):
101  
-        """
102  
-        List all files in all locations.
103  
-        """
104  
-        for prefix, root in self.locations:
105  
-            storage = self.storages[root]
106  
-            for path in utils.get_files(storage, ignore_patterns):
107  
-                yield path, storage
108 111
 
109  
-
110  
-class AppDirectoriesFinder(BaseFinder):
  112
+class AppDirectoriesFinder(BaseStoragesFinder):
111 113
     """
112 114
     A static files finder that looks in the directory of each app as
113 115
     specified in the source_dir attribute of the given storage class.
114 116
     """
115  
-    storage_class = AppStaticStorage
  117
+    def __init__(self, apps=None):
  118
+        super(AppDirectoriesFinder, self).__init__()
116 119
 
117  
-    def __init__(self, apps=None, *args, **kwargs):
118  
-        # The list of apps that are handled
119  
-        self.apps = []
120  
-        # Mapping of app module paths to storage instances
121  
-        self.storages = SortedDict()
122 120
         if apps is None:
123 121
             apps = settings.INSTALLED_APPS
124 122
         for app in apps:
125  
-            app_storage = self.storage_class(app)
126  
-            if os.path.isdir(app_storage.location):
127  
-                self.storages[app] = app_storage
128  
-                if app not in self.apps:
129  
-                    self.apps.append(app)
130  
-        super(AppDirectoriesFinder, self).__init__(*args, **kwargs)
131  
-
132  
-    def list(self, ignore_patterns):
133  
-        """
134  
-        List all files in all app storages.
135  
-        """
136  
-        for storage in six.itervalues(self.storages):
137  
-            if storage.exists(''):  # check if storage location exists
138  
-                for path in utils.get_files(storage, ignore_patterns):
139  
-                    yield path, storage
140  
-
141  
-    def find(self, path, all=False):
142  
-        """
143  
-        Looks for files in the app directories.
144  
-        """
145  
-        matches = []
146  
-        for app in self.apps:
147  
-            match = self.find_in_app(app, path)
148  
-            if match:
149  
-                if not all:
150  
-                    return match
151  
-                matches.append(match)
152  
-        return matches
153  
-
154  
-    def find_in_app(self, app, path):
155  
-        """
156  
-        Find a requested static file in an app's static locations.
157  
-        """
158  
-        storage = self.storages.get(app, None)
159  
-        if storage:
160  
-            if storage.prefix:
161  
-                prefix = '%s%s' % (storage.prefix, os.sep)
162  
-                if not path.startswith(prefix):
163  
-                    return None
164  
-                path = path[len(prefix):]
165  
-            # only try to find a file if the source dir actually exists
166  
-            if storage.exists(path):
167  
-                matched_path = storage.path(path)
168  
-                if matched_path:
169  
-                    return matched_path
  123
+            app_storage = AppDirectoryStorage(app, 'static')
  124
+            self.storages[app] = app_storage
170 125
 
171 126
 
172 127
 class BaseStorageFinder(BaseFinder):
@@ -192,16 +147,11 @@ def find(self, path, all=False):
192 147
         """
193 148
         Looks for files in the default file storage, if it's local.
194 149
         """
195  
-        try:
196  
-            self.storage.path('')
197  
-        except NotImplementedError:
198  
-            pass
199  
-        else:
200  
-            if self.storage.exists(path):
201  
-                match = self.storage.path(path)
202  
-                if all:
203  
-                    match = [match]
204  
-                return match
  150
+        if self.storage.exists(path):
  151
+            match = (self.storage, path)
  152
+            if all:
  153
+                match = [match]
  154
+            return match
205 155
         return []
206 156
 
207 157
     def list(self, ignore_patterns):
@@ -239,8 +189,9 @@ def find(path, all=False):
239 189
         result = finder.find(path, all=all)
240 190
         if not all and result:
241 191
             return result
242  
-        if not isinstance(result, (list, tuple)):
243  
-            result = [result]
  192
+        # if not isinstance(result, (list, tuple)):
  193
+        #     result = [result]
  194
+        assert isinstance(result, list)
244 195
         matches.extend(result)
245 196
     if matches:
246 197
         return matches
4  django/contrib/staticfiles/management/commands/findstatic.py
@@ -21,10 +21,10 @@ def handle_label(self, path, **options):
21 21
         result = finders.find(path, all=options['all'])
22 22
         path = smart_text(path)
23 23
         if result:
24  
-            if not isinstance(result, (list, tuple)):
  24
+            if not isinstance(result, list):
25 25
                 result = [result]
26 26
             output = '\n  '.join(
27  
-                (smart_text(os.path.realpath(path)) for path in result))
  27
+                (smart_text(os.path.realpath(storage.path(p))) for storage, p in result))
28 28
             self.stdout.write("Found '%s' here:\n  %s" % (path, output))
29 29
         else:
30 30
             if verbosity >= 1:
15  django/contrib/staticfiles/views.py
@@ -12,7 +12,7 @@
12 12
 
13 13
 from django.conf import settings
14 14
 from django.core.exceptions import ImproperlyConfigured
15  
-from django.http import Http404
  15
+from django.http import HttpResponse, Http404
16 16
 from django.views import static
17 17
 
18 18
 from django.contrib.staticfiles import finders
@@ -35,10 +35,11 @@ def serve(request, path, document_root=None, insecure=False, **kwargs):
35 35
                                    "debug mode or if the the --insecure "
36 36
                                    "option of 'runserver' is used")
37 37
     normalized_path = posixpath.normpath(unquote(path)).lstrip('/')
38  
-    absolute_path = finders.find(normalized_path)
39  
-    if not absolute_path:
40  
-        if path.endswith('/') or path == '':
41  
-            raise Http404("Directory indexes are not allowed here.")
  38
+    storage_path = finders.find(normalized_path)
  39
+    if storage_path is None:
42 40
         raise Http404("'%s' could not be found" % path)
43  
-    document_root, path = os.path.split(absolute_path)
44  
-    return static.serve(request, path, document_root=document_root, **kwargs)
  41
+    storage, relative_path = storage_path
  42
+    if storage.isdir(relative_path):
  43
+        raise Http404("Directory indexes are not allowed here.")
  44
+    return HttpResponse(storage.open(relative_path))
  45
+
128  django/core/files/storage.py
@@ -4,8 +4,21 @@
4 4
     from urllib.parse import urljoin
5 5
 except ImportError:     # Python 2
6 6
     from urlparse import urljoin
  7
+import pkgutil
7 8
 import itertools
8 9
 from datetime import datetime
  10
+try:
  11
+    import pkg_resources
  12
+except ImportError:
  13
+    pkg_resources = None
  14
+try:
  15
+    from cStringIO import StringIO
  16
+except ImportError:
  17
+    from StringIO import StringIO
  18
+try:
  19
+    import zipimport
  20
+except ImportError:
  21
+    zipimport = None
9 22
 
10 23
 from django.conf import settings
11 24
 from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
@@ -100,6 +113,12 @@ def exists(self, name):
100 113
         """
101 114
         raise NotImplementedError()
102 115
 
  116
+    def isdir(self, name):
  117
+        """
  118
+        Returns True if path is an existing directory in the storage system.
  119
+        """
  120
+        raise NotImplementedError()
  121
+
103 122
     def listdir(self, path):
104 123
         """
105 124
         Lists the contents of the specified path, returning a 2-tuple of lists;
@@ -242,6 +261,9 @@ def delete(self, name):
242 261
     def exists(self, name):
243 262
         return os.path.exists(self.path(name))
244 263
 
  264
+    def isdir(self, name):
  265
+        return os.path.isdir(self.path(name))
  266
+
245 267
     def listdir(self, path):
246 268
         path = self.path(path)
247 269
         directories, files = [], []
@@ -276,6 +298,112 @@ def created_time(self, name):
276 298
     def modified_time(self, name):
277 299
         return datetime.fromtimestamp(os.path.getmtime(self.path(name)))
278 300
 
  301
+class _PkgResourcesAppDirectoryStorage(Storage):
  302
+
  303
+    def __init__(self, app, path):
  304
+        self.app = app
  305
+        self.path = '/' + path.lstrip('/')
  306
+
  307
+    def _path(self, name):
  308
+        return safe_join(self.path, name.lstrip('/'))[1:]
  309
+
  310
+    def _open(self, name, mode):
  311
+        return File(pkg_resources.resource_stream(self.app, self._path(name)), name)
  312
+
  313
+    def exists(self, name):
  314
+        return pkg_resources.resource_exists(self.app, self._path(name))
  315
+
  316
+    def isdir(self, name):
  317
+        return pkg_resources.resource_isdir(self.app, self._path(name))
  318
+
  319
+    def listdir(self, path):
  320
+        return pkg_resources.resource_listdir(self.app, self._path(name))
  321
+
  322
+class _PEP302AppDirectoryStorage(Storage):
  323
+
  324
+    def __init__(self, app, path):
  325
+        self.app = app
  326
+        self.path = '/' + path.lstrip('/')
  327
+
  328
+    def _path(self, name):
  329
+        return safe_join(self.path, name.lstrip('/'))[1:]
  330
+
  331
+    def _open(self, name, mode):
  332
+        data = pkgutils.get_data(self.app, self._path(name))
  333
+        if data is not None:
  334
+            return File(StringIO(data), name)
  335
+        raise IOError
  336
+
  337
+    def isdir(self, name):
  338
+        return True
  339
+
  340
+    def exists(self, name):
  341
+        return True
  342
+
  343
+    def listdir(self, path):
  344
+        return []
  345
+
  346
+class _ZipAppDirectoryStorage(Storage):
  347
+
  348
+    def __init__(self, app, loader, path):
  349
+        self.app = app
  350
+        self.loader = loader
  351
+        self.path = path
  352
+        if self.loader.prefix:
  353
+            self.prefix = '/' + self.loader.prefix.strip('/') + '/' + self.app.replace('.', '/')
  354
+        else:
  355
+            self.prefix = '/' + self.app.replace('.', '/')
  356
+        self.files = zipimport._zip_directory_cache[self.loader.archive]
  357
+
  358
+    def _path(self, name):
  359
+        return safe_join(self.prefix , self.path.lstrip('/'), name.lstrip('/'))[1:]
  360
+
  361
+    def _open(self, name, mode):
  362
+        return File(StringIO(self.loader.get_data(self._path(name))), name)
  363
+
  364
+    def isdir(self, name):
  365
+        return self._path(name)+'/' in self.files
  366
+
  367
+    def exists(self, name):
  368
+        if self.isdir(name.rstrip('/')):
  369
+            return True
  370
+
  371
+        return self._path(name) in self.files
  372
+
  373
+    def listdir(self, path):
  374
+        if not isdir(path):
  375
+            raise OSError
  376
+        path = self._path(path)
  377
+
  378
+        return [ name for name in self.files 
  379
+                 if name.startswith(path) and 
  380
+                 '/' not in name[len(path):].rstrip('/') ]
  381
+
  382
+class AppDirectoryStorage(LazyObject):
  383
+
  384
+    def __init__(self, app, path):
  385
+        super(AppDirectoryStorage, self).__init__()
  386
+        self.__dict__['_app'] = app
  387
+        self.__dict__['_path'] = path
  388
+
  389
+    def _setup(self):
  390
+        mod = import_module(self._app)
  391
+        loader = pkgutil.get_loader(self._app)
  392
+        if isinstance(loader, pkgutil.ImpLoader):
  393
+            self._wrapped = FileSystemStorage(safe_join(mod.__path__[0], self._path))
  394
+            return
  395
+
  396
+        if pkg_resources:
  397
+            self._wrapped = _PkgResourcesAppDirectoryStorage(self._app, self._path)
  398
+            return
  399
+
  400
+        if zipimport:
  401
+            if isinstance(loader, zipimport.zipimporter):
  402
+                self._wrapped = _ZipAppDirectoryStorage(self._app, loader, self._path)
  403
+                return
  404
+
  405
+        self._wrapped = _PEP302AppDirectoryStorage(self._app, self._path)
  406
+
279 407
 def get_storage_class(import_path=None):
280 408
     if import_path is None:
281 409
         import_path = settings.DEFAULT_FILE_STORAGE
61  django/core/management/__init__.py
@@ -3,12 +3,13 @@
3 3
 import sys
4 4
 from optparse import OptionParser, NO_DEFAULT
5 5
 import imp
  6
+import pkgutil
6 7
 import warnings
7 8
 
8 9
 from django.core.exceptions import ImproperlyConfigured
9 10
 from django.core.management.base import BaseCommand, CommandError, handle_default_options
10 11
 from django.core.management.color import color_style
11  
-from django.utils.importlib import import_module
  12
+from django.utils.importlib import import_module, find_package_path
12 13
 from django.utils import six
13 14
 
14 15
 # For backwards compatibility: get_version() used to be in this module.
@@ -25,11 +26,11 @@ def find_commands(management_dir):
25 26
 
26 27
     Returns an empty list if no commands are defined.
27 28
     """
28  
-    command_dir = os.path.join(management_dir, 'commands')
29 29
     try:
30  
-        return [f[:-3] for f in os.listdir(command_dir)
31  
-                if not f.startswith('_') and f.endswith('.py')]
32  
-    except OSError:
  30
+        commands_dir = find_package_path('commands', [management_dir])[0]
  31
+        return [name for loader,name,ispkg in pkgutil.iter_modules([commands_dir])
  32
+                if not name.startswith('_') ]
  33
+    except ImportError:
33 34
         return []
34 35
 
35 36
 def find_management_module(app_name):
@@ -41,31 +42,39 @@ def find_management_module(app_name):
41 42
     """
42 43
     parts = app_name.split('.')
43 44
     parts.append('management')
44  
-    parts.reverse()
45  
-    part = parts.pop()
46  
-    path = None
47  
-
48  
-    # When using manage.py, the project module is added to the path,
49  
-    # loaded, then removed from the path. This means that
50  
-    # testproject.testapp.models can be loaded in future, even if
51  
-    # testproject isn't in the path. When looking for the management
52  
-    # module, we need look for the case where the project name is part
53  
-    # of the app_name but the project directory itself isn't on the path.
54  
-    try:
55  
-        f, path, descr = imp.find_module(part, path)
56  
-    except ImportError as e:
57  
-        if os.path.basename(os.getcwd()) != part:
58  
-            raise e
  45
+
  46
+    for i in range(len(parts), 0, -1):
  47
+        try:
  48
+            path = sys.modules['.'.join(parts[:i])].__path__
  49
+        except AttributeError:
  50
+            raise ImportError("No package named %s" % parts[i-1])
  51
+        except KeyError:
  52
+            continue
  53
+
  54
+        parts = parts[i:]
  55
+        parts.reverse()
  56
+        break
59 57
     else:
60  
-        if f:
61  
-            f.close()
  58
+        parts.reverse()
  59
+        part = parts.pop()
  60
+        path = None
  61
+
  62
+        # When using manage.py, the project module is added to the path,
  63
+        # loaded, then removed from the path. This means that
  64
+        # testproject.testapp.models can be loaded in future, even if
  65
+        # testproject isn't in the path. When looking for the management
  66
+        # module, we need look for the case where the project name is part
  67
+        # of the app_name but the project directory itself isn't on the path.
  68
+        try:
  69
+            path = find_package_path(part, path)
  70
+        except ImportError as e:
  71
+            if os.path.basename(os.getcwd()) != part:
  72
+                raise e
62 73
 
63 74
     while parts:
64 75
         part = parts.pop()
65  
-        f, path, descr = imp.find_module(part, path and [path] or None)
66  
-        if f:
67  
-            f.close()
68  
-    return path
  76
+        path = find_package_path(part, path)
  77
+    return path[0]
69 78
 
70 79
 def load_command_class(app_name, name):
71 80
     """
91  django/core/management/commands/loaddata.py
@@ -7,6 +7,7 @@
7 7
 
8 8
 from django.conf import settings
9 9
 from django.core import serializers
  10
+from django.core.files.storage import FileSystemStorage, AppDirectoryStorage
10 11
 from django.core.management.base import BaseCommand, CommandError
11 12
 from django.core.management.color import no_style
12 13
 from django.db import (connections, router, transaction, DEFAULT_DB_ALIAS,
@@ -77,37 +78,55 @@ def handle(self, *fixture_labels, **options):
77 78
             transaction.managed(True, using=self.using)
78 79
 
79 80
         class SingleZipReader(zipfile.ZipFile):
80  
-            def __init__(self, *args, **kwargs):
81  
-                zipfile.ZipFile.__init__(self, *args, **kwargs)
  81
+            def __init__(self, storage, path, mode, *args, **kwargs):
  82
+                fileobj = storage.open(path, mode)
  83
+                zipfile.ZipFile.__init__(self, fileobj, *args, **kwargs)
82 84
                 if settings.DEBUG:
83 85
                     assert len(self.namelist()) == 1, "Zip-compressed fixtures must contain only one file."
84 86
             def read(self):
85 87
                 return zipfile.ZipFile.read(self, self.namelist()[0])
86 88
 
  89
+        class GzipFileReader(gzip.GzipFile):
  90
+            def __init__(self, storage, path, mode, *args, **kwargs):
  91
+                fileobj = storage.open(path, mode)
  92
+                gzip.GzipFile.__init__(self, fileobj, *args, **kwargs)
  93
+
  94
+        class _BZ2FileReader(object):
  95
+            def __init__(self, storage, path, mode):
  96
+                self.fileobj = storage.open(path, mode)
  97
+
  98
+            def read(self):
  99
+                return bz2.BZ2Decompressor(self.fileobj.read())
  100
+
  101
+            def close(self):
  102
+                self.fileobj.close()
  103
+
  104
+        def BZ2FileReader(storage, path, mode):
  105
+            try:
  106
+                absolute_path = storage.path(path)
  107
+                return bz2.BZ2File(absolute_path, mode=mode)
  108
+            except NotImplementedError:
  109
+                return _BZ2FileReader(storage, path, mode)
  110
+            
  111
+
87 112
         self.compression_types = {
88  
-            None:   open,
89  
-            'gz':   gzip.GzipFile,
  113
+            None:   lambda storage, path, mode: storage.open(path, mode),
  114
+            'gz':   GzipFileReader,
90 115
             'zip':  SingleZipReader
91 116
         }
92 117
         if has_bz2:
93  
-            self.compression_types['bz2'] = bz2.BZ2File
  118
+            self.compression_types['bz2'] = BZ2FileReader
94 119
 
95  
-        app_module_paths = []
  120
+        app_fixture_storages = []
96 121
         for app in get_apps():
97  
-            if hasattr(app, '__path__'):
98  
-                # It's a 'models/' subpackage
99  
-                for path in app.__path__:
100  
-                    app_module_paths.append(path)
101  
-            else:
102  
-                # It's a models.py module
103  
-                app_module_paths.append(app.__file__)
104  
-
105  
-        app_fixtures = [os.path.join(os.path.dirname(path), 'fixtures') for path in app_module_paths]
  122
+            storage = AppDirectoryStorage(app.__name__[:-7], 'fixtures')
  123
+            if storage.isdir(''):
  124
+                app_fixture_storages.append(storage)
106 125
 
107 126
         try:
108 127
             with connection.constraint_checks_disabled():
109 128
                 for fixture_label in fixture_labels:
110  
-                    self.load_label(fixture_label, app_fixtures)
  129
+                    self.load_label(fixture_label, app_fixture_storages)
111 130
 
112 131
             # Since we disabled constraint checks, we must manually check for
113 132
             # any invalid keys that might have been added
@@ -155,7 +174,7 @@ def read(self):
155 174
         if commit:
156 175
             connection.close()
157 176
 
158  
-    def load_label(self, fixture_label, app_fixtures):
  177
+    def load_label(self, fixture_label, app_fixture_storages):
159 178
 
160 179
         parts = fixture_label.split('.')
161 180
 
@@ -184,21 +203,22 @@ def load_label(self, fixture_label, app_fixtures):
184 203
                     (fixture_name, format))
185 204
 
186 205
         if os.path.isabs(fixture_name):
187  
-            fixture_dirs = [fixture_name]
  206
+            fixture_storages = [FileSystemStorage(fixture_name)]
188 207
         else:
189  
-            fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + ['']
  208
+            fixture_storages = app_fixture_storages + list(map(FileSystemStorage, settings.FIXTURE_DIRS)) + [FileSystemStorage('')]
190 209
 
191  
-        for fixture_dir in fixture_dirs:
192  
-            self.process_dir(fixture_dir, fixture_name, compression_formats,
  210
+        for fixture_storage in fixture_storages:
  211
+            self.process_storage(fixture_storage, fixture_name, compression_formats,
193 212
                              formats)
194 213
 
195  
-    def process_dir(self, fixture_dir, fixture_name, compression_formats,
  214
+    def process_storage(self, fixture_storage, fixture_name, compression_formats,
196 215
                     serialization_formats):
197 216
 
198  
-        humanize = lambda dirname: "'%s'" % dirname if dirname else 'absolute path'
  217
+        # humanize = lambda dirname: "'%s'" % dirname if dirname else 'absolute path'
199 218
 
200 219
         if self.verbosity >= 2:
201  
-            self.stdout.write("Checking %s for fixtures..." % humanize(fixture_dir))
  220
+            # self.stdout.write("Checking %s for fixtures..." % humanize(fixture_dir))
  221
+            self.stdout.write("Checking %s for fixtures..." % str(fixture_storage))
202 222
 
203 223
         label_found = False
204 224
         for combo in product([self.using, None], serialization_formats, compression_formats):
@@ -211,28 +231,36 @@ def process_dir(self, fixture_dir, fixture_name, compression_formats,
211 231
             )
212 232
 
213 233
             if self.verbosity >= 3:
  234
+                # self.stdout.write("Trying %s for %s fixture '%s'..." % \
  235
+                #     (humanize(fixture_dir), file_name, fixture_name))
214 236
                 self.stdout.write("Trying %s for %s fixture '%s'..." % \
215  
-                    (humanize(fixture_dir), file_name, fixture_name))
216  
-            full_path = os.path.join(fixture_dir, file_name)
  237
+                    (str(fixture_storage), file_name, fixture_name))
  238
+
217 239
             open_method = self.compression_types[compression_format]
218 240
             try:
219  
-                fixture = open_method(full_path, 'r')
  241
+                fixture = open_method(fixture_storage, file_name, 'r')
220 242
             except IOError:
221 243
                 if self.verbosity >= 2:
  244
+                    # self.stdout.write("No %s fixture '%s' in %s." % \
  245
+                    #    (format, fixture_name, humanize(fixture_dir)))
222 246
                     self.stdout.write("No %s fixture '%s' in %s." % \
223  
-                        (format, fixture_name, humanize(fixture_dir)))
  247
+                        (format, fixture_name, str(fixture_storage)))
224 248
             else:
225 249
                 try:
226 250
                     if label_found:
  251
+                        # raise CommandError("Multiple fixtures named '%s' in %s. Aborting." %
  252
+                        #    (fixture_name, humanize(fixture_dir)))
227 253
                         raise CommandError("Multiple fixtures named '%s' in %s. Aborting." %
228  
-                            (fixture_name, humanize(fixture_dir)))
  254
+                            (fixture_name, str(fixture_storage)))
229 255
 
230 256
                     self.fixture_count += 1
231 257
                     objects_in_fixture = 0
232 258
                     loaded_objects_in_fixture = 0
233 259
                     if self.verbosity >= 2:
  260
+                        # self.stdout.write("Installing %s fixture '%s' from %s." % \
  261
+                        #    (format, fixture_name, humanize(fixture_dir)))
234 262
                         self.stdout.write("Installing %s fixture '%s' from %s." % \
235  
-                            (format, fixture_name, humanize(fixture_dir)))
  263
+                            (format, fixture_name, str(fixture_storage)))
236 264
 
237 265
                     objects = serializers.deserialize(format, fixture, using=self.using, ignorenonexistent=self.ignore)
238 266
 
@@ -257,7 +285,8 @@ def process_dir(self, fixture_dir, fixture_name, compression_formats,
257 285
                     label_found = True
258 286
                 except Exception as e:
259 287
                     if not isinstance(e, CommandError):
260  
-                        e.args = ("Problem installing fixture '%s': %s" % (full_path, e),)
  288
+                        # e.args = ("Problem installing fixture '%s': %s" % (full_path, e),)
  289
+                        e.args = ("Problem installing fixture '%s': %s" % (str(fixture_storage)+file_name, e),)
261 290
                     raise
262 291
                 finally:
263 292
                     fixture.close()
12  django/db/utils.py
... ...
@@ -1,6 +1,7 @@
1 1
 import os
2 2
 import pkgutil
3 3
 from threading import local
  4
+import pkgutil
4 5
 
5 6
 from django.conf import settings
6 7
 from django.core.exceptions import ImproperlyConfigured
@@ -27,13 +28,10 @@ def load_backend(backend_name):
27 28
     except ImportError as e_user:
28 29
         # The database backend wasn't found. Display a helpful error message
29 30
         # listing all possible (built-in) database backends.
30  
-        backend_dir = os.path.join(os.path.dirname(__file__), 'backends')
31  
-        try:
32  
-            builtin_backends = [
33  
-                name for _, name, ispkg in pkgutil.iter_modules([backend_dir])
34  
-                if ispkg and name != 'dummy']
35  
-        except EnvironmentError:
36  
-            builtin_backends = []
  31
+        backend_dir = import_module('django.db.backends').__path__
  32
+        builtin_backends = [
  33
+            name for _, name, ispkg in pkgutil.iter_modules([backend_dir])
  34
+            if ispkg and name != 'dummy']
37 35
         if backend_name not in ['django.db.backends.%s' % b for b in
38 36
                                 builtin_backends]:
39 37
             backend_reprs = map(repr, sorted(builtin_backends))
34  django/template/loader.py
@@ -3,10 +3,9 @@
3 3
 # This uses the TEMPLATE_LOADERS setting, which is a list of loaders to use.
4 4
 # Each loader is expected to have this interface:
5 5
 #
6  
-#    callable(name, dirs=[])
  6
+#    callable(name)
7 7
 #
8 8
 # name is the template name.
9  
-# dirs is an optional list of directories to search instead of TEMPLATE_DIRS.
10 9
 #
11 10
 # The loader should return a tuple of (template_source, path). The path returned
12 11
 # might be shown to the user for debugging purposes, so it should identify where
@@ -36,15 +35,12 @@
36 35
 class BaseLoader(object):
37 36
     is_usable = False
38 37
 
39  
-    def __init__(self, *args, **kwargs):
40  
-        pass
41  
-
42  
-    def __call__(self, template_name, template_dirs=None):
43  
-        return self.load_template(template_name, template_dirs)
  38
+    def __call__(self, template_name):
  39
+        return self.load_template(template_name)
44 40
 
45  
-    def load_template(self, template_name, template_dirs=None):
46  
-        source, display_name = self.load_template_source(template_name, template_dirs)
47  
-        origin = make_origin(display_name, self.load_template_source, template_name, template_dirs)
  41
+    def load_template(self, template_name):
  42
+        source, display_name = self.load_template_source(template_name)
  43
+        origin = make_origin(display_name, self.load_template_source, template_name)
48 44
         try:
49 45
             template = get_template_from_string(source, origin, template_name)
50 46
             return template, None
@@ -55,7 +51,7 @@ def load_template(self, template_name, template_dirs=None):
55 51
             # not exist.
56 52
             return source, display_name
57 53
 
58  
-    def load_template_source(self, template_name, template_dirs=None):
  54
+    def load_template_source(self, template_name):
59 55
         """
60 56
         Returns a tuple containing the source and origin for the given template
61 57
         name.
@@ -72,16 +68,16 @@ def reset(self):
72 68
         pass
73 69
 
74 70
 class LoaderOrigin(Origin):
75  
-    def __init__(self, display_name, loader, name, dirs):
  71
+    def __init__(self, display_name, loader, name):
76 72
         super(LoaderOrigin, self).__init__(display_name)
77  
-        self.loader, self.loadname, self.dirs = loader, name, dirs
  73
+        self.loader, self.loadname = loader, name
78 74
 
79 75
     def reload(self):
80  
-        return self.loader(self.loadname, self.dirs)[0]
  76
+        return self.loader(self.loadname)[0]
81 77
 
82  
-def make_origin(display_name, loader, name, dirs):
  78
+def make_origin(display_name, loader, name):
83 79
     if settings.TEMPLATE_DEBUG and display_name:
84  
-        return LoaderOrigin(display_name, loader, name, dirs)
  80
+        return LoaderOrigin(display_name, loader, name)
85 81
     else:
86 82
         return None
87 83
 
@@ -118,7 +114,7 @@ def find_template_loader(loader):
118 114
     else:
119 115
         raise ImproperlyConfigured('Loader does not define a "load_template" callable template source loader')
120 116
 
121  
-def find_template(name, dirs=None):
  117
+def find_template(name):
122 118
     # Calculate template_source_loaders the first time the function is executed
123 119
     # because putting this logic in the module-level namespace may cause
124 120
     # circular import errors. See Django ticket #1292.
@@ -132,8 +128,8 @@ def find_template(name, dirs=None):
132 128
         template_source_loaders = tuple(loaders)
133 129
     for loader in template_source_loaders:
134 130
         try:
135  
-            source, display_name = loader(name, dirs)
136  
-            return (source, make_origin(display_name, loader, name, dirs))
  131
+            source, display_name = loader(name)
  132
+            return (source, make_origin(display_name, loader, name))
137 133
         except TemplateDoesNotExist:
138 134
             pass
139 135
     raise TemplateDoesNotExist(name)
66  django/template/loaders/app_directories.py
@@ -8,58 +8,34 @@
8 8
 
9 9
 from django.conf import settings
10 10
 from django.core.exceptions import ImproperlyConfigured
  11
+from django.core.files.storage import AppDirectoryStorage
11 12
 from django.template.base import TemplateDoesNotExist
12 13
 from django.template.loader import BaseLoader
13  
-from django.utils._os import safe_join
14  
-from django.utils.importlib import import_module
15  
-from django.utils import six
16 14
 
17  
-# At compile time, cache the directories to search.
18  
-if not six.PY3:
19  
-    fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
20  
-app_template_dirs = []
21  
-for app in settings.INSTALLED_APPS:
22  
-    try:
23  
-        mod = import_module(app)
24  
-    except ImportError as e:
25  
-        raise ImproperlyConfigured('ImportError %s: %s' % (app, e.args[0]))
26  
-    template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
27  
-    if os.path.isdir(template_dir):
28  
-        if not six.PY3:
29  
-            template_dir = template_dir.decode(fs_encoding)
30  
-        app_template_dirs.append(template_dir)
31 15
 
32  
-# It won't change, so convert it to a tuple to save memory.
33  
-app_template_dirs = tuple(app_template_dirs)
34 16
 
35 17
 class Loader(BaseLoader):
36 18
     is_usable = True
37 19
 
38  
-    def get_template_sources(self, template_name, template_dirs=None):
39  
-        """
40  
-        Returns the absolute paths to "template_name", when appended to each
41  
-        directory in "template_dirs". Any paths that don't lie inside one of the
42  
-        template dirs are excluded from the result set, for security reasons.
43  
-        """
44  
-        if not template_dirs:
45  
-            template_dirs = app_template_dirs
46  
-        for template_dir in template_dirs:
47  
-            try:
48  
-                yield safe_join(template_dir, template_name)
49  
-            except UnicodeDecodeError:
50  
-                # The template dir name was a bytestring that wasn't valid UTF-8.
51  
-                raise
52  
-            except ValueError:
53  
-                # The joined path was located outside of template_dir.
54  
-                pass
  20
+    def __init__(self, apps=None):
  21
+        self.apps = apps
  22
+        if apps is None:
  23
+            self.apps = settings.INSTALLED_APPS
  24
+        self.reset()
55 25
 
56  
-    def load_template_source(self, template_name, template_dirs=None):
57  
-        for filepath in self.get_template_sources(template_name, template_dirs):
58  
-            try:
59  
-                with open(filepath, 'rb') as fp:
60  
-                    return (fp.read().decode(settings.FILE_CHARSET), filepath)
61  
-            except IOError:
62  
-                pass
63  
-        raise TemplateDoesNotExist(template_name)
  26
+    def reset(self):
  27
+        app_template_storages = []
  28
+        for app in self.apps:
  29
+            storage = AppDirectoryStorage(app, 'templates')
  30
+            if storage.isdir(''):
  31
+                app_template_storages.append(storage)
  32
+
  33
+        self.app_template_storages = tuple(app_template_storages)
64 34
 
65  
-_loader = Loader()
  35
+    def load_template_source(self, template_name):
  36
+        for storage in self.app_template_storages:
  37
+            if storage.exists(template_name):
  38
+                with storage.open(template_name, 'rb') as fp:
  39
+                    return (fp.read().decode(settings.FILE_CHARSET), template_name)
  40
+
  41
+        raise TemplateDoesNotExist(template_name)
13  django/template/loaders/cached.py
@@ -27,23 +27,20 @@ def loaders(self):
27 27
             self._cached_loaders = cached_loaders
28 28
         return self._cached_loaders
29 29
 
30  
-    def find_template(self, name, dirs=None):
  30
+    def find_template(self, name):
31 31
         for loader in self.loaders:
32 32
             try:
33  
-                template, display_name = loader(name, dirs)
34  
-                return (template, make_origin(display_name, loader, name, dirs))
  33
+                template, display_name = loader(name)
  34
+                return (template, make_origin(display_name, loader, name))
35 35
             except TemplateDoesNotExist:
36 36
                 pass
37 37
         raise TemplateDoesNotExist(name)
38 38
 
39  
-    def load_template(self, template_name, template_dirs=None):
  39
+    def load_template(self, template_name):
40 40
         key = template_name
41  
-        if template_dirs:
42  
-            # If template directories were specified, use a hash to differentiate
43  
-            key = '-'.join([template_name, hashlib.sha1('|'.join(template_dirs)).hexdigest()])
44 41
 
45 42
         if key not in self.template_cache:
46  
-            template, origin = self.find_template(template_name, template_dirs)
  43
+            template, origin = self.find_template(template_name)
47 44
             if not hasattr(template, 'render'):
48 45
                 try:
49 46
                     template = get_template_from_string(template, origin, template_name)
35  django/template/loaders/eggs.py
... ...
@@ -1,35 +0,0 @@
1  
-# Wrapper for loading templates from eggs via pkg_resources.resource_string.
2  
-from __future__ import unicode_literals
3  
-
4  
-try:
5  
-    from pkg_resources import resource_string
6  
-except ImportError:
7  
-    resource_string = None
8  
-
9  
-from django.conf import settings
10  
-from django.template.base import TemplateDoesNotExist
11  
-from django.template.loader import BaseLoader
12  
-from django.utils import six
13  
-
14  
-class Loader(BaseLoader):
15  
-    is_usable = resource_string is not None
16  
-
17  
-    def load_template_source(self, template_name, template_dirs=None):
18  
-        """
19  
-        Loads templates from Python eggs via pkg_resource.resource_string.
20  
-
21  
-        For every installed app, it tries to get the resource (app, template_name).
22  
-        """
23  
-        if resource_string is not None:
24  
-            pkg_name = 'templates/' + template_name
25  
-            for app in settings.INSTALLED_APPS:
26  
-                try:
27  
-                    resource = resource_string(app, pkg_name)
28  
-                except Exception:
29  
-                    continue
30  
-                if not six.PY3:
31  
-                    resource = resource.decode(settings.FILE_CHARSET)
32  
-                return (resource, 'egg:%s:%s' % (app, pkg_name))
33  
-        raise TemplateDoesNotExist(template_name)
34  
-
35  
-_loader = Loader()
17  django/template/loaders/filesystem.py
@@ -10,15 +10,18 @@
10 10
 class Loader(BaseLoader):
11 11
     is_usable = True
12 12
 
13  
-    def get_template_sources(self, template_name, template_dirs=None):
  13
+    def __init__(self, template_dirs=None):
  14
+        self.template_dirs = template_dirs
  15
+        if template_dirs is None:
  16
+            self.template_dirs = settings.TEMPLATE_DIRS
  17
+
  18
+    def get_template_sources(self, template_name):
14 19
         """
15 20
         Returns the absolute paths to "template_name", when appended to each
16 21
         directory in "template_dirs". Any paths that don't lie inside one of the
17 22
         template dirs are excluded from the result set, for security reasons.
18 23
         """
19  
-        if not template_dirs:
20  
-            template_dirs = settings.TEMPLATE_DIRS
21  
-        for template_dir in template_dirs:
  24
+        for template_dir in self.template_dirs:
22 25
             try:
23 26
                 yield safe_join(template_dir, template_name)
24 27
             except UnicodeDecodeError:
@@ -30,9 +33,9 @@ def get_template_sources(self, template_name, template_dirs=None):
30 33
                 # fatal).
31 34
                 pass
32 35
 
33  
-    def load_template_source(self, template_name, template_dirs=None):
  36
+    def load_template_source(self, template_name):
34 37
         tried = []
35  
-        for filepath in self.get_template_sources(template_name, template_dirs):
  38
+        for filepath in self.get_template_sources(template_name):
36 39
             try:
37 40
                 with open(filepath, 'rb') as fp:
38 41
                     return (fp.read().decode(settings.FILE_CHARSET), filepath)
@@ -44,5 +47,3 @@ def load_template_source(self, template_name, template_dirs=None):
44 47
             error_msg = "Your TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory."
45 48
         raise TemplateDoesNotExist(error_msg)
46 49
     load_template_source.is_usable = True
47  
-
48  
-_loader = Loader()
87  django/utils/importlib.py
... ...
@@ -1,5 +1,9 @@
1 1
 # Taken from Python 2.7 with permission from/by the original author.
  2
+import os
2 3
 import sys
  4
+import imp
  5
+import pkgutil
  6
+import warnings
3 7
 
4 8
 def _resolve_name(name, package, level):
5 9
     """Return the absolute name of the module to be imported."""
@@ -34,3 +38,86 @@ def import_module(name, package=None):
34 38
         name = _resolve_name(name[level:], package, level)
35 39
     __import__(name)
36 40
     return sys.modules[name]
  41
+
  42
+
  43
+def find_package_path(name, path=None):
  44
+    """Finds search path for package with given name.
  45
+
  46
+    The 'path' argument defaults to ``sys.path``.
  47
+
  48
+    Raises ImportError if no search path could be found.
  49
+    """
  50
+    if path is None:
  51
+        path = sys.path
  52
+
  53
+    results = []
  54
+
  55
+    for path_item in path:
  56
+        importer = get_importer(path_item)
  57
+
  58
+        if importer is None:
  59
+            continue
  60
+
  61
+        try:
  62
+            loader = importer.find_module(name)
  63
+
  64
+            if loader is not None:
  65
+
  66
+                if not hasattr(loader, 'is_package'):
  67
+                    warnings.warn(
  68
+                        "Django cannot find search path for package '%s' ",
  69
+                        "under '%s', because the loader returned by '%s' does ",
  70
+                        "not implement 'is_package' method."%(
  71
+                            name,
  72
+                            path_item,
  73
+                            importer.__class__.__name__))
  74
+                    continue
  75
+
  76
+                if not hasattr(loader, 'get_filename'):
  77
+                    warnings.warn(
  78
+                        "Django cannot find search path for package '%s' ",
  79
+                        "under '%s', because the loader returned by '%s' does ",
  80
+                        "not implement 'get_filename' method."%(
  81
+                            name,
  82
+                            path_item,
  83
+                            importer.__class__.__name__))
  84
+                    continue
  85
+
  86
+                if loader.is_package(name):
  87
+                    results.append(os.path.dirname(loader.get_filename(name)))
  88
+        except ImportError:
  89
+            pass
  90
+
  91
+    if not results:
  92
+        raise ImportError("No package named %s" % name)
  93
+
  94
+    return results
  95
+
  96
+
  97
+get_importer = pkgutil.get_importer
  98
+
  99
+try:
  100
+    import zipimport
  101
+
  102
+    if hasattr(zipimport.zipimporter, 'get_filename'):
  103
+        class ZipImporter(zipimport.zipimporter):
  104
+            def get_filename(self, fullname):
  105
+                archivepath = os.path.join(self.archive, self.prefix)
  106
+                if self.is_package(fullname):
  107
+                    return os.path.join(archivepath, fullname, '__init__.py')
  108
+
  109
+                return os.path.join(archivepath, fullname + '.py')
  110
+
  111
+        def get_importer(path_item):
  112
+            importer = pkgutil.get_importer(path_item)
  113
+
  114
+            if isinstance(importer, zipimport.zipimporter):
  115
+                archivepath = os.path.join(importer.archive, importer.prefix)
  116
+                importer = ZipImporter(os.path.dirname(archivepath))
  117
+
  118
+            return importer
  119
+
  120
+except ImportError:
  121
+    pass
  122
+
  123
+
37  django/utils/translation/trans_real.py
@@ -8,13 +8,13 @@
8 8
 import gettext as gettext_module
9 9
 from threading import local
10 10
 
  11
+from django.core.files.storage import FileSystemStorage, AppDirectoryStorage
11 12
 from django.utils.importlib import import_module
12 13
 from django.utils.encoding import force_str, force_text
13 14
 from django.utils.safestring import mark_safe, SafeData
14 15
 from django.utils import six
15 16
 from django.utils.six import StringIO
16 17
 
17  
-
18 18
 # Translations are cached in a dictionary for every language+app tuple.
19 19
 # The active translations are stored by threadid to make them thread local.
20 20
 _translations = {}
@@ -109,7 +109,7 @@ def translation(language):
109 109
 
110 110
     from django.conf import settings
111 111
 
112  
-    globalpath = os.path.join(os.path.dirname(sys.modules[settings.__module__].__file__), 'locale')
  112
+    globalstorage = FileSystemStorage(os.path.join(os.path.dirname(sys.modules[settings.__module__].__file__), 'locale'))
113 113
 
114 114
     def _fetch(lang, fallback=None):
115 115
 
@@ -121,15 +121,21 @@ def _fetch(lang, fallback=None):
121 121
 
122 122
         loc = to_locale(lang)
123 123
 
124  
-        def _translation(path):
125  
-            try:
126  
-                t = gettext_module.translation('django', path, [loc], DjangoTranslation)
127  
-                t.set_language(lang)
128  
-                return t
129  
-            except IOError:
  124
+        def _translation(storage):
  125
+            nelangs = gettext_module._expand_lang(loc)
  126
+            for nelang in nelangs:
  127
+                locpath = os.path.join(nelang, 'LC_MESSAGES', 'django.mo')
  128
+                if storage.exists(locpath) and not storage.isdir(locpath):
  129
+                    fp = storage.open(locpath)
  130
+                    t = DjangoTranslation(fp)
  131
+                    break
  132
+            else:
130 133
                 return None
131 134
 
132  
-        res = _translation(globalpath)
  135
+            t.set_language(lang)
  136
+            return t
  137
+
  138
+        res = _translation(globalstorage)
133 139
 
134 140
         # We want to ensure that, for example,  "en-gb" and "en-us" don't share
135 141
         # the same translation object (thus, merging en-us with a local update
@@ -140,8 +146,8 @@ def _translation(path):
140 146
             res._info = res._info.copy()
141 147
             res._catalog = res._catalog.copy()
142 148
 
143  
-        def _merge(path):
144  
-            t = _translation(path)
  149
+        def _merge(storage):
  150
+            t = _translation(storage)
145 151
             if t is not None:
146 152
                 if res is None:
147 153
                     return t
@@ -150,15 +156,14 @@ def _merge(path):
150 156
             return res
151 157
 
152 158
         for appname in reversed(settings.INSTALLED_APPS):
153  
-            app = import_module(appname)
154  
-            apppath = os.path.join(os.path.dirname(app.__file__), 'locale')
  159
+            appstorage = AppDirectoryStorage(appname, 'locale')
155 160
 
156  
-            if os.path.isdir(apppath):
157  
-                res = _merge(apppath)
  161
+            if appstorage.isdir(''):
  162
+                res = _merge(appstorage)
158 163
 
159 164
         for localepath in reversed(settings.LOCALE_PATHS):
160 165
             if os.path.isdir(localepath):
161  
-                res = _merge(localepath)
  166
+                res = _merge(FileSystemStorage(localepath))
162 167
 
163 168
         if res is None:
164 169
             if fallback is not None:
4  docs/ref/templates/api.txt
@@ -828,8 +828,8 @@ of the ``load_template_source()`` method implemented there::
828 828
     class Loader(app_directories.Loader):
829 829
         is_usable = True
830 830
 
831  
-        def load_template(self, template_name, template_dirs=None):
832  
-            source, origin = self.load_template_source(template_name, template_dirs)
  831
+        def load_template(self, template_name):
  832
+            source, origin = self.load_template_source(template_name)
833 833
             template = Template(source)
834 834
             return template, origin
835 835
 
0  tests/regressiontests/admin_scripts/lib1/nons_app/__init__.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/nons_app/management/__init__.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/__init__.py
No changes.
9  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/nons_app_command1.py
... ...
@@ -0,0 +1,9 @@
  1
+from django.core.management.base import BaseCommand
  2
+
  3
+class Command(BaseCommand):
  4
+    help = 'Test managment commands in non-namespaced app'
  5
+    requires_model_validation = False
  6
+    args = ''
  7
+
  8
+    def handle(self, *labels, **options):
  9
+        print 'EXECUTE:nons_app_command1'
0  tests/regressiontests/admin_scripts/lib1/nons_app/models.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/npapp/__init__.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/npapp/management.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/npapp/models.py
No changes.
6  tests/regressiontests/admin_scripts/lib1/nsapps/__init__.py
... ...
@@ -0,0 +1,6 @@
  1
+# http://packages.python.org/distribute/setuptools.html#namespace-packages
  2
+try:
  3
+    __import__('pkg_resources').declare_namespace(__name__)
  4
+except ImportError: