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/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=False, *, headers=None, **extra):
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=False,
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=False, *, headers=None, **extra):
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=False, *, headers=None, **extra):
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=False,
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=False,
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=False,
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=False,
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=False,
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=False,
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=False,
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=False,
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=False,
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=False,
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=False,
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=False,
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=False,
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 an aware or naive datetime.datetime, depending on settings.USE_TZ.
166
+ Return a timezone aware datetime.
188
167
  """
189
- return datetime.now(tz=timezone.utc if settings.USE_TZ else None)
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.4.1
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=z2BoAdb5IF_WpPdbSsS1lc2GMsCOrb2y-ZgUfa6NmaE,14416
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=I7Ev-Y-VaR-Fgs_arVYNePt4qjwF_PozFBCqER2UMIY,18642
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=nyL1-NLAIt2jvEhGt4VwUy7xsXrVuU_wMuMzRXVhleA,35178
27
- plain/forms/forms.py,sha256=QgIV4JDLXzM8wfLYVZUUNH5kmJlQYQVG6UAENrxFIBo,10357
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=sRHjXOrxFMPIXSbwa6-hXJHNhR-EF4E5IprGtHUqeyE,26007
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=LcKKRtKlBk17Iy_8MRbx7wskULpnhHzA0fOlunzd9Lo,4506
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=MjY1HXfUD4fsGz1kGs0eI2hpoSmD57x_30CMbLpw3h4,8445
72
- plain/preflight/security/csrf.py,sha256=EZy_DkVqc1kUmBA-UbNmhVsKhRINfmqgWSRlatKy5AA,1237
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=F5USETg445RX1RxC13y_1NrbOs3GHQI2Jl9idGdVFnk,2270
75
- plain/runtime/__init__.py,sha256=jKak9tK8MfcGNYOzqCOEmwSRAGOoji8r7e2IAwOymzE,1392
76
- plain/runtime/global_settings.py,sha256=PrJ5IAPk1nEr8U_XM3gLrItIVA4v2ig1t1bx_fV0NPE,6118
77
- plain/runtime/user_settings.py,sha256=3016w7YiOkG85L1Y5BsobgxgCOAaRbZzFaBadzmHSZg,11315
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=rCsizsluW-lt8ihwicf19kU-sOewMvkIbeI9MrFNwOw,9491
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=cu43S-NL606VERUYi7NjvzIrVchlNLHqcKro7pnYmS4,31385
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=tFd4NfQNwTiHKExNxTc0wWKUpDZximHlcN0Ykv-wQ6k,6910
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=WQy5btO4NNkEQAleII57oXUaDdd76a7OnFGdK7uFook,3160
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.4.1.dist-info/LICENSE,sha256=m0D5O7QoH9l5Vz_rrX_9r-C8d9UNr_ciK6Qwac7o6yo,3175
147
- plain-0.4.1.dist-info/METADATA,sha256=l7Q6qWK98LKLVsoQ4Fr5Apb5YimjLkIzhUwAO4PSjPE,2716
148
- plain-0.4.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
149
- plain-0.4.1.dist-info/entry_points.txt,sha256=7O1RZTmMasKYB73bfqQcTwIhsXo7RjEIKv2WbtTtOIM,39
150
- plain-0.4.1.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 1.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,3 +0,0 @@
1
- # Middleware
2
-
3
- Hook into the request/response cycle.
@@ -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
@@ -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