mirror of
https://github.com/django/django.git
synced 2025-10-28 08:06:09 +00:00
Fixed #21874 -- Require Django applications to have a filesystem path.
Wherever possible this filesystem path is derived automatically from the app module's ``__path__`` and ``__file__`` attributes (this avoids any backwards-compatibility problems). AppConfig allows specifying an app's filesystem location explicitly, which overrides all autodetection based on ``__path__`` and ``__file__``. This permits Django to support any type of module as an app (namespace packages, fake modules, modules loaded by other hypothetical non-filesystem module loaders), as long as the app is configured with an explicit filesystem path. Thanks Aymeric for review and discussion.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
from importlib import import_module
|
||||
import os
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.module_loading import module_has_submodule
|
||||
@@ -34,23 +35,10 @@ class AppConfig(object):
|
||||
self.verbose_name = self.label.title()
|
||||
|
||||
# Filesystem path to the application directory eg.
|
||||
# u'/usr/lib/python2.7/dist-packages/django/contrib/admin'. May be
|
||||
# None if the application isn't a bona fide package eg. if it's an
|
||||
# egg. Otherwise it's a unicode on Python 2 and a str on Python 3.
|
||||
# u'/usr/lib/python2.7/dist-packages/django/contrib/admin'. Unicode on
|
||||
# Python 2 and a str on Python 3.
|
||||
if not hasattr(self, 'path'):
|
||||
try:
|
||||
paths = app_module.__path__
|
||||
except AttributeError:
|
||||
self.path = None
|
||||
else:
|
||||
# Convert paths to list because Python 3.3 _NamespacePath does
|
||||
# not support indexing.
|
||||
paths = list(paths)
|
||||
if len(paths) > 1:
|
||||
raise ImproperlyConfigured(
|
||||
"The namespace package app %r has multiple locations, "
|
||||
"which is not supported: %r" % (app_name, paths))
|
||||
self.path = upath(paths[0])
|
||||
self.path = self._path_from_module(app_module)
|
||||
|
||||
# Module containing models eg. <module 'django.contrib.admin.models'
|
||||
# from 'django/contrib/admin/models.pyc'>. Set by import_models().
|
||||
@@ -64,6 +52,29 @@ class AppConfig(object):
|
||||
def __repr__(self):
|
||||
return '<%s: %s>' % (self.__class__.__name__, self.label)
|
||||
|
||||
def _path_from_module(self, module):
|
||||
"""Attempt to determine app's filesystem path from its module."""
|
||||
# See #21874 for extended discussion of the behavior of this method in
|
||||
# various cases.
|
||||
# Convert paths to list because Python 3.3 _NamespacePath does not
|
||||
# support indexing.
|
||||
paths = list(getattr(module, '__path__', []))
|
||||
if len(paths) != 1:
|
||||
filename = getattr(module, '__file__', None)
|
||||
if filename is not None:
|
||||
paths = [os.path.dirname(filename)]
|
||||
if len(paths) > 1:
|
||||
raise ImproperlyConfigured(
|
||||
"The app module %r has multiple filesystem locations (%r); "
|
||||
"you must configure this app with an AppConfig subclass "
|
||||
"with a 'path' class attribute." % (module, paths))
|
||||
elif not paths:
|
||||
raise ImproperlyConfigured(
|
||||
"The app module %r has no filesystem location, "
|
||||
"you must configure this app with an AppConfig subclass "
|
||||
"with a 'path' class attribute." % (module,))
|
||||
return upath(paths[0])
|
||||
|
||||
@classmethod
|
||||
def create(cls, entry):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user