diff --git a/django/test/client.py b/django/test/client.py
a
|
b
|
|
13 | 13 | |
14 | 14 | from django.conf import settings |
15 | 15 | from django.contrib.auth import authenticate, login |
| 16 | from django.contrib.sessions.middleware import SessionMiddleware |
16 | 17 | from django.core.handlers.base import BaseHandler |
17 | 18 | from django.core.handlers.wsgi import WSGIRequest |
18 | 19 | from django.core.signals import got_request_exception |
… |
… |
|
24 | 25 | from django.utils.http import urlencode |
25 | 26 | from django.utils.importlib import import_module |
26 | 27 | from django.utils.itercompat import is_iterable |
| 28 | from django.utils.module_loading import issubclass_by_name |
27 | 29 | from django.db import close_connection |
28 | 30 | from django.test.utils import ContextList |
29 | 31 | |
… |
… |
|
497 | 499 | not available. |
498 | 500 | """ |
499 | 501 | user = authenticate(**credentials) |
500 | | if user and user.is_active \ |
501 | | and 'django.contrib.sessions.middleware.SessionMiddleware' in settings.MIDDLEWARE_CLASSES: |
| 502 | sessions_enabled = False |
| 503 | if user and user.is_active: |
| 504 | for middleware in settings.MIDDLEWARE_CLASSES: |
| 505 | sessions_enabled = issubclass_by_name(middleware, SessionMiddleware) |
| 506 | if sessions_enabled: |
| 507 | break |
| 508 | if sessions_enabled: |
502 | 509 | engine = import_module(settings.SESSION_ENGINE) |
503 | 510 | |
504 | 511 | # Create a fake request to store login details. |
diff --git a/django/utils/module_loading.py b/django/utils/module_loading.py
a
|
b
|
|
2 | 2 | import os |
3 | 3 | import sys |
4 | 4 | |
| 5 | from django.utils.importlib import import_module |
| 6 | |
5 | 7 | |
6 | 8 | def module_has_submodule(package, module_name): |
7 | 9 | """See if 'module' is in 'package'.""" |
… |
… |
|
67 | 69 | else: |
68 | 70 | # Exhausted the search, so the module cannot be found. |
69 | 71 | return False |
| 72 | |
| 73 | def issubclass_by_name(symbol_name, klass): |
| 74 | """ |
| 75 | Verify if possibly dotted, in string form :param symbol_name: is a |
| 76 | subclass of :param klass:. |
| 77 | |
| 78 | Example: |
| 79 | |
| 80 | >>> issubclass_by_name('django.utils.datastructures.SortedDict', dict) |
| 81 | True |
| 82 | """ |
| 83 | try: |
| 84 | mod, dot, klass_name = symbol_name.rpartition('.') |
| 85 | mod = import_module(mod) |
| 86 | subclass = getattr(mod, klass_name, None) |
| 87 | if subclass is None: |
| 88 | return False |
| 89 | return issubclass(subclass, klass) |
| 90 | except ImportError: |
| 91 | return False |
diff --git a/tests/regressiontests/test_client_regress/middleware.py b/tests/regressiontests/test_client_regress/middleware.py
new file mode 100644
-
|
+
|
|
| 1 | from django.contrib.sessions.middleware import SessionMiddleware |
| 2 | |
| 3 | |
| 4 | class CustomizedDjangoSessionMiddleware(SessionMiddleware): |
| 5 | pass |
| 6 | |
| 7 | |
| 8 | class ReallyCustomSessionMiddleware(object): |
| 9 | pass |
diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
a
|
b
|
|
2 | 2 | """ |
3 | 3 | Regression tests for the Test Client, especially the customized assertions. |
4 | 4 | """ |
| 5 | from __future__ import absolute_import |
| 6 | |
5 | 7 | import os |
6 | 8 | import warnings |
7 | 9 | |
… |
… |
|
17 | 19 | from django.template.response import SimpleTemplateResponse |
18 | 20 | from django.http import HttpResponse |
19 | 21 | |
| 22 | from . import middleware |
| 23 | |
| 24 | CUSTOM_SESSION_MIDDLEWARE1 = '%s.CustomizedDjangoSessionMiddleware' % middleware.__name__ |
| 25 | CUSTOM_SESSION_MIDDLEWARE2 = '%s.ReallyCustomSessionMiddleware' % middleware.__name__ |
20 | 26 | |
21 | 27 | class AssertContainsTests(TestCase): |
22 | 28 | def setUp(self): |
… |
… |
|
564 | 570 | self.assertEqual(response.context['user'].username, 'testclient') |
565 | 571 | |
566 | 572 | |
567 | | class NoSessionsAppInstalled(SessionEngineTests): |
568 | | """#7836 - Test client can exercise sessions even when 'django.contrib.sessions' isn't installed.""" |
| 573 | class SessionMiddlewareTests(TestCase): |
| 574 | fixtures = ['testdata'] |
569 | 575 | |
570 | 576 | # Remove the 'session' contrib app from INSTALLED_APPS |
571 | 577 | @override_settings(INSTALLED_APPS=tuple(filter(lambda a: a!='django.contrib.sessions', settings.INSTALLED_APPS))) |
572 | | def test_session(self): |
| 578 | def test_no_session_app(self): |
| 579 | """Test client can exercise sessions even when 'django.contrib.sessions' isn't installed (#7836).""" |
573 | 580 | # This request sets a session variable. |
574 | 581 | response = self.client.get('/test_client_regress/set_session/') |
575 | 582 | self.assertEqual(response.status_code, 200) |
576 | 583 | self.assertEqual(self.client.session['session_var'], 'YES') |
577 | 584 | |
| 585 | # Use a custom session middleware that inherits from Django's |
| 586 | @override_settings( |
| 587 | MIDDLEWARE_CLASSES=map( |
| 588 | lambda m: m if m!='django.contrib.sessions.middleware.SessionMiddleware' else CUSTOM_SESSION_MIDDLEWARE1, |
| 589 | settings.MIDDLEWARE_CLASSES |
| 590 | ) |
| 591 | ) |
| 592 | def test_subclassed_session_middleware(self): |
| 593 | """Test client can login when a subclass of Django SessionMiddleware is in use (#16605).""" |
| 594 | login = self.client.login(username='testclient', password='password') |
| 595 | self.assertTrue(login, 'Could not log in') |
| 596 | |
| 597 | # Use a custom session middleware |
| 598 | @override_settings( |
| 599 | MIDDLEWARE_CLASSES=map( |
| 600 | lambda m: m if m!='django.contrib.sessions.middleware.SessionMiddleware' else CUSTOM_SESSION_MIDDLEWARE2, |
| 601 | settings.MIDDLEWARE_CLASSES |
| 602 | ) |
| 603 | ) |
| 604 | def test_custom_session_middleware(self): |
| 605 | """Test client can login when a custom session middleware is in use.""" |
| 606 | login = self.client.login(username='testclient', password='password') |
| 607 | self.assertTrue(login, 'Could not log in') |
| 608 | |
578 | 609 | |
579 | 610 | class URLEscapingTests(TestCase): |
580 | 611 | def test_simple_argument_get(self): |
diff --git a/tests/regressiontests/utils/module_loading.py b/tests/regressiontests/utils/module_loading.py
a
|
b
|
|
5 | 5 | |
6 | 6 | from django.utils import unittest |
7 | 7 | from django.utils.importlib import import_module |
8 | | from django.utils.module_loading import module_has_submodule |
| 8 | from django.utils.module_loading import module_has_submodule, issubclass_by_name |
9 | 9 | |
10 | 10 | |
11 | 11 | class DefaultLoader(unittest.TestCase): |
… |
… |
|
154 | 154 | def tearDown(self): |
155 | 155 | super(CustomLoader, self).tearDown() |
156 | 156 | sys.path_hooks.pop(0) |
| 157 | |
| 158 | |
| 159 | class IssubclassByNameTests(unittest.TestCase): |
| 160 | def test_success(self): |
| 161 | self.assertTrue(issubclass_by_name('django.utils.datastructures.SortedDict', dict)) |
| 162 | |
| 163 | def test_failure(self): |
| 164 | self.assertFalse(issubclass_by_name('django.utils.datastructures.SortedDict', str)) |
| 165 | |
| 166 | def test_import_failure_returns_false(self): |
| 167 | self.assertFalse(issubclass_by_name('i.dont.exist.SortedDict', str)) |
| 168 | |
| 169 | def test_bogus_symbol_name_returns_false(self): |
| 170 | self.assertFalse(issubclass_by_name('django.utils.module_loading.IDontExist', str)) |
diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py
a
|
b
|
|
5 | 5 | |
6 | 6 | from .dateformat import DateFormatTests |
7 | 7 | from .feedgenerator import FeedgeneratorTest |
8 | | from .module_loading import DefaultLoader, EggLoader, CustomLoader |
| 8 | from .module_loading import DefaultLoader, EggLoader, CustomLoader, IssubclassByNameTests |
9 | 9 | from .termcolors import TermColorTests |
10 | 10 | from .html import TestUtilsHtml |
11 | 11 | from .http import TestUtilsHttp |