plain 0.43.1__tar.gz → 0.45.0__tar.gz

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 (164) hide show
  1. {plain-0.43.1 → plain-0.45.0}/.gitignore +2 -1
  2. {plain-0.43.1 → plain-0.45.0}/PKG-INFO +2 -2
  3. {plain-0.43.1 → plain-0.45.0}/plain/README.md +1 -1
  4. {plain-0.43.1 → plain-0.45.0}/plain/cli/scaffold.py +2 -0
  5. {plain-0.43.1 → plain-0.45.0}/plain/csrf/middleware.py +1 -1
  6. {plain-0.43.1 → plain-0.45.0}/plain/forms/fields.py +3 -0
  7. {plain-0.43.1 → plain-0.45.0}/plain/forms/forms.py +1 -1
  8. {plain-0.43.1 → plain-0.45.0}/plain/http/request.py +1 -1
  9. {plain-0.43.1 → plain-0.45.0}/plain/http/response.py +12 -2
  10. {plain-0.43.1 → plain-0.45.0}/plain/internal/files/base.py +1 -1
  11. {plain-0.43.1 → plain-0.45.0}/plain/internal/handlers/wsgi.py +2 -4
  12. {plain-0.43.1 → plain-0.45.0}/plain/paginator.py +1 -1
  13. {plain-0.43.1 → plain-0.45.0}/plain/runtime/global_settings.py +0 -6
  14. {plain-0.43.1 → plain-0.45.0}/plain/runtime/user_settings.py +8 -4
  15. {plain-0.43.1 → plain-0.45.0}/plain/signing.py +0 -16
  16. {plain-0.43.1 → plain-0.45.0}/plain/utils/connection.py +1 -1
  17. {plain-0.43.1 → plain-0.45.0}/plain/utils/functional.py +0 -43
  18. {plain-0.43.1 → plain-0.45.0}/pyproject.toml +1 -1
  19. plain-0.45.0/tests/test_cli.py +17 -0
  20. plain-0.43.1/tests/test_cli.py +0 -11
  21. {plain-0.43.1 → plain-0.45.0}/LICENSE +0 -0
  22. {plain-0.43.1 → plain-0.45.0}/README.md +0 -0
  23. {plain-0.43.1 → plain-0.45.0}/plain/__main__.py +0 -0
  24. {plain-0.43.1 → plain-0.45.0}/plain/assets/README.md +0 -0
  25. {plain-0.43.1 → plain-0.45.0}/plain/assets/__init__.py +0 -0
  26. {plain-0.43.1 → plain-0.45.0}/plain/assets/compile.py +0 -0
  27. {plain-0.43.1 → plain-0.45.0}/plain/assets/finders.py +0 -0
  28. {plain-0.43.1 → plain-0.45.0}/plain/assets/fingerprints.py +0 -0
  29. {plain-0.43.1 → plain-0.45.0}/plain/assets/urls.py +0 -0
  30. {plain-0.43.1 → plain-0.45.0}/plain/assets/views.py +0 -0
  31. {plain-0.43.1 → plain-0.45.0}/plain/chores/README.md +0 -0
  32. {plain-0.43.1 → plain-0.45.0}/plain/chores/__init__.py +0 -0
  33. {plain-0.43.1 → plain-0.45.0}/plain/chores/registry.py +0 -0
  34. {plain-0.43.1 → plain-0.45.0}/plain/cli/README.md +0 -0
  35. {plain-0.43.1 → plain-0.45.0}/plain/cli/__init__.py +0 -0
  36. {plain-0.43.1 → plain-0.45.0}/plain/cli/build.py +0 -0
  37. {plain-0.43.1 → plain-0.45.0}/plain/cli/chores.py +0 -0
  38. {plain-0.43.1 → plain-0.45.0}/plain/cli/core.py +0 -0
  39. {plain-0.43.1 → plain-0.45.0}/plain/cli/docs.py +0 -0
  40. {plain-0.43.1 → plain-0.45.0}/plain/cli/formatting.py +0 -0
  41. {plain-0.43.1 → plain-0.45.0}/plain/cli/preflight.py +0 -0
  42. {plain-0.43.1 → plain-0.45.0}/plain/cli/print.py +0 -0
  43. {plain-0.43.1 → plain-0.45.0}/plain/cli/registry.py +0 -0
  44. {plain-0.43.1 → plain-0.45.0}/plain/cli/settings.py +0 -0
  45. {plain-0.43.1 → plain-0.45.0}/plain/cli/shell.py +0 -0
  46. {plain-0.43.1 → plain-0.45.0}/plain/cli/startup.py +0 -0
  47. {plain-0.43.1 → plain-0.45.0}/plain/cli/urls.py +0 -0
  48. {plain-0.43.1 → plain-0.45.0}/plain/cli/utils.py +0 -0
  49. {plain-0.43.1 → plain-0.45.0}/plain/csrf/README.md +0 -0
  50. {plain-0.43.1 → plain-0.45.0}/plain/csrf/views.py +0 -0
  51. {plain-0.43.1 → plain-0.45.0}/plain/debug.py +0 -0
  52. {plain-0.43.1 → plain-0.45.0}/plain/exceptions.py +0 -0
  53. {plain-0.43.1 → plain-0.45.0}/plain/forms/README.md +0 -0
  54. {plain-0.43.1 → plain-0.45.0}/plain/forms/__init__.py +0 -0
  55. {plain-0.43.1 → plain-0.45.0}/plain/forms/boundfield.py +0 -0
  56. {plain-0.43.1 → plain-0.45.0}/plain/forms/exceptions.py +0 -0
  57. {plain-0.43.1 → plain-0.45.0}/plain/http/README.md +0 -0
  58. {plain-0.43.1 → plain-0.45.0}/plain/http/__init__.py +0 -0
  59. {plain-0.43.1 → plain-0.45.0}/plain/http/cookie.py +0 -0
  60. {plain-0.43.1 → plain-0.45.0}/plain/http/multipartparser.py +0 -0
  61. {plain-0.43.1 → plain-0.45.0}/plain/internal/__init__.py +0 -0
  62. {plain-0.43.1 → plain-0.45.0}/plain/internal/files/__init__.py +0 -0
  63. {plain-0.43.1 → plain-0.45.0}/plain/internal/files/locks.py +0 -0
  64. {plain-0.43.1 → plain-0.45.0}/plain/internal/files/move.py +0 -0
  65. {plain-0.43.1 → plain-0.45.0}/plain/internal/files/temp.py +0 -0
  66. {plain-0.43.1 → plain-0.45.0}/plain/internal/files/uploadedfile.py +0 -0
  67. {plain-0.43.1 → plain-0.45.0}/plain/internal/files/uploadhandler.py +0 -0
  68. {plain-0.43.1 → plain-0.45.0}/plain/internal/files/utils.py +0 -0
  69. {plain-0.43.1 → plain-0.45.0}/plain/internal/handlers/__init__.py +0 -0
  70. {plain-0.43.1 → plain-0.45.0}/plain/internal/handlers/base.py +0 -0
  71. {plain-0.43.1 → plain-0.45.0}/plain/internal/handlers/exception.py +0 -0
  72. {plain-0.43.1 → plain-0.45.0}/plain/internal/middleware/__init__.py +0 -0
  73. {plain-0.43.1 → plain-0.45.0}/plain/internal/middleware/headers.py +0 -0
  74. {plain-0.43.1 → plain-0.45.0}/plain/internal/middleware/https.py +0 -0
  75. {plain-0.43.1 → plain-0.45.0}/plain/internal/middleware/slash.py +0 -0
  76. {plain-0.43.1 → plain-0.45.0}/plain/json.py +0 -0
  77. {plain-0.43.1 → plain-0.45.0}/plain/logs/README.md +0 -0
  78. {plain-0.43.1 → plain-0.45.0}/plain/logs/__init__.py +0 -0
  79. {plain-0.43.1 → plain-0.45.0}/plain/logs/configure.py +0 -0
  80. {plain-0.43.1 → plain-0.45.0}/plain/logs/loggers.py +0 -0
  81. {plain-0.43.1 → plain-0.45.0}/plain/logs/utils.py +0 -0
  82. {plain-0.43.1 → plain-0.45.0}/plain/packages/README.md +0 -0
  83. {plain-0.43.1 → plain-0.45.0}/plain/packages/__init__.py +0 -0
  84. {plain-0.43.1 → plain-0.45.0}/plain/packages/config.py +0 -0
  85. {plain-0.43.1 → plain-0.45.0}/plain/packages/registry.py +0 -0
  86. {plain-0.43.1 → plain-0.45.0}/plain/preflight/README.md +0 -0
  87. {plain-0.43.1 → plain-0.45.0}/plain/preflight/__init__.py +0 -0
  88. {plain-0.43.1 → plain-0.45.0}/plain/preflight/files.py +0 -0
  89. {plain-0.43.1 → plain-0.45.0}/plain/preflight/messages.py +0 -0
  90. {plain-0.43.1 → plain-0.45.0}/plain/preflight/registry.py +0 -0
  91. {plain-0.43.1 → plain-0.45.0}/plain/preflight/security.py +0 -0
  92. {plain-0.43.1 → plain-0.45.0}/plain/preflight/urls.py +0 -0
  93. {plain-0.43.1 → plain-0.45.0}/plain/runtime/README.md +0 -0
  94. {plain-0.43.1 → plain-0.45.0}/plain/runtime/__init__.py +0 -0
  95. {plain-0.43.1 → plain-0.45.0}/plain/signals/README.md +0 -0
  96. {plain-0.43.1 → plain-0.45.0}/plain/signals/__init__.py +0 -0
  97. {plain-0.43.1 → plain-0.45.0}/plain/signals/dispatch/__init__.py +0 -0
  98. {plain-0.43.1 → plain-0.45.0}/plain/signals/dispatch/dispatcher.py +0 -0
  99. {plain-0.43.1 → plain-0.45.0}/plain/signals/dispatch/license.txt +0 -0
  100. {plain-0.43.1 → plain-0.45.0}/plain/templates/README.md +0 -0
  101. {plain-0.43.1 → plain-0.45.0}/plain/templates/__init__.py +0 -0
  102. {plain-0.43.1 → plain-0.45.0}/plain/templates/core.py +0 -0
  103. {plain-0.43.1 → plain-0.45.0}/plain/templates/jinja/__init__.py +0 -0
  104. {plain-0.43.1 → plain-0.45.0}/plain/templates/jinja/environments.py +0 -0
  105. {plain-0.43.1 → plain-0.45.0}/plain/templates/jinja/extensions.py +0 -0
  106. {plain-0.43.1 → plain-0.45.0}/plain/templates/jinja/filters.py +0 -0
  107. {plain-0.43.1 → plain-0.45.0}/plain/templates/jinja/globals.py +0 -0
  108. {plain-0.43.1 → plain-0.45.0}/plain/test/README.md +0 -0
  109. {plain-0.43.1 → plain-0.45.0}/plain/test/__init__.py +0 -0
  110. {plain-0.43.1 → plain-0.45.0}/plain/test/client.py +0 -0
  111. {plain-0.43.1 → plain-0.45.0}/plain/test/encoding.py +0 -0
  112. {plain-0.43.1 → plain-0.45.0}/plain/test/exceptions.py +0 -0
  113. {plain-0.43.1 → plain-0.45.0}/plain/urls/README.md +0 -0
  114. {plain-0.43.1 → plain-0.45.0}/plain/urls/__init__.py +0 -0
  115. {plain-0.43.1 → plain-0.45.0}/plain/urls/converters.py +0 -0
  116. {plain-0.43.1 → plain-0.45.0}/plain/urls/exceptions.py +0 -0
  117. {plain-0.43.1 → plain-0.45.0}/plain/urls/patterns.py +0 -0
  118. {plain-0.43.1 → plain-0.45.0}/plain/urls/resolvers.py +0 -0
  119. {plain-0.43.1 → plain-0.45.0}/plain/urls/routers.py +0 -0
  120. {plain-0.43.1 → plain-0.45.0}/plain/urls/utils.py +0 -0
  121. {plain-0.43.1 → plain-0.45.0}/plain/utils/README.md +0 -0
  122. {plain-0.43.1 → plain-0.45.0}/plain/utils/__init__.py +0 -0
  123. {plain-0.43.1 → plain-0.45.0}/plain/utils/cache.py +0 -0
  124. {plain-0.43.1 → plain-0.45.0}/plain/utils/crypto.py +0 -0
  125. {plain-0.43.1 → plain-0.45.0}/plain/utils/datastructures.py +0 -0
  126. {plain-0.43.1 → plain-0.45.0}/plain/utils/dateparse.py +0 -0
  127. {plain-0.43.1 → plain-0.45.0}/plain/utils/deconstruct.py +0 -0
  128. {plain-0.43.1 → plain-0.45.0}/plain/utils/decorators.py +0 -0
  129. {plain-0.43.1 → plain-0.45.0}/plain/utils/duration.py +0 -0
  130. {plain-0.43.1 → plain-0.45.0}/plain/utils/encoding.py +0 -0
  131. {plain-0.43.1 → plain-0.45.0}/plain/utils/hashable.py +0 -0
  132. {plain-0.43.1 → plain-0.45.0}/plain/utils/html.py +0 -0
  133. {plain-0.43.1 → plain-0.45.0}/plain/utils/http.py +0 -0
  134. {plain-0.43.1 → plain-0.45.0}/plain/utils/inspect.py +0 -0
  135. {plain-0.43.1 → plain-0.45.0}/plain/utils/ipv6.py +0 -0
  136. {plain-0.43.1 → plain-0.45.0}/plain/utils/itercompat.py +0 -0
  137. {plain-0.43.1 → plain-0.45.0}/plain/utils/module_loading.py +0 -0
  138. {plain-0.43.1 → plain-0.45.0}/plain/utils/regex_helper.py +0 -0
  139. {plain-0.43.1 → plain-0.45.0}/plain/utils/safestring.py +0 -0
  140. {plain-0.43.1 → plain-0.45.0}/plain/utils/text.py +0 -0
  141. {plain-0.43.1 → plain-0.45.0}/plain/utils/timesince.py +0 -0
  142. {plain-0.43.1 → plain-0.45.0}/plain/utils/timezone.py +0 -0
  143. {plain-0.43.1 → plain-0.45.0}/plain/utils/tree.py +0 -0
  144. {plain-0.43.1 → plain-0.45.0}/plain/validators.py +0 -0
  145. {plain-0.43.1 → plain-0.45.0}/plain/views/README.md +0 -0
  146. {plain-0.43.1 → plain-0.45.0}/plain/views/__init__.py +0 -0
  147. {plain-0.43.1 → plain-0.45.0}/plain/views/base.py +0 -0
  148. {plain-0.43.1 → plain-0.45.0}/plain/views/csrf.py +0 -0
  149. {plain-0.43.1 → plain-0.45.0}/plain/views/errors.py +0 -0
  150. {plain-0.43.1 → plain-0.45.0}/plain/views/exceptions.py +0 -0
  151. {plain-0.43.1 → plain-0.45.0}/plain/views/forms.py +0 -0
  152. {plain-0.43.1 → plain-0.45.0}/plain/views/objects.py +0 -0
  153. {plain-0.43.1 → plain-0.45.0}/plain/views/redirect.py +0 -0
  154. {plain-0.43.1 → plain-0.45.0}/plain/views/templates.py +0 -0
  155. {plain-0.43.1 → plain-0.45.0}/plain/wsgi.py +0 -0
  156. {plain-0.43.1 → plain-0.45.0}/tests/.gitignore +0 -0
  157. {plain-0.43.1 → plain-0.45.0}/tests/app/.gitignore +0 -0
  158. {plain-0.43.1 → plain-0.45.0}/tests/app/settings.py +0 -0
  159. {plain-0.43.1 → plain-0.45.0}/tests/app/test/__init__.py +0 -0
  160. {plain-0.43.1 → plain-0.45.0}/tests/app/test/default_settings.py +0 -0
  161. {plain-0.43.1 → plain-0.45.0}/tests/app/urls.py +0 -0
  162. {plain-0.43.1 → plain-0.45.0}/tests/conftest.py +0 -0
  163. {plain-0.43.1 → plain-0.45.0}/tests/test_runtime.py +0 -0
  164. {plain-0.43.1 → plain-0.45.0}/tests/test_wsgi.py +0 -0
