plain 0.4.1__py3-none-any.whl → 0.11.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.
- plain/cli/cli.py +36 -13
- plain/csrf/middleware.py +23 -40
- plain/forms/fields.py +2 -3
- plain/forms/forms.py +2 -1
- plain/http/request.py +5 -5
- plain/internal/handlers/base.py +13 -1
- plain/internal/middleware/headers.py +19 -0
- plain/internal/middleware/https.py +36 -0
- plain/{middleware/common.py → internal/middleware/slash.py} +2 -25
- plain/preflight/security/base.py +5 -174
- plain/preflight/security/csrf.py +1 -5
- plain/runtime/README.md +0 -4
- plain/runtime/__init__.py +13 -15
- plain/runtime/global_settings.py +38 -55
- plain/runtime/user_settings.py +226 -217
- plain/signing.py +5 -23
- plain/test/client.py +17 -17
- plain/utils/timezone.py +2 -23
- plain/views/base.py +4 -0
- {plain-0.4.1.dist-info → plain-0.11.0.dist-info}/METADATA +2 -2
- {plain-0.4.1.dist-info → plain-0.11.0.dist-info}/RECORD +25 -27
- {plain-0.4.1.dist-info → plain-0.11.0.dist-info}/WHEEL +1 -1
- plain/middleware/README.md +0 -3
- plain/middleware/clickjacking.py +0 -52
- plain/middleware/gzip.py +0 -64
- plain/middleware/security.py +0 -64
- /plain/{middleware → internal/middleware}/__init__.py +0 -0
- {plain-0.4.1.dist-info → plain-0.11.0.dist-info}/LICENSE +0 -0
- {plain-0.4.1.dist-info → plain-0.11.0.dist-info}/entry_points.txt +0 -0
plain/test/client.py
CHANGED
@@ -379,7 +379,7 @@ class RequestFactory:
|
|
379
379
|
# Refs comment in `get_bytes_from_wsgi()`.
|
380
380
|
return path.decode("iso-8859-1")
|
381
381
|
|
382
|
-
def get(self, path, data=None, secure=
|
382
|
+
def get(self, path, data=None, secure=True, *, headers=None, **extra):
|
383
383
|
"""Construct a GET request."""
|
384
384
|
data = {} if data is None else data
|
385
385
|
return self.generic(
|
@@ -398,7 +398,7 @@ class RequestFactory:
|
|
398
398
|
path,
|
399
399
|
data=None,
|
400
400
|
content_type=MULTIPART_CONTENT,
|
401
|
-
secure=
|
401
|
+
secure=True,
|
402
402
|
*,
|
403
403
|
headers=None,
|
404
404
|
**extra,
|
@@ -417,7 +417,7 @@ class RequestFactory:
|
|
417
417
|
**extra,
|
418
418
|
)
|
419
419
|
|
420
|
-
def head(self, path, data=None, secure=
|
420
|
+
def head(self, path, data=None, secure=True, *, headers=None, **extra):
|
421
421
|
"""Construct a HEAD request."""
|
422
422
|
data = {} if data is None else data
|
423
423
|
return self.generic(
|
@@ -431,7 +431,7 @@ class RequestFactory:
|
|
431
431
|
},
|
432
432
|
)
|
433
433
|
|
434
|
-
def trace(self, path, secure=
|
434
|
+
def trace(self, path, secure=True, *, headers=None, **extra):
|
435
435
|
"""Construct a TRACE request."""
|
436
436
|
return self.generic("TRACE", path, secure=secure, headers=headers, **extra)
|
437
437
|
|
@@ -440,7 +440,7 @@ class RequestFactory:
|
|
440
440
|
path,
|
441
441
|
data="",
|
442
442
|
content_type="application/octet-stream",
|
443
|
-
secure=
|
443
|
+
secure=True,
|
444
444
|
*,
|
445
445
|
headers=None,
|
446
446
|
**extra,
|
@@ -455,7 +455,7 @@ class RequestFactory:
|
|
455
455
|
path,
|
456
456
|
data="",
|
457
457
|
content_type="application/octet-stream",
|
458
|
-
secure=
|
458
|
+
secure=True,
|
459
459
|
*,
|
460
460
|
headers=None,
|
461
461
|
**extra,
|
@@ -471,7 +471,7 @@ class RequestFactory:
|
|
471
471
|
path,
|
472
472
|
data="",
|
473
473
|
content_type="application/octet-stream",
|
474
|
-
secure=
|
474
|
+
secure=True,
|
475
475
|
*,
|
476
476
|
headers=None,
|
477
477
|
**extra,
|
@@ -487,7 +487,7 @@ class RequestFactory:
|
|
487
487
|
path,
|
488
488
|
data="",
|
489
489
|
content_type="application/octet-stream",
|
490
|
-
secure=
|
490
|
+
secure=True,
|
491
491
|
*,
|
492
492
|
headers=None,
|
493
493
|
**extra,
|
@@ -504,7 +504,7 @@ class RequestFactory:
|
|
504
504
|
path,
|
505
505
|
data="",
|
506
506
|
content_type="application/octet-stream",
|
507
|
-
secure=
|
507
|
+
secure=True,
|
508
508
|
*,
|
509
509
|
headers=None,
|
510
510
|
**extra,
|
@@ -704,7 +704,7 @@ class Client(ClientMixin, RequestFactory):
|
|
704
704
|
path,
|
705
705
|
data=None,
|
706
706
|
follow=False,
|
707
|
-
secure=
|
707
|
+
secure=True,
|
708
708
|
*,
|
709
709
|
headers=None,
|
710
710
|
**extra,
|
@@ -725,7 +725,7 @@ class Client(ClientMixin, RequestFactory):
|
|
725
725
|
data=None,
|
726
726
|
content_type=MULTIPART_CONTENT,
|
727
727
|
follow=False,
|
728
|
-
secure=
|
728
|
+
secure=True,
|
729
729
|
*,
|
730
730
|
headers=None,
|
731
731
|
**extra,
|
@@ -752,7 +752,7 @@ class Client(ClientMixin, RequestFactory):
|
|
752
752
|
path,
|
753
753
|
data=None,
|
754
754
|
follow=False,
|
755
|
-
secure=
|
755
|
+
secure=True,
|
756
756
|
*,
|
757
757
|
headers=None,
|
758
758
|
**extra,
|
@@ -775,7 +775,7 @@ class Client(ClientMixin, RequestFactory):
|
|
775
775
|
data="",
|
776
776
|
content_type="application/octet-stream",
|
777
777
|
follow=False,
|
778
|
-
secure=
|
778
|
+
secure=True,
|
779
779
|
*,
|
780
780
|
headers=None,
|
781
781
|
**extra,
|
@@ -803,7 +803,7 @@ class Client(ClientMixin, RequestFactory):
|
|
803
803
|
data="",
|
804
804
|
content_type="application/octet-stream",
|
805
805
|
follow=False,
|
806
|
-
secure=
|
806
|
+
secure=True,
|
807
807
|
*,
|
808
808
|
headers=None,
|
809
809
|
**extra,
|
@@ -831,7 +831,7 @@ class Client(ClientMixin, RequestFactory):
|
|
831
831
|
data="",
|
832
832
|
content_type="application/octet-stream",
|
833
833
|
follow=False,
|
834
|
-
secure=
|
834
|
+
secure=True,
|
835
835
|
*,
|
836
836
|
headers=None,
|
837
837
|
**extra,
|
@@ -859,7 +859,7 @@ class Client(ClientMixin, RequestFactory):
|
|
859
859
|
data="",
|
860
860
|
content_type="application/octet-stream",
|
861
861
|
follow=False,
|
862
|
-
secure=
|
862
|
+
secure=True,
|
863
863
|
*,
|
864
864
|
headers=None,
|
865
865
|
**extra,
|
@@ -886,7 +886,7 @@ class Client(ClientMixin, RequestFactory):
|
|
886
886
|
path,
|
887
887
|
data="",
|
888
888
|
follow=False,
|
889
|
-
secure=
|
889
|
+
secure=True,
|
890
890
|
*,
|
891
891
|
headers=None,
|
892
892
|
**extra,
|
plain/utils/timezone.py
CHANGED
@@ -138,27 +138,6 @@ class override(ContextDecorator):
|
|
138
138
|
_active.value = self.old_timezone
|
139
139
|
|
140
140
|
|
141
|
-
# Templates
|
142
|
-
|
143
|
-
|
144
|
-
def template_localtime(value, use_tz=None):
|
145
|
-
"""
|
146
|
-
Check if value is a datetime and converts it to local time if necessary.
|
147
|
-
|
148
|
-
If use_tz is provided and is not None, that will force the value to
|
149
|
-
be converted (or not), overriding the value of settings.USE_TZ.
|
150
|
-
|
151
|
-
This function is designed for use by the template engine.
|
152
|
-
"""
|
153
|
-
should_convert = (
|
154
|
-
isinstance(value, datetime)
|
155
|
-
and (settings.USE_TZ if use_tz is None else use_tz)
|
156
|
-
and not is_naive(value)
|
157
|
-
and getattr(value, "convert_to_local_time", True)
|
158
|
-
)
|
159
|
-
return localtime(value) if should_convert else value
|
160
|
-
|
161
|
-
|
162
141
|
# Utilities
|
163
142
|
|
164
143
|
|
@@ -184,9 +163,9 @@ def localtime(value=None, timezone=None):
|
|
184
163
|
|
185
164
|
def now():
|
186
165
|
"""
|
187
|
-
Return
|
166
|
+
Return a timezone aware datetime.
|
188
167
|
"""
|
189
|
-
return datetime.now(tz=timezone.utc
|
168
|
+
return datetime.now(tz=timezone.utc)
|
190
169
|
|
191
170
|
|
192
171
|
# By design, these four functions don't perform any checks on their arguments.
|
plain/views/base.py
CHANGED
@@ -15,6 +15,10 @@ logger = logging.getLogger("plain.request")
|
|
15
15
|
|
16
16
|
|
17
17
|
class View:
|
18
|
+
request: HttpRequest
|
19
|
+
url_args: tuple
|
20
|
+
url_kwargs: dict
|
21
|
+
|
18
22
|
def __init__(self, *args, **kwargs) -> None:
|
19
23
|
# Views can customize their init, which receives
|
20
24
|
# the args and kwargs from as_view()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: plain
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.11.0
|
4
4
|
Summary: A web framework for building products with Python.
|
5
5
|
Author: Dave Gaeddert
|
6
6
|
Author-email: dave.gaeddert@dropseed.dev
|
@@ -8,9 +8,9 @@ Requires-Python: >=3.11,<4.0
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
10
10
|
Classifier: Programming Language :: Python :: 3.12
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
11
12
|
Requires-Dist: click (>=8.0.0)
|
12
13
|
Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
|
13
|
-
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
14
14
|
Description-Content-Type: text/markdown
|
15
15
|
|
16
16
|
<!-- This file is compiled from plain/plain/README.md. Do not edit this file directly. -->
|
@@ -9,13 +9,13 @@ plain/assets/urls.py,sha256=ZTIoM1Zq35JaXZ3wFhXhfGa7VoITDNlH9i5RS0R5xow,933
|
|
9
9
|
plain/assets/views.py,sha256=dhjIpMu0GDR_VGbXM90_6RnC84C2C4bFv1RxDVklGBk,9173
|
10
10
|
plain/cli/README.md,sha256=xjr1K-sIMTi5OWxdxL--O7aoo16Pd1xdawIZtz6BL7Q,2464
|
11
11
|
plain/cli/__init__.py,sha256=9ByBOIdM8DebChjNz-RH2atdz4vWe8somlwNEsbhwh4,40
|
12
|
-
plain/cli/cli.py,sha256=
|
12
|
+
plain/cli/cli.py,sha256=Ns6Yi3fhrvvBsE-HrhqtaFoLiegni7d61ypj-MrLjss,14834
|
13
13
|
plain/cli/formatting.py,sha256=1hZH13y1qwHcU2K2_Na388nw9uvoeQH8LrWL-O9h8Yc,2207
|
14
14
|
plain/cli/packages.py,sha256=69VH1bIi1-5N5l2jlBcR5EP0pt-v16sPar9arO3gCSE,2052
|
15
15
|
plain/cli/print.py,sha256=XraUYrgODOJquIiEv78wSCYGRBplHXtXSS9QtFG5hqY,217
|
16
16
|
plain/cli/startup.py,sha256=PJYA-tNWGia-QbTlT0e5HvC8C7yDSq8wkAkIxgfKkvw,680
|
17
17
|
plain/csrf/README.md,sha256=RXMWMtHmzf30gVVNOfj0kD4xlSqFIPgJh-n7dIciaEM,163
|
18
|
-
plain/csrf/middleware.py,sha256=
|
18
|
+
plain/csrf/middleware.py,sha256=MlDQ55B4eRXySbzauFNs8gKhgQy32yWspBfPI0a3PzA,17775
|
19
19
|
plain/csrf/views.py,sha256=YDgT451X16iUdCxpQ6rcHIy7nD0u7DAvCQl5-Mx5i9Y,219
|
20
20
|
plain/debug.py,sha256=fdrWy4RNQOuXo80_jgwthCkMZKjjaF9lDj3Kqln_gJk,604
|
21
21
|
plain/exceptions.py,sha256=tDS6l0epe_L9IlxpEdT2k2hWgEoAu8YBNIumNCtJ-WY,6333
|
@@ -23,13 +23,13 @@ plain/forms/README.md,sha256=fglB9MmHiEgfGGdZmcRstNl6eYaFljrElu2mzapK52M,377
|
|
23
23
|
plain/forms/__init__.py,sha256=UxqPwB8CiYPCQdHmUc59jadqaXqDmXBH8y4bt9vTPms,226
|
24
24
|
plain/forms/boundfield.py,sha256=LhydhCVR0okrli0-QBMjGjAJ8-06gTCXVEaBZhBouQk,1741
|
25
25
|
plain/forms/exceptions.py,sha256=XCLDRl5snIEDu5-8mLB0NnU_tegcBfyIHMiJxqvbxnc,164
|
26
|
-
plain/forms/fields.py,sha256=
|
27
|
-
plain/forms/forms.py,sha256
|
26
|
+
plain/forms/fields.py,sha256=86ZE9jac6Zyg5vKsYGgyOUOIQLKxO--UomGXwA65tk4,35103
|
27
|
+
plain/forms/forms.py,sha256=-EcS2QVpAy4H95Y-RL108LnWnHLSyCGgEnUCdIIXnjg,10451
|
28
28
|
plain/http/README.md,sha256=00zLFQ-FPjYXu3A8QsLhCCXxaT0ImvI5I-8xd3dp8WA,7
|
29
29
|
plain/http/__init__.py,sha256=DIsDRbBsCGa4qZgq-fUuQS0kkxfbTU_3KpIM9VvH04w,1067
|
30
30
|
plain/http/cookie.py,sha256=11FnSG3Plo6T3jZDbPoCw7SKh9ExdBio3pTmIO03URg,597
|
31
31
|
plain/http/multipartparser.py,sha256=Z2PFDuGucj_nFnQagwdxowJcZHqzCfDApkXl5yRlRe4,27325
|
32
|
-
plain/http/request.py,sha256=
|
32
|
+
plain/http/request.py,sha256=kOXN9uhgtgbd1IC25-oRupYlCofacE1jyoDZRlg2v5k,25990
|
33
33
|
plain/http/response.py,sha256=h43Gx4PVPGEf63EHHrABYtwYu-8Y9mgAebwiGt8qeLE,24074
|
34
34
|
plain/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
35
35
|
plain/internal/files/README.md,sha256=kMux-NU5qiH0o1K8IajYQT8VjrYl_jLk9LkGG_kGuSc,45
|
@@ -42,21 +42,19 @@ plain/internal/files/uploadedfile.py,sha256=JRB7T3quQjg-1y3l1ASPxywtSQZhaeMc45uF
|
|
42
42
|
plain/internal/files/uploadhandler.py,sha256=BZGQDHJMEUeBh9uJtxNVWQkFmHE7jzVTx9CLVt59Jqg,7197
|
43
43
|
plain/internal/files/utils.py,sha256=XrHAs2tMqmywURgz5C6-GSj6sr2R-MCERcWT8yzBp5k,2652
|
44
44
|
plain/internal/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
45
|
-
plain/internal/handlers/base.py,sha256=
|
45
|
+
plain/internal/handlers/base.py,sha256=tpTrVhC_gZKrIoTJmCWD3bIpucOCGVV1DTkF0W2HZPI,4883
|
46
46
|
plain/internal/handlers/exception.py,sha256=KUZSBzmzE6YSFxAZ336Mye_9vAPVIj9Av-w1SK5R4PA,4579
|
47
47
|
plain/internal/handlers/wsgi.py,sha256=WIZvXlEAOn8lxwDM_HpSP82-ePKVu-Tzgpe65KkXEMk,7538
|
48
|
+
plain/internal/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
|
+
plain/internal/middleware/headers.py,sha256=UnJWnlVVLD-10h7PB_QSYeREBzLo-TS3C-_ahmZ6w0I,636
|
50
|
+
plain/internal/middleware/https.py,sha256=XpuQK8HicYX1jNanQHqNgyQ9rqe4NLUOZO3ZzKdsP8k,1203
|
51
|
+
plain/internal/middleware/slash.py,sha256=LhQi5aUztE4kJnvRn75u8zaFvAVPPEl_Whu1gYWGs7g,2656
|
48
52
|
plain/json.py,sha256=McJdsbMT1sYwkGRG--f2NSZz0hVXPMix9x3nKaaak2o,1262
|
49
53
|
plain/logs/README.md,sha256=H6uVXdInYlasq0Z1WnhWnPmNwYQoZ1MSLPDQ4ZE7u4A,492
|
50
54
|
plain/logs/__init__.py,sha256=rASvo4qFBDIHfkACmGLNGa6lRGbG9PbNjW6FmBt95ys,168
|
51
55
|
plain/logs/configure.py,sha256=6mV7d1IxkDYT3VBz61qhIj0Esuy5l5QdQfsHaGCfI6w,1063
|
52
56
|
plain/logs/loggers.py,sha256=iz9SYcwP9w5QAuwpULl48SFkVyJuuMoQ_fdLgdCHpNg,2121
|
53
57
|
plain/logs/utils.py,sha256=9UzdCCQXJinGDs71Ngw297mlWkhgZStSd67ya4NOW98,1257
|
54
|
-
plain/middleware/README.md,sha256=MgiLHwAfP8ooBSlDi1JhTwIHMlwphOqAkeWglYRbe8s,52
|
55
|
-
plain/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
|
-
plain/middleware/clickjacking.py,sha256=MJOHWSDqJB8K6YE6XTh34uyr2LNKuE9XsywZRRsljFk,1764
|
57
|
-
plain/middleware/common.py,sha256=-YySkYUyaRujYA5Yg7GRD3xFjlQOZpeJP1Stpt6pias,3631
|
58
|
-
plain/middleware/gzip.py,sha256=2NogLO6hPxVc3otxkhMDl7-r2Zw3vcIkAP29fx4j2eU,2383
|
59
|
-
plain/middleware/security.py,sha256=r9UatFEaKVL1eZ5AxAuVX9uf5eLKwImEZmjL2t1slaY,2477
|
60
58
|
plain/packages/README.md,sha256=Vq1Nw3mmEmZ2IriQavuVi4BjcQC2nb8k7YIbnm8QjIg,799
|
61
59
|
plain/packages/__init__.py,sha256=DnHN1wwHXiXib4Y9BV__x9WrbUaTovoTIxW-tVyScTU,106
|
62
60
|
plain/packages/config.py,sha256=6Vdf1TEQllZkkEvK0WK__zHJYT9nxmS3EyYrbuq0GkM,11201
|
@@ -68,19 +66,19 @@ plain/preflight/files.py,sha256=wbHCNgps7o1c1zQNBd8FDCaVaqX90UwuvLgEQ_DbUpY,510
|
|
68
66
|
plain/preflight/messages.py,sha256=u0oc7q7YmBlKYJRcF5SQpzncfOkEzDhZTcpyclQDfHg,2427
|
69
67
|
plain/preflight/registry.py,sha256=ZpxnZPIklXuT8xZVTxCUp_IER3zhd7DdfsmqIpAbLj4,2306
|
70
68
|
plain/preflight/security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
71
|
-
plain/preflight/security/base.py,sha256=
|
72
|
-
plain/preflight/security/csrf.py,sha256=
|
69
|
+
plain/preflight/security/base.py,sha256=nsv-g-bFr_188mkOQwC1ZDnyS0rE6eZED8xZT-FEM8M,3074
|
70
|
+
plain/preflight/security/csrf.py,sha256=8dKzs5kQwTTKeyfHbkrzdPk3OEoUN8mc-0xhSBo1KmM,1175
|
73
71
|
plain/preflight/urls.py,sha256=O4PQ_v205VA2872fQlhPfxaihDDRCsVp0ZVKQ92aX4k,3019
|
74
|
-
plain/runtime/README.md,sha256=
|
75
|
-
plain/runtime/__init__.py,sha256=
|
76
|
-
plain/runtime/global_settings.py,sha256=
|
77
|
-
plain/runtime/user_settings.py,sha256
|
72
|
+
plain/runtime/README.md,sha256=Q8VVO7JRGuYrDxzuYL6ptoilhclbecxKzpRXKgbWGkU,2061
|
73
|
+
plain/runtime/__init__.py,sha256=DH8TwKTGJhjviOy4yh_d051v8YGaAWMlFBPhK8ZuC9g,1499
|
74
|
+
plain/runtime/global_settings.py,sha256=_FaHDQjtLDoRCTv1-2EEA8GZWCiCVPJHIm_O7OxwrsU,5554
|
75
|
+
plain/runtime/user_settings.py,sha256=-1xXUggueuOF3YlgnLfeyG55CUvR3azOGWr2UkTOmfs,11259
|
78
76
|
plain/signals/README.md,sha256=cd3tKEgH-xc88CUWyDxl4-qv-HBXx8VT32BXVwA5azA,230
|
79
77
|
plain/signals/__init__.py,sha256=eAs0kLqptuP6I31dWXeAqRNji3svplpAV4Ez6ktjwXM,131
|
80
78
|
plain/signals/dispatch/__init__.py,sha256=FzEygqV9HsM6gopio7O2Oh_X230nA4d5Q9s0sUjMq0E,292
|
81
79
|
plain/signals/dispatch/dispatcher.py,sha256=VxSlqn9PCOTghPPJLOqZPs6FNQZfV2BJpMfFMSg6Dtc,11531
|
82
80
|
plain/signals/dispatch/license.txt,sha256=o9EhDhsC4Q5HbmD-IfNGVTEkXtNE33r5rIt3lleJ8gc,1727
|
83
|
-
plain/signing.py,sha256=
|
81
|
+
plain/signing.py,sha256=V6A6PTDYWekuwtQRI1iFD8dud5OHPZTv4EkeoZEHoXo,8737
|
84
82
|
plain/templates/README.md,sha256=VfA2HmrklG5weE1md85q9g84cWnMBEiXAynKzM7S1Sk,464
|
85
83
|
plain/templates/__init__.py,sha256=Jh1jit55UR4dRpklQ6qAN2ixzYZBVoDi0AOdfD4Nh4E,106
|
86
84
|
plain/templates/core.py,sha256=iw58EAmyyv8N5HDA-Sq4-fLgz_qx8v8WJfurgR116jw,625
|
@@ -92,7 +90,7 @@ plain/templates/jinja/filters.py,sha256=3KJKKbxcv9dLzUDWPcaa88k3NU2m1GG3iMIgFhzX
|
|
92
90
|
plain/templates/jinja/globals.py,sha256=qhvQuikkRkOTpHSW5FwdsvoViJNlRgHq3-O7ZyeajsE,669
|
93
91
|
plain/test/README.md,sha256=Zso3Ir7a8vQerzKB6egjROQWkpveLAbscn7VTROPAiU,37
|
94
92
|
plain/test/__init__.py,sha256=rXe88Y602NP8DBnReSyXb7dUzKoWweLuT43j-qwOUl4,138
|
95
|
-
plain/test/client.py,sha256=
|
93
|
+
plain/test/client.py,sha256=470yny2wfLEebdVjQckBqC9pqyDkHy8e0EH-rlVjsAQ,31368
|
96
94
|
plain/urls/README.md,sha256=pWnCvgYkWN7rG7hSyBOtX4ZUP3iO7FhqM6lvwwYll6c,33
|
97
95
|
plain/urls/__init__.py,sha256=3UzwIufXjIks2K_X_Vms2MV19IqvyPLrXUeHU3WP47c,753
|
98
96
|
plain/urls/base.py,sha256=ECaOCEXs1ygKn4k1mt5XxSNPNlg5raJvx0aPaj7DFfE,3719
|
@@ -129,12 +127,12 @@ plain/utils/safestring.py,sha256=SHGhpbX6FFDKSYOY9zYAgAQX0g0exzRba7dM2bJalWs,187
|
|
129
127
|
plain/utils/termcolors.py,sha256=78MimQMp4Etoh1X1lokOJ6ucxErHtg8z9rxeTtV5nhk,7394
|
130
128
|
plain/utils/text.py,sha256=QxhJsk_4VrNVUtwwo0DXGTMHJ1x_hrKOqJOxlPB33qc,16596
|
131
129
|
plain/utils/timesince.py,sha256=essdb0XWBKWmKtIprs-4rO0qKTtsFqZ0Fwn-RTDyhOc,4758
|
132
|
-
plain/utils/timezone.py,sha256=
|
130
|
+
plain/utils/timezone.py,sha256=AZ7lcmUjofUTfQUb08pHXu0u7TDuPJpMRB5lgvE4E0w,6212
|
133
131
|
plain/utils/tree.py,sha256=wdWzmfsgc26YDF2wxhAY3yVxXTixQYqYDKE9mL3L3ZY,4383
|
134
132
|
plain/validators.py,sha256=L9v9KtTe4iZhZVramZdKGf33R5Tt95FCdg2AJD2-2n0,19963
|
135
133
|
plain/views/README.md,sha256=qndsXKyNMnipPlLaAvgQeGxqXknNQwlFh31Yxk8rHp8,5994
|
136
134
|
plain/views/__init__.py,sha256=a-N1nkklVohJTtz0yD1MMaS0g66HviEjsKydNVVjvVc,392
|
137
|
-
plain/views/base.py,sha256=
|
135
|
+
plain/views/base.py,sha256=wMkCAbr3XqXyP8dJr-O9atA1-N6K4-cTFflLhSYGOpY,3227
|
138
136
|
plain/views/csrf.py,sha256=gO9npd_Ut_LoYF_u7Qb_ZsPRfSeE3aTPG97XlMp4oEo,724
|
139
137
|
plain/views/errors.py,sha256=Y4oGX4Z6D2COKcDEfINvXE1acE8Ad15KwNNWPs5BCfc,967
|
140
138
|
plain/views/exceptions.py,sha256=b4euI49ZUKS9O8AGAcFfiDpstzkRAuuj_uYQXzWNHME,138
|
@@ -143,8 +141,8 @@ plain/views/objects.py,sha256=9QBYyb8PgkRirXCQ8-Pms4_yMzP37dfeL30hWRYmtZg,7909
|
|
143
141
|
plain/views/redirect.py,sha256=KLnlktzK6ZNMTlaEiZpMKQMEP5zeTgGLJ9BIkIJfwBo,1733
|
144
142
|
plain/views/templates.py,sha256=nF9CcdhhjAyp3LB0RrSYnBaHpHzMfPSw719RCdcXk7o,2007
|
145
143
|
plain/wsgi.py,sha256=R6k5FiAElvGDApEbMPTT0MPqSD7n2e2Az5chQqJZU0I,236
|
146
|
-
plain-0.
|
147
|
-
plain-0.
|
148
|
-
plain-0.
|
149
|
-
plain-0.
|
150
|
-
plain-0.
|
144
|
+
plain-0.11.0.dist-info/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
|
145
|
+
plain-0.11.0.dist-info/METADATA,sha256=tRnc7WP5pznuQMZQYEQXwyhi6cLywdRkcSikq-Vu9QI,2722
|
146
|
+
plain-0.11.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
147
|
+
plain-0.11.0.dist-info/entry_points.txt,sha256=7O1RZTmMasKYB73bfqQcTwIhsXo7RjEIKv2WbtTtOIM,39
|
148
|
+
plain-0.11.0.dist-info/RECORD,,
|
plain/middleware/README.md
DELETED
plain/middleware/clickjacking.py
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Clickjacking Protection Middleware.
|
3
|
-
|
4
|
-
This module provides a middleware that implements protection against a
|
5
|
-
malicious site loading resources from your site in a hidden frame.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from plain.runtime import settings
|
9
|
-
|
10
|
-
|
11
|
-
class XFrameOptionsMiddleware:
|
12
|
-
"""
|
13
|
-
Set the X-Frame-Options HTTP header in HTTP responses.
|
14
|
-
|
15
|
-
Do not set the header if it's already set or if the response contains
|
16
|
-
a xframe_options_exempt value set to True.
|
17
|
-
|
18
|
-
By default, set the X-Frame-Options header to 'DENY', meaning the response
|
19
|
-
cannot be displayed in a frame, regardless of the site attempting to do so.
|
20
|
-
To enable the response to be loaded on a frame within the same site, set
|
21
|
-
X_FRAME_OPTIONS in your project's Plain settings to 'SAMEORIGIN'.
|
22
|
-
"""
|
23
|
-
|
24
|
-
def __init__(self, get_response):
|
25
|
-
self.get_response = get_response
|
26
|
-
|
27
|
-
def __call__(self, request):
|
28
|
-
response = self.get_response(request)
|
29
|
-
|
30
|
-
# Don't set it if it's already in the response
|
31
|
-
if response.get("X-Frame-Options") is not None:
|
32
|
-
return response
|
33
|
-
|
34
|
-
# Don't set it if they used @xframe_options_exempt
|
35
|
-
if getattr(response, "xframe_options_exempt", False):
|
36
|
-
return response
|
37
|
-
|
38
|
-
response.headers["X-Frame-Options"] = self.get_xframe_options_value(
|
39
|
-
request,
|
40
|
-
response,
|
41
|
-
)
|
42
|
-
return response
|
43
|
-
|
44
|
-
def get_xframe_options_value(self, request, response):
|
45
|
-
"""
|
46
|
-
Get the value to set for the X_FRAME_OPTIONS header. Use the value from
|
47
|
-
the X_FRAME_OPTIONS setting, or 'DENY' if not set.
|
48
|
-
|
49
|
-
This method can be overridden if needed, allowing it to vary based on
|
50
|
-
the request or response.
|
51
|
-
"""
|
52
|
-
return getattr(settings, "X_FRAME_OPTIONS", "DENY").upper()
|
plain/middleware/gzip.py
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
from plain.utils.cache import patch_vary_headers
|
2
|
-
from plain.utils.regex_helper import _lazy_re_compile
|
3
|
-
from plain.utils.text import compress_sequence, compress_string
|
4
|
-
|
5
|
-
re_accepts_gzip = _lazy_re_compile(r"\bgzip\b")
|
6
|
-
|
7
|
-
|
8
|
-
class GZipMiddleware:
|
9
|
-
"""
|
10
|
-
Compress content if the browser allows gzip compression.
|
11
|
-
Set the Vary header accordingly, so that caches will base their storage
|
12
|
-
on the Accept-Encoding header.
|
13
|
-
"""
|
14
|
-
|
15
|
-
max_random_bytes = 100
|
16
|
-
|
17
|
-
def __init__(self, get_response):
|
18
|
-
self.get_response = get_response
|
19
|
-
|
20
|
-
def __call__(self, request):
|
21
|
-
response = self.get_response(request)
|
22
|
-
|
23
|
-
# It's not worth attempting to compress really short responses.
|
24
|
-
if not response.streaming and len(response.content) < 200:
|
25
|
-
return response
|
26
|
-
|
27
|
-
# Avoid gzipping if we've already got a content-encoding.
|
28
|
-
if response.has_header("Content-Encoding"):
|
29
|
-
return response
|
30
|
-
|
31
|
-
patch_vary_headers(response, ("Accept-Encoding",))
|
32
|
-
|
33
|
-
ae = request.META.get("HTTP_ACCEPT_ENCODING", "")
|
34
|
-
if not re_accepts_gzip.search(ae):
|
35
|
-
return response
|
36
|
-
|
37
|
-
if response.streaming:
|
38
|
-
response.streaming_content = compress_sequence(
|
39
|
-
response.streaming_content,
|
40
|
-
max_random_bytes=self.max_random_bytes,
|
41
|
-
)
|
42
|
-
# Delete the `Content-Length` header for streaming content, because
|
43
|
-
# we won't know the compressed size until we stream it.
|
44
|
-
del response.headers["Content-Length"]
|
45
|
-
else:
|
46
|
-
# Return the compressed content only if it's actually shorter.
|
47
|
-
compressed_content = compress_string(
|
48
|
-
response.content,
|
49
|
-
max_random_bytes=self.max_random_bytes,
|
50
|
-
)
|
51
|
-
if len(compressed_content) >= len(response.content):
|
52
|
-
return response
|
53
|
-
response.content = compressed_content
|
54
|
-
response.headers["Content-Length"] = str(len(response.content))
|
55
|
-
|
56
|
-
# If there is a strong ETag, make it weak to fulfill the requirements
|
57
|
-
# of RFC 9110 Section 8.8.1 while also allowing conditional request
|
58
|
-
# matches on ETags.
|
59
|
-
etag = response.get("ETag")
|
60
|
-
if etag and etag.startswith('"'):
|
61
|
-
response.headers["ETag"] = "W/" + etag
|
62
|
-
response.headers["Content-Encoding"] = "gzip"
|
63
|
-
|
64
|
-
return response
|
plain/middleware/security.py
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
|
3
|
-
from plain.http import ResponsePermanentRedirect
|
4
|
-
from plain.runtime import settings
|
5
|
-
|
6
|
-
|
7
|
-
class SecurityMiddleware:
|
8
|
-
def __init__(self, get_response):
|
9
|
-
self.get_response = get_response
|
10
|
-
self.sts_seconds = settings.SECURE_HSTS_SECONDS
|
11
|
-
self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
|
12
|
-
self.sts_preload = settings.SECURE_HSTS_PRELOAD
|
13
|
-
self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
|
14
|
-
self.redirect = settings.SECURE_SSL_REDIRECT
|
15
|
-
self.redirect_host = settings.SECURE_SSL_HOST
|
16
|
-
self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
|
17
|
-
self.referrer_policy = settings.SECURE_REFERRER_POLICY
|
18
|
-
self.cross_origin_opener_policy = settings.SECURE_CROSS_ORIGIN_OPENER_POLICY
|
19
|
-
|
20
|
-
def __call__(self, request):
|
21
|
-
path = request.path.lstrip("/")
|
22
|
-
if (
|
23
|
-
self.redirect
|
24
|
-
and not request.is_secure()
|
25
|
-
and not any(pattern.search(path) for pattern in self.redirect_exempt)
|
26
|
-
):
|
27
|
-
host = self.redirect_host or request.get_host()
|
28
|
-
return ResponsePermanentRedirect(f"https://{host}{request.get_full_path()}")
|
29
|
-
|
30
|
-
response = self.get_response(request)
|
31
|
-
|
32
|
-
if (
|
33
|
-
self.sts_seconds
|
34
|
-
and request.is_secure()
|
35
|
-
and "Strict-Transport-Security" not in response
|
36
|
-
):
|
37
|
-
sts_header = "max-age=%s" % self.sts_seconds
|
38
|
-
if self.sts_include_subdomains:
|
39
|
-
sts_header += "; includeSubDomains"
|
40
|
-
if self.sts_preload:
|
41
|
-
sts_header += "; preload"
|
42
|
-
response.headers["Strict-Transport-Security"] = sts_header
|
43
|
-
|
44
|
-
if self.content_type_nosniff:
|
45
|
-
response.headers.setdefault("X-Content-Type-Options", "nosniff")
|
46
|
-
|
47
|
-
if self.referrer_policy:
|
48
|
-
# Support a comma-separated string or iterable of values to allow
|
49
|
-
# fallback.
|
50
|
-
response.headers.setdefault(
|
51
|
-
"Referrer-Policy",
|
52
|
-
",".join(
|
53
|
-
[v.strip() for v in self.referrer_policy.split(",")]
|
54
|
-
if isinstance(self.referrer_policy, str)
|
55
|
-
else self.referrer_policy
|
56
|
-
),
|
57
|
-
)
|
58
|
-
|
59
|
-
if self.cross_origin_opener_policy:
|
60
|
-
response.setdefault(
|
61
|
-
"Cross-Origin-Opener-Policy",
|
62
|
-
self.cross_origin_opener_policy,
|
63
|
-
)
|
64
|
-
return response
|
File without changes
|
File without changes
|
File without changes
|