django-multisite2 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. django_multisite2-2.0.0.dist-info/LICENSE +28 -0
  2. django_multisite2-2.0.0.dist-info/METADATA +246 -0
  3. django_multisite2-2.0.0.dist-info/RECORD +51 -0
  4. django_multisite2-2.0.0.dist-info/WHEEL +5 -0
  5. django_multisite2-2.0.0.dist-info/top_level.txt +2 -0
  6. multisite/__init__.py +1 -0
  7. multisite/admin/__init__.py +3 -0
  8. multisite/admin/alias_admin.py +58 -0
  9. multisite/admin/multisite_changelist.py +64 -0
  10. multisite/admin/multisite_model_admin.py +161 -0
  11. multisite/apps.py +29 -0
  12. multisite/exceptions.py +57 -0
  13. multisite/forms.py +22 -0
  14. multisite/hacks.py +163 -0
  15. multisite/management/__init__.py +0 -0
  16. multisite/management/commands/__init__.py +0 -0
  17. multisite/management/commands/update_public_suffix_list.py +33 -0
  18. multisite/middleware/__init__.py +4 -0
  19. multisite/middleware/cookie_domain_middleware.py +72 -0
  20. multisite/middleware/dynamic_site_middleware.py +198 -0
  21. multisite/migrations/0001_initial.py +71 -0
  22. multisite/migrations/0002_alter_alias_id_alter_alias_is_canonical.py +32 -0
  23. multisite/migrations/0003_alter_alias_options_alter_alias_unique_together_and_more.py +41 -0
  24. multisite/migrations/0004_alter_alias_managers.py +22 -0
  25. multisite/migrations/__init__.py +0 -0
  26. multisite/models/__init__.py +3 -0
  27. multisite/models/alias.py +110 -0
  28. multisite/models/managers.py +196 -0
  29. multisite/models/signals.py +36 -0
  30. multisite/models/validators.py +14 -0
  31. multisite/tests/__init__.py +0 -0
  32. multisite/tests/get_test_allowed_hosts.py +14 -0
  33. multisite/tests/get_test_http_response.py +13 -0
  34. multisite/tests/hosts.py +46 -0
  35. multisite/tests/test_settings.py +23 -0
  36. multisite/tests/tests/__init__.py +0 -0
  37. multisite/tests/tests/request_factory.py +12 -0
  38. multisite/tests/tests/test_alias.py +271 -0
  39. multisite/tests/tests/test_contrib_site.py +21 -0
  40. multisite/tests/tests/test_cookie_domain_middleware.py +196 -0
  41. multisite/tests/tests/test_dynamic_site_middleware.py +262 -0
  42. multisite/tests/tests/test_site_cache.py +127 -0
  43. multisite/tests/tests/test_site_domain.py +24 -0
  44. multisite/tests/tests/test_site_id.py +76 -0
  45. multisite/tests/tests/test_template_loader.py +49 -0
  46. multisite/threadlocals.py +136 -0
  47. multisite/utils.py +172 -0
  48. multisite_app/__init__.py +0 -0
  49. multisite_app/apps.py +6 -0
  50. multisite_app/urls.py +5 -0
  51. multisite_app/views.py +8 -0