@@ -14,4 +14,5 @@ plain*/tests/.plain
14
14
 
15
15
  /llms-full.txt
16
16
 
17
- /.plain
17
+ # Plain temp dirs
18
+ .plain
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain
3
- Version: 0.43.1
3
+ Version: 0.45.0
4
4
  Summary: A web framework for building products with Python.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-File: LICENSE
@@ -42,7 +42,7 @@ The `plain` package includes everything you need to start handling web requests
42
42
  - [plain.cache](/plain-cache/plain/cache/README.md) - A database-driven general purpose cache.
43
43
  - [plain.email](/plain-email/plain/email/README.md) - Send emails with SMTP or custom backends.
44
44
  - [plain.sessions](/plain-sessions/plain/sessions/README.md) - User sessions and cookies.
45
- - [plain.worker](/plain-worker/plain/worker/README.md) - Backgrounb jobs stored in the database.
45
+ - [plain.worker](/plain-worker/plain/worker/README.md) - Background jobs stored in the database.
46
46
  - [plain.api](/plain-api/plain/api/README.md) - Build APIs with Plain views.
47
47
 
48
48
  ## Auth Packages
@@ -31,7 +31,7 @@ The `plain` package includes everything you need to start handling web requests
31
31
  - [plain.cache](/plain-cache/plain/cache/README.md) - A database-driven general purpose cache.
