diff options
author | Carl Meyer <carl@oddbird.net> | 2014-01-27 13:28:53 -0700 |
---|---|---|
committer | Carl Meyer <carl@oddbird.net> | 2014-01-27 15:34:22 -0700 |
commit | 88a2d39159872f6a7fced1bae591550650fd7f38 (patch) | |
tree | 0d7ee946c71354f8ea6095b549d926734b2e5579 /tests/apps | |
parent | b87bc461c89f2006f0b27c7240fb488fac32bed1 (diff) | |
download | django-88a2d39159872f6a7fced1bae591550650fd7f38.tar.gz |
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.
Diffstat (limited to 'tests/apps')
-rw-r--r-- | tests/apps/tests.py | 67 |
1 files changed, 66 insertions, 1 deletions
diff --git a/tests/apps/tests.py b/tests/apps/tests.py index d09757c7bd..777e26d816 100644 --- a/tests/apps/tests.py +++ b/tests/apps/tests.py @@ -4,7 +4,7 @@ import os import sys from unittest import skipUnless -from django.apps import apps +from django.apps import apps, AppConfig from django.apps.registry import Apps from django.contrib.admin.models import LogEntry from django.core.exceptions import ImproperlyConfigured @@ -201,6 +201,71 @@ class AppsTests(TestCase): self.assertEqual(new_apps.get_model("apps", "SouthPonies"), temp_model) +class Stub(object): + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +class AppConfigTests(TestCase): + """Unit tests for AppConfig class.""" + def test_path_set_explicitly(self): + """If subclass sets path as class attr, no module attributes needed.""" + class MyAppConfig(AppConfig): + path = 'foo' + + ac = MyAppConfig('label', Stub()) + + self.assertEqual(ac.path, 'foo') + + def test_explicit_path_overrides(self): + """If path set as class attr, overrides __path__ and __file__.""" + class MyAppConfig(AppConfig): + path = 'foo' + + ac = MyAppConfig('label', Stub(__path__=['a'], __file__='b/__init__.py')) + + self.assertEqual(ac.path, 'foo') + + def test_dunder_path(self): + """If single element in __path__, use it (in preference to __file__).""" + ac = AppConfig('label', Stub(__path__=['a'], __file__='b/__init__.py')) + + self.assertEqual(ac.path, 'a') + + def test_no_dunder_path_fallback_to_dunder_file(self): + """If there is no __path__ attr, use __file__.""" + ac = AppConfig('label', Stub(__file__='b/__init__.py')) + + self.assertEqual(ac.path, 'b') + + def test_empty_dunder_path_fallback_to_dunder_file(self): + """If the __path__ attr is empty, use __file__ if set.""" + ac = AppConfig('label', Stub(__path__=[], __file__='b/__init__.py')) + + self.assertEqual(ac.path, 'b') + + def test_multiple_dunder_path_fallback_to_dunder_file(self): + """If the __path__ attr is length>1, use __file__ if set.""" + ac = AppConfig('label', Stub(__path__=['a', 'b'], __file__='c/__init__.py')) + + self.assertEqual(ac.path, 'c') + + def test_no_dunder_path_or_dunder_file(self): + """If there is no __path__ or __file__, raise ImproperlyConfigured.""" + with self.assertRaises(ImproperlyConfigured): + AppConfig('label', Stub()) + + def test_empty_dunder_path_no_dunder_file(self): + """If the __path__ attr is empty and there is no __file__, raise.""" + with self.assertRaises(ImproperlyConfigured): + AppConfig('label', Stub(__path__=[])) + + def test_multiple_dunder_path_no_dunder_file(self): + """If the __path__ attr is length>1 and there is no __file__, raise.""" + with self.assertRaises(ImproperlyConfigured): + AppConfig('label', Stub(__path__=['a', 'b'])) + + @skipUnless( sys.version_info > (3, 3, 0), "Namespace packages sans __init__.py were added in Python 3.3") |