@@ -0,0 +1,262 @@
1
+ from django.conf import settings
2
+ from django.contrib.sites.models import Site
3
+ from django.core.exceptions import ImproperlyConfigured
4
+ from django.http import Http404, HttpResponse
5
+ from django.test import TestCase, override_settings, tag
6
+
7
+ from multisite import SiteID
8
+ from multisite.exceptions import MultisiteCacheError, MultisiteError
9
+ from multisite.middleware import DynamicSiteMiddleware
10
+ from multisite.models import Alias
11
+
12
+ from ..get_test_allowed_hosts import get_test_allowed_hosts
13
+ from ..get_test_http_response import get_test_http_response
14
+ from .request_factory import RequestFactory
15
+
16
+
17
+ @override_settings(
18
+ ROOT_URLCONF="multisite_app.urls",
19
+ SITE_ID=SiteID(default=0),
20
+ CACHE_MULTISITE_ALIAS="multisite",
21
+ CACHES={
22
+ "default": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"},
23
+ "multisite": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"},
24
+ },
25
+ MULTISITE_FALLBACK=None,
26
+ ALLOWED_HOSTS=get_test_allowed_hosts("example.com", "anothersite.example", replace=True),
27
+ )
28
+ class DynamicSiteMiddlewareTest(TestCase):
29
+ def setUp(self):
30
+ self.host = "example.com"
31
+ self.factory = RequestFactory(host=self.host)
32
+
33
+ Site.objects.all().delete()
34
+ self.site = Site.objects.create(pk=1, domain=self.host)
35
+ self.site2 = Site.objects.create(pk=2, domain="anothersite.example")
36
+
37
+ def test_valid_domain(self):
38
+ # Make the request
39
+ request = self.factory.get("/")
40
+ self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
41
+ self.assertEqual(settings.SITE_ID, self.site.pk)
42
+ # Request again
43
+ self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
44
+ self.assertEqual(settings.SITE_ID, self.site.pk)
45
+
46
+ def test_valid_domain_port(self):
47
+ # Make the request with a specific port
48
+ request = self.factory.get("/", host=self.host + ":8000")
49
+ self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
50
+ self.assertEqual(settings.SITE_ID, self.site.pk)
51
+ # Request again
52
+ self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
53
+ self.assertEqual(settings.SITE_ID, self.site.pk)
54
+
55
+ def test_case_sensitivity(self):
56
+ # Make the request in all uppercase
57
+ request = self.factory.get("/", host=self.host.upper())
58
+ self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
59
+ self.assertEqual(settings.SITE_ID, self.site.pk)
60
+
61
+ def test_change_domain(self):
62
+ # Make the request
63
+ request = self.factory.get("/")
64
+ self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
65
+ self.assertEqual(settings.SITE_ID, self.site.pk)
66
+ # Another request with a different site
67
+ request = self.factory.get("/", host=self.site2.domain)
68
+ self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
69
+ self.assertEqual(settings.SITE_ID, self.site2.pk)
70
+
71
+ def test_unknown_host(self):
72
+ # Unknown host
73
+ request = self.factory.get("/", host="unknown")
74
+ with self.assertRaises(Http404):
75
+ DynamicSiteMiddleware(HttpResponse)(request)
76
+ # The middleware resets SiteID to its default value, as given above, on error.
77
+ self.assertEqual(settings.SITE_ID, 0)
78
+
79
+ def test_unknown_hostport(self):
80
+ # Unknown host:port
81
+ request = self.factory.get("/", host="unknown:8000")
82
+ with self.assertRaises(Http404):
83
+ DynamicSiteMiddleware(HttpResponse)(request)
84
+ # The middleware resets SiteID to its default value, as given above, on error.
85
+ self.assertEqual(settings.SITE_ID, 0)
86
+
87
+ def test_invalid_host(self):
88
+ # Invalid host
89
+ request = self.factory.get("/", host="")
90
+ with self.assertRaises(Http404):
91
+ DynamicSiteMiddleware(HttpResponse)(request)
92
+
93
+ def test_invalid_hostport(self):
94
+ # Invalid host:port
95
+ request = self.factory.get("/", host=":8000")
96
+ with self.assertRaises(Http404):
97
+ DynamicSiteMiddleware(HttpResponse)(request)
98
+
99
+ def test_no_sites(self):
100
+ # FIXME: this needs to go into its own TestCase since
101
+ # it requires modifying the fixture to work properly
102
+ # Remove all Sites
103
+ Site.objects.all().delete()
104
+ # Make the request
105
+ request = self.factory.get("/")
106
+ self.assertRaises(Http404, DynamicSiteMiddleware(HttpResponse), request)
107
+ # The middleware resets SiteID to its default value, as given above, on error.
108
+ self.assertEqual(settings.SITE_ID, 0)
109
+
110
+ @override_settings(MULTISITE_DEBUG=True)
111
+ def test_debug_no_sites_raises(self):
112
+ Site.objects.all().delete()
113
+ request = self.factory.get("/")
114
+ self.assertRaises(MultisiteCacheError, DynamicSiteMiddleware(HttpResponse), request)
115
+
116
+ @override_settings(MULTISITE_DEBUG=False)
117
+ def test_no_sites_raises(self):
118
+ Site.objects.all().delete()
119
+ request = self.factory.get("/")
120
+ self.assertRaises(Http404, DynamicSiteMiddleware(HttpResponse), request)
121
+
122
+ @override_settings(ALLOWED_HOSTS=["example.org"])
123
+ def test_redirect(self):
124
+ host = "example.org"
125
+ alias = Alias.objects.create(site=self.site, domain=host)
126
+ self.assertTrue(alias.redirect_to_canonical)
127
+ # Make the request
128
+ http_response = get_test_http_response()
129
+ request = self.factory.get("/path", host=host)
130
+ response = DynamicSiteMiddleware(http_response)(request)
131
+ self.assertEqual(response.status_code, 301)
132
+ self.assertEqual(response["Location"], "http://%s/path" % self.host)
133
+
134
+ @override_settings(ALLOWED_HOSTS=["example.org"])
135
+ def test_no_redirect(self):
136
+ host = "example.org"
137
+ Alias.objects.create(site=self.site, domain=host, redirect_to_canonical=False)
138
+ # Make the request
139
+ http_response = get_test_http_response()
140
+ request = self.factory.get("/path", host=host)
141
+ self.assertEqual(DynamicSiteMiddleware(http_response)(request).status_code, 200)
142
+ self.assertEqual(settings.SITE_ID, self.site.pk)
143
+
144
+ @tag("9")
145
+ def test_integration(self):
146
+ """Test that the middleware loads and runs properly
147
+ under settings.MIDDLEWARE.
148
+ """
149
+ self.assertTrue(Site.objects.get(domain="example.com"))
150
+ self.assertTrue(Site.objects.get(domain="anothersite.example"))
151
+
152
+ response = self.client.get("/domain/", HTTP_HOST=self.host)
153
+ self.assertEqual(response.status_code, 200)
154
+ self.assertContains(response, self.site.domain)
155
+ self.assertEqual(settings.SITE_ID, self.site.pk)
156
+
157
+ response = self.client.get("/domain/", HTTP_HOST=self.site2.domain)
158
+ self.assertEqual(response.status_code, 200)
159
+ self.assertContains(response, self.site2.domain)
160
+ self.assertEqual(settings.SITE_ID, self.site2.pk)
161
+
162
+
163
+ @override_settings(
164
+ SITE_ID=SiteID(default=0),
165
+ CACHE_MULTISITE_ALIAS="multisite",
166
+ CACHES={"multisite": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"}},
167
+ MULTISITE_FALLBACK=None,
168
+ MULTISITE_FALLBACK_KWARGS={},
169
+ )
170
+ class DynamicSiteMiddlewareFallbackTest(TestCase):
171
+ def setUp(self):
172
+ self.factory = RequestFactory(host="unknown")
173
+
174
+ Site.objects.all().delete()
175
+
176
+ def test_404(self):
177
+ http_response = get_test_http_response()
178
+ request = self.factory.get("/")
179
+ self.assertRaises(Http404, DynamicSiteMiddleware(http_response), request)
180
+ self.assertEqual(settings.SITE_ID, 0)
181
+
182
+ def test_testserver(self):
183
+ host = "testserver"
184
+ site = Site.objects.create(domain=host)
185
+ http_response = get_test_http_response()
186
+ request = self.factory.get("/", host=host)
187
+ self.assertEqual(DynamicSiteMiddleware(http_response)(request).status_code, 200)
188
+ self.assertEqual(settings.SITE_ID, site.pk)
189
+
190
+ def test_string_class(self):
191
+ # Class based
192
+ settings.MULTISITE_FALLBACK = "django.views.generic.base.RedirectView"
193
+ settings.MULTISITE_FALLBACK_KWARGS = {
194
+ "url": "http://example.com/",
195
+ "permanent": False,
196
+ }
197
+ request = self.factory.get("/")
198
+ http_response = get_test_http_response()
199
+ response = DynamicSiteMiddleware(http_response)(request)
200
+ self.assertEqual(response.status_code, 302)
201
+ self.assertEqual(response["Location"], settings.MULTISITE_FALLBACK_KWARGS["url"])
202
+
203
+ def test_class_view(self):
204
+ from django.views.generic.base import RedirectView
205
+
206
+ settings.MULTISITE_FALLBACK = RedirectView.as_view(
207
+ url="http://example.com/", permanent=False
208
+ )
209
+ http_response = get_test_http_response()
210
+ request = self.factory.get("/")
211
+ response = DynamicSiteMiddleware(http_response)(request)
212
+ self.assertEqual(response.status_code, 302)
213
+ self.assertEqual(response["Location"], "http://example.com/")
214
+
215
+ def test_invalid(self):
216
+ settings.MULTISITE_FALLBACK = ""
217
+ http_response = get_test_http_response()
218
+ request = self.factory.get("/")
219
+ self.assertRaises(ImproperlyConfigured, DynamicSiteMiddleware(http_response), request)
220
+
221
+
222
+ @override_settings(SITE_ID=0)
223
+ class DynamicSiteMiddlewareSettingsTest(TestCase):
224
+
225
+ def test_invalid_settings(self):
226
+ http_response = get_test_http_response()
227
+ self.assertRaises(MultisiteError, DynamicSiteMiddleware, http_response)
228
+
229
+
230
+ @override_settings(
231
+ SITE_ID=SiteID(default=0),
232
+ CACHE_MULTISITE_ALIAS="multisite",
233
+ CACHES={"multisite": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}},
234
+ MULTISITE_FALLBACK=None,
235
+ ALLOWED_HOSTS=get_test_allowed_hosts("example.com", replace=True),
236
+ )
237
+ class CacheTest(TestCase):
238
+ def setUp(self):
239
+ self.host = "example.com"
240
+ self.factory = RequestFactory(host=self.host)
241
+
242
+ Site.objects.all().delete()
243
+ self.site = Site.objects.create(domain=self.host)
244
+
245
+ def test_site_domain_changed(self):
246
+ # Test to ensure that the cache is cleared properly
247
+ http_response = get_test_http_response()
248
+ middleware = DynamicSiteMiddleware(http_response)
249
+ cache_key = middleware.get_cache_key(self.host)
250
+ self.assertEqual(middleware.cache.get(cache_key), None)
251
+ # Make the request
252
+ request = self.factory.get("/")
253
+ self.assertEqual(middleware(request).status_code, 200)
254
+ self.assertEqual(middleware.cache.get(cache_key).site_id, self.site.pk)
255
+ # Change the domain name
256
+ self.site.domain = "example.org"
257
+ self.site.save()
258
+ self.assertEqual(middleware.cache.get(cache_key), None)
259
+ # Make the request again, which will now be invalid
260
+ request = self.factory.get("/")
261
+ self.assertRaises(Http404, middleware, request)
262
+ self.assertEqual(settings.SITE_ID, 0)
@@ -0,0 +1,127 @@
1
+ from django.conf import settings
2
+ from django.contrib.sites.models import Site
3
+ from django.test import TestCase, override_settings
4
+
5
+ from multisite import SiteID
6
+ from multisite.hacks import use_framework_for_site_cache
7
+
8
+
9
+ @override_settings(SITE_ID=SiteID())
10
+ class SiteCacheTest(TestCase):
11
+ def _initialize_cache(self):
12
+ # initialize cache again so override key prefix settings are used
13
+ from django.contrib.sites import models
14
+
15
+ use_framework_for_site_cache()
16
+ self.cache = models.SITE_CACHE
17
+
18
+ def setUp(self):
19
+ from django.contrib.sites import models
20
+
21
+ if hasattr(models, "clear_site_cache"):
22
+ # Before Django 1.6, the Site cache is cleared after the Site
23
+ # object has been created. This replicates that behaviour.
24
+ def save(self, *args, **kwargs):
25
+ super(models.Site, self).save(*args, **kwargs)
26
+ models.SITE_CACHE.clear()
27
+
28
+ models.Site.save = save
29
+
30
+ self._initialize_cache()
31
+ Site.objects.all().delete()
32
+ self.host = "example.com"
33
+ self.site = Site.objects.create(domain=self.host)
34
+ settings.SITE_ID.set(self.site.id)
35
+
36
+ def test_get_current(self):
37
+ self.assertRaises(KeyError, self.cache.__getitem__, self.site.id)
38
+ # Populate cache
39
+ self.assertEqual(Site.objects.get_current(), self.site)
40
+ self.assertEqual(self.cache[self.site.id], self.site)
41
+ self.assertEqual(self.cache.get(key=self.site.id), self.site)
42
+ self.assertEqual(self.cache.get(key=-1), None) # Site doesn't exist
43
+ self.assertEqual(self.cache.get(-1, "Default"), "Default") # Site doesn't exist
44
+ self.assertEqual(
45
+ self.cache.get(key=-1, default="Non-existant"), "Non-existant"
46
+ ) # Site doesn't exist
47
+ self.assertEqual(
48
+ "Non-existant",
49
+ self.cache.get(self.site.id, default="Non-existant", version=100),
50
+ ) # Wrong key version 3
51
+ # Clear cache
52
+ self.cache.clear()
53
+ self.assertRaises(KeyError, self.cache.__getitem__, self.site.id)
54
+ self.assertEqual(self.cache.get(key=self.site.id, default="Cleared"), "Cleared")
55
+
56
+ def test_create_site(self):
57
+ self.assertEqual(Site.objects.get_current(), self.site)
58
+ self.assertEqual(Site.objects.get_current().domain, self.site.domain)
59
+ # Create new site
60
+ site = Site.objects.create(domain="example.org")
61
+ settings.SITE_ID.set(site.id)
62
+ self.assertEqual(Site.objects.get_current(), site)
63
+ self.assertEqual(Site.objects.get_current().domain, site.domain)
64
+
65
+ def test_change_site(self):
66
+ self.assertEqual(Site.objects.get_current(), self.site)
67
+ self.assertEqual(Site.objects.get_current().domain, self.site.domain)
68
+ # Change site domain
69
+ self.site.domain = "example.org"
70
+ self.site.save()
71
+ self.assertEqual(Site.objects.get_current(), self.site)
72
+ self.assertEqual(Site.objects.get_current().domain, self.site.domain)
73
+
74
+ def test_delete_site(self):
75
+ self.assertEqual(Site.objects.get_current(), self.site)
76
+ self.assertEqual(Site.objects.get_current().domain, self.site.domain)
77
+ # Delete site
78
+ self.site.delete()
79
+ self.assertRaises(KeyError, self.cache.__getitem__, self.site.id)
80
+
81
+ @override_settings(CACHE_MULTISITE_KEY_PREFIX="__test__")
82
+ def test_multisite_key_prefix(self):
83
+ self._initialize_cache()
84
+ # Populate cache
85
+ self.assertEqual(Site.objects.get_current(), self.site)
86
+ self.assertEqual(self.cache[self.site.id], self.site)
87
+ self.assertEqual(
88
+ self.cache._cache._get_cache_key(self.site.id),
89
+ "sites.{}.{}".format(settings.CACHE_MULTISITE_KEY_PREFIX, self.site.id),
90
+ self.cache._cache._get_cache_key(self.site.id),
91
+ )
92
+
93
+ @override_settings(
94
+ CACHE_MULTISITE_ALIAS="multisite",
95
+ CACHES={
96
+ "multisite": {
97
+ "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
98
+ "KEY_PREFIX": "looselycoupled",
99
+ }
100
+ },
101
+ )
102
+ def test_default_key_prefix(self):
103
+ """
104
+ If CACHE_MULTISITE_KEY_PREFIX is undefined,
105
+ the caching system should use CACHES[current]['KEY_PREFIX'].
106
+ """
107
+ self._initialize_cache()
108
+ # Populate cache
109
+ self.assertEqual(Site.objects.get_current(), self.site)
110
+ self.assertEqual(self.cache[self.site.id], self.site)
111
+ self.assertEqual(
112
+ self.cache._cache._get_cache_key(self.site.id),
113
+ "sites.looselycoupled.{}".format(self.site.id),
114
+ )
115
+
116
+ @override_settings(
117
+ CACHE_MULTISITE_KEY_PREFIX="virtuouslyvirtual",
118
+ )
119
+ def test_multisite_key_prefix_takes_priority_over_default(self):
120
+ self._initialize_cache()
121
+ # Populate cache
122
+ self.assertEqual(Site.objects.get_current(), self.site)
123
+ self.assertEqual(self.cache[self.site.id], self.site)
124
+ self.assertEqual(
125
+ self.cache._cache._get_cache_key(self.site.id),
126
+ "sites.virtuouslyvirtual.{}".format(self.site.id),
127
+ )
@@ -0,0 +1,24 @@
1
+ from django.contrib.sites.models import Site
2
+ from django.core.exceptions import ObjectDoesNotExist
3
+ from django.test import TestCase
4
+
5
+ from multisite import SiteDomain
6
+
7
+
8
+ class TestSiteDomain(TestCase):
9
+ def setUp(self):
10
+ Site.objects.all().delete()
11
+ self.domain = "example.com"
12
+ self.site = Site.objects.create(domain=self.domain)
13
+
14
+ def test_init(self):
15
+ self.assertEqual(int(SiteDomain(default=self.domain)), self.site.id)
16
+ self.assertRaises(ObjectDoesNotExist, int, SiteDomain(default="invalid"))
17
+ self.assertRaises(TypeError, SiteDomain, default=None)
18
+ self.assertRaises(TypeError, SiteDomain, default=1)
19
+
20
+ def test_deferred_site(self):
21
+ domain = "example.org"
22
+ self.assertRaises(ObjectDoesNotExist, int, SiteDomain(default=domain))
23
+ site = Site.objects.create(domain=domain)
24
+ self.assertEqual(int(SiteDomain(default=domain)), site.id)
@@ -0,0 +1,76 @@
1
+ from django.contrib.sites.models import Site
2
+ from django.test import TestCase
3
+
4
+ from multisite import SiteID
5
+
6
+
7
+ class TestSiteID(TestCase):
8
+ def setUp(self):
9
+ Site.objects.all().delete()
10
+ self.site = Site.objects.create(domain="example.com")
11
+ self.site_id = SiteID()
12
+
13
+ def test_invalid_default(self):
14
+ self.assertRaises(ValueError, SiteID, default="a")
15
+ self.assertRaises(ValueError, SiteID, default=self.site_id)
16
+
17
+ def test_compare_default_site_id(self):
18
+ self.site_id = SiteID(default=self.site.id)
19
+ self.assertEqual(self.site_id, self.site.id)
20
+ self.assertFalse(self.site_id != self.site.id)
21
+ self.assertFalse(self.site_id < self.site.id)
22
+ self.assertTrue(self.site_id <= self.site.id)
23
+ self.assertFalse(self.site_id > self.site.id)
24
+ self.assertTrue(self.site_id >= self.site.id)
25
+
26
+ def test_compare_site_ids(self):
27
+ self.site_id.set(1)
28
+ self.assertEqual(self.site_id, self.site_id)
29
+ self.assertFalse(self.site_id != self.site_id)
30
+ self.assertFalse(self.site_id < self.site_id)
31
+ self.assertTrue(self.site_id <= self.site_id)
32
+ self.assertFalse(self.site_id > self.site_id)
33
+ self.assertTrue(self.site_id >= self.site_id)
34
+
35
+ def test_compare_differing_types(self):
36
+ self.site_id.set(1)
37
+ self.assertNotEqual(self.site_id, "1")
38
+ self.assertFalse(self.site_id == "1")
39
+ self.assertTrue(self.site_id < "1")
40
+ self.assertTrue(self.site_id <= "1")
41
+ self.assertFalse(self.site_id > "1")
42
+ self.assertFalse(self.site_id >= "1")
43
+ self.assertNotEqual("1", self.site_id)
44
+ self.assertFalse("1" == self.site_id)
45
+ self.assertFalse("1" < self.site_id)
46
+ self.assertFalse("1" <= self.site_id)
47
+ self.assertTrue("1" > self.site_id)
48
+ self.assertTrue("1" >= self.site_id)
49
+
50
+ def test_set(self):
51
+ self.site_id.set(10)
52
+ self.assertEqual(int(self.site_id), 10)
53
+ self.site_id.set(20)
54
+ self.assertEqual(int(self.site_id), 20)
55
+ self.site_id.set(self.site)
56
+ self.assertEqual(int(self.site_id), self.site.id)
57
+
58
+ def test_hash(self):
59
+ self.site_id.set(10)
60
+ self.assertEqual(hash(self.site_id), 10)
61
+ self.site_id.set(20)
62
+ self.assertEqual(hash(self.site_id), 20)
63
+
64
+ def test_str_repr(self):
65
+ self.site_id.set(10)
66
+ self.assertEqual(str(self.site_id), "10")
67
+ self.assertEqual(repr(self.site_id), "10")
68
+
69
+ def test_context_manager(self):
70
+ self.assertEqual(self.site_id.site_id, None)
71
+ with self.site_id.override(1):
72
+ self.assertEqual(self.site_id.site_id, 1)
73
+ with self.site_id.override(2):
74
+ self.assertEqual(self.site_id.site_id, 2)
75
+ self.assertEqual(self.site_id.site_id, 1)
76
+ self.assertEqual(self.site_id.site_id, None)
@@ -0,0 +1,49 @@
1
+ import logging
2
+ import os
3
+ import tempfile
4
+ from io import StringIO
5
+ from pathlib import Path
6
+ from unittest import mock
7
+
8
+ from django.core.management import call_command
9
+ from django.template.loader import get_template
10
+ from django.test import TestCase, override_settings, tag
11
+
12
+
13
+ class UpdatePublicSuffixListCommandTestCase(TestCase):
14
+ def setUp(self):
15
+ self.cache_dir = tempfile.gettempdir()
16
+ # save the tldextract logger output to a buffer to test output
17
+ self.out = StringIO()
18
+ self.logger = logging.getLogger("tldextract")
19
+ self.logger.setLevel(logging.DEBUG)
20
+ stdout_handler = logging.StreamHandler(self.out)
21
+ stdout_handler.setLevel(logging.DEBUG)
22
+ self.logger.addHandler(stdout_handler)
23
+
24
+ # patch tldextract to avoid actual requests
25
+ self.patcher = mock.patch("tldextract.TLDExtract")
26
+ self.tldextract = self.patcher.start()
27
+
28
+ def tearDown(self):
29
+ self.patcher.stop()
30
+
31
+ def tldextract_update_side_effect(self, *args, **kwargs):
32
+ self.logger.debug("TLDExtract.update called")
33
+
34
+ def test_command(self):
35
+ call_command("update_public_suffix_list")
36
+ expected_calls = [
37
+ mock.call(cache_dir=self.cache_dir),
38
+ mock.call().update(fetch_now=True),
39
+ ]
40
+ self.assertEqual(self.tldextract.mock_calls, expected_calls)
41
+
42
+ def test_command_output(self):
43
+ # make sure that the logger receives output from the method
44
+ self.tldextract().update.side_effect = self.tldextract_update_side_effect
45
+
46
+ call_command("update_public_suffix_list", verbosity=3)
47
+ update_message = "Updating {}".format(self.cache_dir)
48
+ self.assertIn(update_message, self.out.getvalue())
49
+ self.assertIn("TLDExtract.update called", self.out.getvalue())
@@ -0,0 +1,136 @@
1
+ from contextlib import contextmanager
2
+ from threading import local
3
+
4
+ from django.conf import settings
5
+ from django.core.exceptions import ImproperlyConfigured
6
+
7
+
8
+ class SiteID(local):
9
+ """
10
+ Dynamic settings.SITE_ID replacement, which acts like an integer.
11
+
12
+ django.contrib.sites can allow multiple Django sites to share the
13
+ same database. However, they cannot share the same code by
14
+ default.
15
+
16
+ SiteID can be used to replace the static settings.SITE_ID integer
17
+ when combined with the appropriate middleware.
18
+ """
19
+
20
+ def __init__(self, default=None, *args, **kwargs):
21
+ """
22
+ ``default``, if specified, determines the default SITE_ID,
23
+ if that is unset.
24
+ """
25
+ if default is not None and not isinstance(default, int):
26
+ raise ValueError("%r is not a valid default." % default)
27
+ self.default = default
28
+ self.reset()
29
+
30
+ def __repr__(self):
31
+ return repr(self.__int__())
32
+
33
+ def __str__(self):
34
+ return str(self.__int__())
35
+
36
+ def __int__(self):
37
+ if self.site_id is None:
38
+ return self.get_default()
39
+ return self.site_id
40
+
41
+ def __lt__(self, other):
42
+ if isinstance(other, int):
43
+ return self.__int__() < other
44
+ elif isinstance(other, SiteID):
45
+ return self.__int__() < other.__int__()
46
+ return True
47
+
48
+ def __le__(self, other):
49
+ if isinstance(other, int):
50
+ return self.__int__() <= other
51
+ elif isinstance(other, SiteID):
52
+ return self.__int__() <= other.__int__()
53
+ return True
54
+
55
+ def __eq__(self, other):
56
+ if isinstance(other, int):
57
+ return self.__int__() == other
58
+ elif isinstance(other, SiteID):
59
+ return self.__int__() == other.__int__()
60
+ return False
61
+
62
+ def __ne__(self, other):
63
+ return not self.__eq__(other)
64
+
65
+ def __gt__(self, other):
66
+ return not self.__le__(other)
67
+
68
+ def __ge__(self, other):
69
+ return not self.__lt__(other)
70
+
71
+ def __hash__(self):
72
+ return self.__int__()
73
+
74
+ @contextmanager
75
+ def override(self, value):
76
+ """
77
+ Overrides SITE_ID temporarily::
78
+
79
+ >>> with settings.SITE_ID.override(2):
80
+ ... print settings.SITE_ID
81
+ 2
82
+ """
83
+ site_id_original = self.site_id
84
+ self.set(value)
85
+ try:
86
+ yield self
87
+ finally:
88
+ self.site_id = site_id_original
89
+
90
+ def set(self, value):
91
+ from django.db.models import Model
92
+
93
+ if isinstance(value, Model):
94
+ value = value.pk
95
+ self.site_id = value
96
+
97
+ def reset(self):
98
+ self.site_id = None
99
+
100
+ def get_default(self):
101
+ """Returns the default SITE_ID."""
102
+ if self.default is None:
103
+ raise ValueError("SITE_ID has not been set.")
104
+ return self.default
105
+
106
+
107
+ class SiteDomain(SiteID):
108
+ def __init__(self, default, *args, **kwargs):
109
+ """
110
+ ``default`` is the default domain name, resolved to SITE_ID, if
111
+ that is unset.
112
+ """
113
+ # make sure they passed us a string; doing this is the single
114
+ # hardest py2/py3 compat headache.
115
+ # http://python-future.org/compatible_idioms.html#basestring and
116
+ # https://github.com/PythonCharmers/python-future/blob/master/src/past/types/basestring.py
117
+ # are not super informative, so just fall back on a literal version check:
118
+ if not isinstance(default, str):
119
+ raise TypeError("%r is not a valid default domain." % default)
120
+ self.default_domain = default
121
+ self.default = None
122
+ self.reset()
123
+
124
+ def get_default(self):
125
+ """Returns the default SITE_ID that matches the default domain name."""
126
+ from django.contrib.sites.models import Site
127
+
128
+ if "django.contrib.sites" not in settings.INSTALLED_APPS:
129
+ raise ImproperlyConfigured(
130
+ "django.contrib.sites is not in settings.INSTALLED_APPS"
131
+ )
132
+
133
+ if self.default is None:
134
+ qset = Site.objects.only("id")
135
+ self.default = qset.get(domain=self.default_domain).id
136
+ return self.default