32
32
  - [plain.email](/plain-email/plain/email/README.md) - Send emails with SMTP or custom backends.
33
33
  - [plain.sessions](/plain-sessions/plain/sessions/README.md) - User sessions and cookies.
34
- - [plain.worker](/plain-worker/plain/worker/README.md) - Backgrounb jobs stored in the database.
34
+ - [plain.worker](/plain-worker/plain/worker/README.md) - Background jobs stored in the database.
35
35
  - [plain.api](/plain-api/plain/api/README.md) - Build APIs with Plain views.
36
36
 
37
37
  ## Auth Packages
@@ -38,6 +38,8 @@ def create(package_name):
38
38
  (package_dir / "urls.py").write_text(
39
39
  f"""from plain.urls import path, Router
40
40
 
41
+ from . import views
42
+
41
43
 
42
44
  class {package_name.capitalize()}Router(Router):
43
45
  namespace = f"{package_name}"
@@ -8,6 +8,7 @@ against request forgeries from other sites.
8
8
  import logging
9
9
  import string
10
10
  from collections import defaultdict
11
+ from functools import cached_property
11
12
  from urllib.parse import urlparse
12
13
 
13
14
  from plain.exceptions import DisallowedHost
@@ -16,7 +17,6 @@ from plain.logs import log_response
16
17
  from plain.runtime import settings
17
18
  from plain.utils.cache import patch_vary_headers
18
19
  from plain.utils.crypto import constant_time_compare, get_random_string
19
- from plain.utils.functional import cached_property
20
20
  from plain.utils.http import is_same_domain
21
21
  from plain.utils.regex_helper import _lazy_re_compile
22
22
 
@@ -4,6 +4,7 @@ Field classes.
4
4
 
5
5
  import copy
6
6
  import datetime
7
+ import enum
7
8
  import json
8
9
  import math
9
10
  import re
@@ -773,6 +774,8 @@ class ChoiceField(Field):
773
774
  super().__init__(**kwargs)
774
775
  if hasattr(choices, "choices"):
775
776
  choices = choices.choices
777
+ elif isinstance(choices, enum.EnumMeta):
778
+ choices = [(member.value, member.name) for member in choices]
776
779
  self.choices = choices
777
780
 
778
781
  def __deepcopy__(self, memo):
@@ -3,9 +3,9 @@ Form classes
3
3
  """
4
4
 
5
5
  import copy
6
+ from functools import cached_property
6
7
 
7
8
  from plain.exceptions import NON_FIELD_ERRORS
8
- from plain.utils.functional import cached_property
9
9
 
10
10
  from .exceptions import ValidationError
11
11
  from .fields import Field, FileField
@@ -2,6 +2,7 @@ import codecs
2
2
  import copy
3
3
  import json
4
4
  import uuid
5
+ from functools import cached_property
5
6
  from io import BytesIO
6
7
  from itertools import chain
7
8
  from urllib.parse import parse_qsl, quote, urlencode, urljoin, urlsplit
@@ -25,7 +26,6 @@ from plain.utils.datastructures import (
25
26
  MultiValueDict,
26
27
  )
27
28
  from plain.utils.encoding import iri_to_uri
28
- from plain.utils.functional import cached_property
29
29
  from plain.utils.http import is_same_domain, parse_header_parameters
30
30
  from plain.utils.regex_helper import _lazy_re_compile
31
31
 
@@ -18,7 +18,7 @@ from plain.json import PlainJSONEncoder
18
18
  from plain.runtime import settings
19
19
  from plain.utils import timezone
20
20
  from plain.utils.datastructures import CaseInsensitiveMapping
21
- from plain.utils.encoding import iri_to_uri
21
+ from plain.utils.encoding import force_bytes, iri_to_uri
22
22
  from plain.utils.http import content_disposition_header, http_date
23
23
  from plain.utils.regex_helper import _lazy_re_compile
24
24
 
@@ -258,7 +258,17 @@ class ResponseBase:
258
258
  self.cookies[key]["samesite"] = samesite
259
259
 
260
260
  def set_signed_cookie(self, key, value, salt="", **kwargs):
261
- value = signing.get_cookie_signer(salt=key + salt).sign(value)
261
+ """Set a cookie signed with the SECRET_KEY."""
262
+
263
+ def _cookie_key(k):
264
+ return b"plain.http.cookies" + force_bytes(k)
265
+
266
+ signer = signing.TimestampSigner(
267
+ key=_cookie_key(settings.SECRET_KEY),
268
+ fallback_keys=map(_cookie_key, settings.SECRET_KEY_FALLBACKS),
269
+ salt=key + salt,
270
+ )
271
+ value = signer.sign(value)
262
272
  return self.set_cookie(key, value, **kwargs)
263
273
 
264
274
  def delete_cookie(self, key, path="/", domain=None, samesite=None):
@@ -1,8 +1,8 @@
1
1
  import os
2
+ from functools import cached_property
2
3
  from io import UnsupportedOperation
3
4
 
4
5
  from plain.internal.files.utils import FileProxyMixin
5
- from plain.utils.functional import cached_property
6
6
 
7
7
 
8
8
  class File(FileProxyMixin):
@@ -1,11 +1,11 @@
1
1
  import uuid
2
+ from functools import cached_property
2
3
  from io import IOBase
3
4
  from urllib.parse import quote
4
5
 
5
6
  from plain import signals
6
7
  from plain.http import HttpRequest, QueryDict, parse_cookie
7
8
  from plain.internal.handlers import base
8
- from plain.utils.functional import cached_property
9
9
  from plain.utils.regex_helper import _lazy_re_compile
10
10
 
11
11
  _slashes_re = _lazy_re_compile(rb"/+")
@@ -125,15 +125,13 @@ class WSGIRequest(HttpRequest):
125
125
 
126
126
 
127
127
  class WSGIHandler(base.BaseHandler):
128
- request_class = WSGIRequest
129
-
130
128
  def __init__(self, *args, **kwargs):
131
129
  super().__init__(*args, **kwargs)
132
130
  self.load_middleware()
133
131
 
134
132
  def __call__(self, environ, start_response):
135
133
  signals.request_started.send(sender=self.__class__, environ=environ)
136
- request = self.request_class(environ)
134
+ request = WSGIRequest(environ)
137
135
  response = self.get_response(request)
138
136
 
139
137
  response._handler_class = self.__class__
@@ -1,9 +1,9 @@
1
1
  import collections.abc
2
2
  import inspect
3
3
  import warnings
4
+ from functools import cached_property
4
5
  from math import ceil
5
6
 
6
- from plain.utils.functional import cached_property
7
7
  from plain.utils.inspect import method_has_no_args
8
8
 
9
9
 
@@ -109,12 +109,6 @@ HTTP_ERROR_VIEWS: dict[int] = {}
109
109
  # phase the middleware will be applied in reverse order.
110
110
  MIDDLEWARE: list[str] = []
111
111
 
112
- ###########
113
- # SIGNING #
114
- ###########
115
-
116
- COOKIE_SIGNING_BACKEND = "plain.signing.TimestampSigner"
117
-
118
112
  ########
119
113
  # CSRF #
120
114
  ########
@@ -121,7 +121,9 @@ class Settings:
121
121
  if setting in self._settings:
122
122
  setting_def = self._settings[setting]
123
123
  try:
124
- parsed_value = _parse_env_value(value, setting_def.annotation)
124
+ parsed_value = _parse_env_value(
125
+ value, setting_def.annotation, setting
126
+ )
125
127
  setting_def.set_value(parsed_value, "env")
126
128
  except ImproperlyConfigured as e:
127
129
  self._errors.append(str(e))
@@ -211,9 +213,11 @@ class Settings:
211
213
  return f'<Settings "{self._settings_module}">'
212
214
 
213
215
 
214
- def _parse_env_value(value, annotation):
216
+ def _parse_env_value(value, annotation, setting_name):
215
217
  if not annotation:
216
- raise ImproperlyConfigured("Type hint required to set from environment.")
218
+ raise ImproperlyConfigured(
219
+ f"{setting_name}: Type hint required to set from environment."
220
+ )
217
221
 
218
222
  if annotation is bool:
219
223
  # Special case for bools
@@ -226,7 +230,7 @@ def _parse_env_value(value, annotation):
226
230
  return json.loads(value)
227
231
  except json.JSONDecodeError as e:
228
232
  raise ImproperlyConfigured(
229
- f"Invalid JSON value for setting: {e.msg}"
233
+ f"Invalid JSON value for setting '{setting_name}': {e.msg}"
230
234
  ) from e
231
235
 
232
236
 
@@ -41,8 +41,6 @@ import zlib
41
41
 
42
42
  from plain.runtime import settings
43
43
  from plain.utils.crypto import constant_time_compare, salted_hmac
44
- from plain.utils.encoding import force_bytes
45
- from plain.utils.module_loading import import_string
46
44
  from plain.utils.regex_helper import _lazy_re_compile
47
45
 
48
46
  _SEP_UNSAFE = _lazy_re_compile(r"^[A-z0-9-_=]*$")
@@ -101,20 +99,6 @@ def base64_hmac(salt, value, key, algorithm="sha1"):
101
99
  ).decode()
