plain 0.66.0__py3-none-any.whl → 0.101.2__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 (197) hide show
  1. plain/CHANGELOG.md +684 -0
  2. plain/README.md +1 -1
  3. plain/assets/compile.py +25 -12
  4. plain/assets/finders.py +24 -17
  5. plain/assets/fingerprints.py +10 -7
  6. plain/assets/urls.py +1 -1
  7. plain/assets/views.py +47 -33
  8. plain/chores/README.md +25 -23
  9. plain/chores/__init__.py +2 -1
  10. plain/chores/core.py +27 -0
  11. plain/chores/registry.py +23 -53
  12. plain/cli/README.md +185 -16
  13. plain/cli/__init__.py +2 -1
  14. plain/cli/agent.py +236 -0
  15. plain/cli/build.py +7 -8
  16. plain/cli/changelog.py +11 -5
  17. plain/cli/chores.py +32 -34
  18. plain/cli/core.py +112 -28
  19. plain/cli/docs.py +52 -11
  20. plain/cli/formatting.py +40 -17
  21. plain/cli/install.py +10 -54
  22. plain/cli/{agent/llmdocs.py → llmdocs.py} +21 -9
  23. plain/cli/output.py +6 -2
  24. plain/cli/preflight.py +175 -102
  25. plain/cli/print.py +4 -4
  26. plain/cli/registry.py +95 -26
  27. plain/cli/request.py +206 -0
  28. plain/cli/runtime.py +45 -0
  29. plain/cli/scaffold.py +2 -7
  30. plain/cli/server.py +153 -0
  31. plain/cli/settings.py +53 -49
  32. plain/cli/shell.py +15 -12
  33. plain/cli/startup.py +9 -8
  34. plain/cli/upgrade.py +17 -104
  35. plain/cli/urls.py +12 -7
  36. plain/cli/utils.py +3 -3
  37. plain/csrf/README.md +65 -40
  38. plain/csrf/middleware.py +53 -43
  39. plain/debug.py +5 -2
  40. plain/exceptions.py +22 -114
  41. plain/forms/README.md +453 -24
  42. plain/forms/__init__.py +55 -4
  43. plain/forms/boundfield.py +15 -8
  44. plain/forms/exceptions.py +1 -1
  45. plain/forms/fields.py +346 -143
  46. plain/forms/forms.py +75 -45
  47. plain/http/README.md +356 -9
  48. plain/http/__init__.py +41 -26
  49. plain/http/cookie.py +15 -7
  50. plain/http/exceptions.py +65 -0
  51. plain/http/middleware.py +32 -0
  52. plain/http/multipartparser.py +99 -88
  53. plain/http/request.py +362 -250
  54. plain/http/response.py +99 -197
  55. plain/internal/__init__.py +8 -1
  56. plain/internal/files/base.py +35 -19
  57. plain/internal/files/locks.py +19 -11
  58. plain/internal/files/move.py +8 -3
  59. plain/internal/files/temp.py +25 -6
  60. plain/internal/files/uploadedfile.py +47 -28
  61. plain/internal/files/uploadhandler.py +64 -58
  62. plain/internal/files/utils.py +24 -10
  63. plain/internal/handlers/base.py +34 -23
  64. plain/internal/handlers/exception.py +68 -65
  65. plain/internal/handlers/wsgi.py +65 -54
  66. plain/internal/middleware/headers.py +37 -11
  67. plain/internal/middleware/hosts.py +11 -13
  68. plain/internal/middleware/https.py +17 -7
  69. plain/internal/middleware/slash.py +14 -9
  70. plain/internal/reloader.py +77 -0
  71. plain/json.py +2 -1
  72. plain/logs/README.md +161 -62
  73. plain/logs/__init__.py +1 -1
  74. plain/logs/{loggers.py → app.py} +71 -67
  75. plain/logs/configure.py +63 -14
  76. plain/logs/debug.py +17 -6
  77. plain/logs/filters.py +15 -0
  78. plain/logs/formatters.py +7 -4
  79. plain/packages/README.md +105 -23
  80. plain/packages/config.py +15 -7
  81. plain/packages/registry.py +40 -15
  82. plain/paginator.py +31 -21
  83. plain/preflight/README.md +208 -23
  84. plain/preflight/__init__.py +5 -24
  85. plain/preflight/checks.py +12 -0
  86. plain/preflight/files.py +19 -13
  87. plain/preflight/registry.py +80 -58
  88. plain/preflight/results.py +37 -0
  89. plain/preflight/security.py +65 -71
  90. plain/preflight/settings.py +54 -0
  91. plain/preflight/urls.py +10 -48
  92. plain/runtime/README.md +115 -47
  93. plain/runtime/__init__.py +10 -6
  94. plain/runtime/global_settings.py +43 -33
  95. plain/runtime/secret.py +20 -0
  96. plain/runtime/user_settings.py +110 -38
  97. plain/runtime/utils.py +1 -1
  98. plain/server/LICENSE +35 -0
  99. plain/server/README.md +155 -0
  100. plain/server/__init__.py +9 -0
  101. plain/server/app.py +52 -0
  102. plain/server/arbiter.py +555 -0
  103. plain/server/config.py +118 -0
  104. plain/server/errors.py +31 -0
  105. plain/server/glogging.py +292 -0
  106. plain/server/http/__init__.py +12 -0
  107. plain/server/http/body.py +283 -0
  108. plain/server/http/errors.py +155 -0
  109. plain/server/http/message.py +400 -0
  110. plain/server/http/parser.py +70 -0
  111. plain/server/http/unreader.py +88 -0
  112. plain/server/http/wsgi.py +421 -0
  113. plain/server/pidfile.py +92 -0
  114. plain/server/sock.py +240 -0
  115. plain/server/util.py +317 -0
  116. plain/server/workers/__init__.py +6 -0
  117. plain/server/workers/base.py +304 -0
  118. plain/server/workers/sync.py +212 -0
  119. plain/server/workers/thread.py +399 -0
  120. plain/server/workers/workertmp.py +50 -0
  121. plain/signals/README.md +170 -1
  122. plain/signals/__init__.py +0 -1
  123. plain/signals/dispatch/dispatcher.py +49 -27
  124. plain/signing.py +131 -35
  125. plain/skills/README.md +36 -0
  126. plain/skills/plain-docs/SKILL.md +25 -0
  127. plain/skills/plain-install/SKILL.md +26 -0
  128. plain/skills/plain-request/SKILL.md +39 -0
  129. plain/skills/plain-shell/SKILL.md +24 -0
  130. plain/skills/plain-upgrade/SKILL.md +35 -0
  131. plain/templates/README.md +211 -20
  132. plain/templates/jinja/__init__.py +14 -27
  133. plain/templates/jinja/environments.py +5 -4
  134. plain/templates/jinja/extensions.py +12 -5
  135. plain/templates/jinja/filters.py +7 -2
  136. plain/templates/jinja/globals.py +2 -2
  137. plain/test/README.md +184 -22
  138. plain/test/client.py +340 -222
  139. plain/test/encoding.py +9 -6
  140. plain/test/exceptions.py +7 -2
  141. plain/urls/README.md +157 -73
  142. plain/urls/converters.py +18 -15
  143. plain/urls/exceptions.py +2 -2
  144. plain/urls/patterns.py +56 -40
  145. plain/urls/resolvers.py +38 -28
  146. plain/urls/utils.py +5 -1
  147. plain/utils/README.md +250 -3
  148. plain/utils/cache.py +17 -11
  149. plain/utils/crypto.py +21 -5
  150. plain/utils/datastructures.py +89 -56
  151. plain/utils/dateparse.py +9 -6
  152. plain/utils/deconstruct.py +15 -7
  153. plain/utils/decorators.py +5 -1
  154. plain/utils/dotenv.py +373 -0
  155. plain/utils/duration.py +8 -4
  156. plain/utils/encoding.py +14 -7
  157. plain/utils/functional.py +66 -49
  158. plain/utils/hashable.py +5 -1
  159. plain/utils/html.py +36 -22
  160. plain/utils/http.py +16 -9
  161. plain/utils/inspect.py +14 -6
  162. plain/utils/ipv6.py +7 -3
  163. plain/utils/itercompat.py +6 -1
  164. plain/utils/module_loading.py +7 -3
  165. plain/utils/regex_helper.py +37 -23
  166. plain/utils/safestring.py +14 -6
  167. plain/utils/text.py +41 -23
  168. plain/utils/timezone.py +33 -22
  169. plain/utils/tree.py +35 -19
  170. plain/validators.py +94 -52
  171. plain/views/README.md +156 -79
  172. plain/views/__init__.py +0 -1
  173. plain/views/base.py +25 -18
  174. plain/views/errors.py +13 -5
  175. plain/views/exceptions.py +4 -1
  176. plain/views/forms.py +6 -6
  177. plain/views/objects.py +52 -49
  178. plain/views/redirect.py +18 -15
  179. plain/views/templates.py +5 -3
  180. plain/wsgi.py +3 -1
  181. {plain-0.66.0.dist-info → plain-0.101.2.dist-info}/METADATA +4 -2
  182. plain-0.101.2.dist-info/RECORD +201 -0
  183. {plain-0.66.0.dist-info → plain-0.101.2.dist-info}/WHEEL +1 -1
  184. plain-0.101.2.dist-info/entry_points.txt +2 -0
  185. plain/AGENTS.md +0 -18
  186. plain/cli/agent/__init__.py +0 -20
  187. plain/cli/agent/docs.py +0 -80
  188. plain/cli/agent/md.py +0 -87
  189. plain/cli/agent/prompt.py +0 -45
  190. plain/cli/agent/request.py +0 -181
  191. plain/csrf/views.py +0 -31
  192. plain/logs/utils.py +0 -46
  193. plain/preflight/messages.py +0 -81
  194. plain/templates/AGENTS.md +0 -3
  195. plain-0.66.0.dist-info/RECORD +0 -168
  196. plain-0.66.0.dist-info/entry_points.txt +0 -4
  197. {plain-0.66.0.dist-info → plain-0.101.2.dist-info}/licenses/LICENSE +0 -0
plain/forms/__init__.py CHANGED
@@ -2,7 +2,58 @@
2
2
  Plain validation and HTML form handling.
3
3
  """
4
4
 
5
- from .boundfield import BoundField # NOQA
6
- from .exceptions import FormFieldMissingError, ValidationError # NOQA
7
- from .fields import * # NOQA
8
- from .forms import Form # NOQA
5
+ from .boundfield import BoundField
6
+ from .exceptions import FormFieldMissingError, ValidationError
7
+ from .fields import (
8
+ BooleanField,
9
+ CharField,
10
+ ChoiceField,
11
+ DateField,
12
+ DateTimeField,
13
+ DecimalField,
14
+ DurationField,
15
+ EmailField,
16
+ Field,
17
+ FileField,
18
+ FloatField,
19
+ ImageField,
20
+ IntegerField,
21
+ JSONField,
22
+ MultipleChoiceField,
23
+ NullBooleanField,
24
+ RegexField,
25
+ TimeField,
26
+ TypedChoiceField,
27
+ URLField,
28
+ UUIDField,
29
+ )
30
+ from .forms import BaseForm, Form
31
+
32
+ __all__ = [
33
+ "BoundField",
34
+ "FormFieldMissingError",
35
+ "ValidationError",
36
+ "BooleanField",
37
+ "CharField",
38
+ "ChoiceField",
39
+ "DateField",
40
+ "DateTimeField",
41
+ "DecimalField",
42
+ "DurationField",
43
+ "EmailField",
44
+ "Field",
45
+ "FileField",
46
+ "FloatField",
47
+ "ImageField",
48
+ "IntegerField",
49
+ "JSONField",
50
+ "MultipleChoiceField",
51
+ "NullBooleanField",
52
+ "RegexField",
53
+ "TimeField",
54
+ "TypedChoiceField",
55
+ "URLField",
56
+ "UUIDField",
57
+ "BaseForm",
58
+ "Form",
59
+ ]
plain/forms/boundfield.py CHANGED
@@ -1,4 +1,11 @@
1
+ from __future__ import annotations
2
+
1
3
  from functools import cached_property
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ if TYPE_CHECKING:
7
+ from .fields import Field
8
+ from .forms import BaseForm
2
9
 
3
10
  __all__ = ("BoundField",)
4
11
 
@@ -6,24 +13,24 @@ __all__ = ("BoundField",)
6
13
  class BoundField:
7
14
  "A Field plus data"
8
15
 
9
- def __init__(self, form, field, name):
16
+ def __init__(self, form: BaseForm, field: Field, name: str):
10
17
  self._form = form
11
18
  self.field = field
12
19
  self.name = name
13
20
  self.html_name = form.add_prefix(name)
14
21
  self.html_id = form.add_prefix(self._auto_id)
15
22
 
16
- def __repr__(self):
23
+ def __repr__(self) -> str:
17
24
  return f'<{self.__class__.__name__} "{self.html_name}">'
18
25
 
19
26
  @property
20
- def errors(self):
27
+ def errors(self) -> list[str]:
21
28
  """
22
29
  Return an error list (empty if there are no errors) for this field.
23
30
  """
24
31
  return self._form.errors.get(self.name, [])
25
32
 
26
- def value(self):
33
+ def value(self) -> Any:
27
34
  """
28
35
  Return the value for this BoundField, using the initial value if
29
36
  the form is not bound or the data otherwise.
@@ -36,22 +43,22 @@ class BoundField:
36
43
  return self.field.prepare_value(data)
37
44
 
38
45
  @cached_property
39
- def initial(self):
46
+ def initial(self) -> Any:
40
47
  return self._form.get_initial_for_field(self.field, self.name)
41
48
 
42
- def _has_changed(self):
49
+ def _has_changed(self) -> bool:
43
50
  return self.field.has_changed(
44
51
  self.initial, self._form._field_data_value(self.field, self.html_name)
45
52
  )
46
53
 
47
54
  @property
48
- def _auto_id(self):
55
+ def _auto_id(self) -> str:
49
56
  """
50
57
  Calculate and return the ID attribute for this BoundField, if the
51
58
  associated Form has specified auto_id. Return an empty string otherwise.
52
59
  """
53
60
  auto_id = self._form._auto_id # Boolean or string
54
- if auto_id and "%s" in str(auto_id):
61
+ if auto_id and isinstance(auto_id, str) and "%s" in auto_id:
55
62
  return auto_id % self.html_name
56
63
  elif auto_id:
57
64
  return self.html_name
plain/forms/exceptions.py CHANGED
@@ -2,7 +2,7 @@ from plain.exceptions import ValidationError
2
2
 
3
3
 
4
4
  class FormFieldMissingError(Exception):
5
- def __init__(self, field_name):
5
+ def __init__(self, field_name: str):
6
6
  self.field_name = field_name
7
7
  self.message = f'The "{self.field_name}" field is missing from the form data.'
8
8