102
100
 
103
101
 
104
- def _cookie_signer_key(key):
105
- # SECRET_KEYS items may be str or bytes.
106
- return b"plain.http.cookies" + force_bytes(key)
107
-
108
-
109
- def get_cookie_signer(salt="plain.signing.get_cookie_signer"):
110
- Signer = import_string(settings.COOKIE_SIGNING_BACKEND)
111
- return Signer(
112
- key=_cookie_signer_key(settings.SECRET_KEY),
113
- fallback_keys=map(_cookie_signer_key, settings.SECRET_KEY_FALLBACKS),
114
- salt=salt,
115
- )
116
-
117
-
118
102
  class JSONSerializer:
119
103
  """
120
104
  Simple wrapper around json to be used in signing.dumps and
@@ -1,7 +1,7 @@
1
+ from functools import cached_property
1
2
  from threading import local
2
3
 
3
4
  from plain.runtime import settings as plain_settings
4
- from plain.utils.functional import cached_property
5
5
 
6
6
 
7
7
  class ConnectionProxy:
@@ -4,49 +4,6 @@ import operator
4
4
  from functools import total_ordering, wraps
5
5
 
6
6
 
7
- class cached_property:
8
- """
9
- Decorator that converts a method with a single self argument into a
10
- property cached on the instance.
11
-
12
- A cached property can be made out of an existing method:
13
- (e.g. ``url = cached_property(get_absolute_url)``).
14
- """
15
-
16
- name = None
17
-
18
- @staticmethod
19
- def func(instance):
20
- raise TypeError(
21
- "Cannot use cached_property instance without calling __set_name__() on it."
22
- )
23
-
24
- def __init__(self, func):
25
- self.real_func = func
26
- self.__doc__ = getattr(func, "__doc__")
27
-
28
- def __set_name__(self, owner, name):
29
- if self.name is None:
30
- self.name = name
31
- self.func = self.real_func
32
- elif name != self.name:
33
- raise TypeError(
34
- "Cannot assign the same cached_property to two different names "
35
- f"({self.name!r} and {name!r})."
36
- )
37
-
38
- def __get__(self, instance, cls=None):
39
- """
40
- Call the function and put the return value in instance.__dict__ so that
41
- subsequent attribute access on the instance returns the cached value
42
- instead of calling cached_property.__get__().
43
- """
44
- if instance is None:
45
- return self
46
- res = instance.__dict__[self.name] = self.func(instance)
47
- return res
48
-
49
-
50
7
  class classproperty:
51
8
  """
52
9
  Decorator that converts a method with a single cls argument into a property
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain"
3
- version = "0.43.1"
3
+ version = "0.45.0"
4
4
  description = "A web framework for building products with Python."
5
5
  authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
6
6
  readme = "README.md"
@@ -0,0 +1,17 @@
1
+ from click.testing import CliRunner
2
+
3
+ from plain.cli.core import cli
4
+
5
+
6
+ def test_plain_cli_help():
7
+ runner = CliRunner()
8
+ result = runner.invoke(cli, ["--help"], prog_name="plain")
9
+ assert result.exit_code == 0
10
+ assert result.output.startswith("Usage: plain")
11
+
12
+
13
+ def test_plain_cli_build():
14
+ runner = CliRunner()
15
+ result = runner.invoke(cli, ["build"], prog_name="plain")
16
+ assert result.exit_code == 0
17
+ assert "Compiled 0 assets into 0 files" in result.output
@@ -1,11 +0,0 @@
1
- import subprocess
2
-
3
-
4
- def test_plain_cli_help():
5
- output = subprocess.check_output(["plain", "--help"])
6
- assert output.startswith(b"Usage: plain")
7
-
8
-
9
- def test_plain_cli_build():
10
- output = subprocess.check_output(["plain", "build"])
11
- assert b"Compiled 0 assets into 0 files" in output
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes