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/server/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # Server
2
+
3
+ **A production-ready WSGI HTTP server based on gunicorn.**
4
+
5
+ - [Overview](#overview)
6
+ - [Worker types](#worker-types)
7
+ - [Configuration options](#configuration-options)
8
+ - [Environment variables](#environment-variables)
9
+ - [Signals](#signals)
10
+ - [Using a different WSGI server](#using-a-different-wsgi-server)
11
+ - [FAQs](#faqs)
12
+ - [Installation](#installation)
13
+
14
+ ## Overview
15
+
16
+ You can run the built-in HTTP server with the `plain server` command.
17
+
18
+ ```bash
19
+ plain server
20
+ ```
21
+
22
+ By default, the server binds to `127.0.0.1:8000` and uses a single worker process. In production, you will typically want to increase the number of workers and optionally enable threading.
23
+
24
+ ```bash
25
+ # Run with 4 worker processes
26
+ plain server --workers 4
27
+
28
+ # Auto-detect based on available CPUs
29
+ plain server --workers auto
30
+
31
+ # Run with 2 workers and 4 threads each
32
+ plain server --workers 2 --threads 4
33
+ ```
34
+
35
+ For local development, you can enable auto-reload to restart workers when code changes.
36
+
37
+ ```bash
38
+ plain server --reload
39
+ ```
40
+
41
+ ## Worker types
42
+
43
+ The server automatically selects the worker type based on your configuration.
44
+
45
+ **Sync workers** handle one request at a time per worker. These are simple and predictable.
46
+
47
+ ```bash
48
+ # Single-threaded (uses sync worker)
49
+ plain server --workers 4
50
+ ```
51
+
52
+ **Threaded workers** handle multiple concurrent requests per worker using a thread pool. These are useful when your application does blocking I/O.
53
+
54
+ ```bash
55
+ # Multi-threaded (uses threaded worker)
56
+ plain server --workers 2 --threads 8
57
+ ```
58
+
59
+ For advanced worker customization, see the [`SyncWorker`](./workers/sync.py#SyncWorker) and [`ThreadWorker`](./workers/thread.py#ThreadWorker) classes.
60
+
61
+ ## Configuration options
62
+
63
+ All options are available via the command line. Run `plain server --help` to see the full list.
64
+
65
+ | Option | Default | Description |
66
+ | ------------------ | ---------------- | ----------------------------------------------------- |
67
+ | `--bind` / `-b` | `127.0.0.1:8000` | Address to bind (can be used multiple times) |
68
+ | `--workers` / `-w` | `1` | Number of worker processes (or `auto` for CPU count) |
69
+ | `--threads` | `1` | Number of threads per worker |
70
+ | `--timeout` / `-t` | `30` | Worker timeout in seconds |
71
+ | `--reload` | `False` | Restart workers when code changes |
72
+ | `--certfile` | - | Path to SSL certificate file |
73
+ | `--keyfile` | - | Path to SSL key file |
74
+ | `--log-level` | `info` | Logging level (debug, info, warning, error, critical) |
75
+ | `--access-log` | `-` (stdout) | Access log file path |
76
+ | `--error-log` | `-` (stderr) | Error log file path |
77
+ | `--max-requests` | `0` (disabled) | Max requests before worker restart |
78
+ | `--pidfile` | - | PID file path |
79
+
80
+ ## Environment variables
81
+
82
+ | Variable | Description |
83
+ | --------------------- | ------------------------------------------------------------------------ |
84
+ | `WEB_CONCURRENCY` | Sets the number of workers (use `auto` to detect CPU cores, or a number) |
85
+ | `SENDFILE` | Enable sendfile() syscall (`1`, `yes`, `true`, or `y` to enable) |
86
+ | `FORWARDED_ALLOW_IPS` | Comma-separated list of trusted proxy IPs (default: `127.0.0.1,::1`) |
87
+
88
+ ## Signals
89
+
90
+ The server responds to UNIX signals for process management.
91
+
92
+ | Signal | Effect |
93
+ | --------- | -------------------------------- |
94
+ | `SIGTERM` | Graceful shutdown |
95
+ | `SIGINT` | Quick shutdown |
96
+ | `SIGQUIT` | Quick shutdown |
97
+ | `SIGHUP` | Reload configuration and workers |
98
+ | `SIGTTIN` | Increase worker count by 1 |
99
+ | `SIGTTOU` | Decrease worker count by 1 |
100
+ | `SIGUSR1` | Reopen log files |
101
+
102
+ ## Using a different WSGI server
103
+
104
+ You can use any WSGI-compatible server instead of the built-in one. Plain provides a standard WSGI application interface at `plain.wsgi:app`.
105
+
106
+ ```bash
107
+ # Using uvicorn
108
+ uvicorn plain.wsgi:app --port 8000
109
+
110
+ # Using waitress
111
+ waitress-serve --port=8000 plain.wsgi:app
112
+
113
+ # Using gunicorn directly
114
+ gunicorn plain.wsgi:app --workers 4
115
+ ```
116
+
117
+ ## FAQs
118
+
119
+ #### How do I run with SSL/TLS?
120
+
121
+ Provide both `--certfile` and `--keyfile` options pointing to your certificate and key files.
122
+
123
+ ```bash
124
+ plain server --certfile cert.pem --keyfile key.pem
125
+ ```
126
+
127
+ #### How do I run behind a reverse proxy?
128
+
129
+ Configure your proxy to pass the appropriate headers, then set `FORWARDED_ALLOW_IPS` to include your proxy's IP address.
130
+
131
+ ```bash
132
+ FORWARDED_ALLOW_IPS="10.0.0.1,10.0.0.2" plain server --bind 0.0.0.0:8000
133
+ ```
134
+
135
+ The server recognizes `X-Forwarded-Proto`, `X-Forwarded-Protocol`, and `X-Forwarded-SSL` headers from trusted proxies.
136
+
137
+ #### How do I handle worker timeouts?
138
+
139
+ If workers are being killed due to timeouts, increase the `--timeout` value. This is common when handling long-running requests.
140
+
141
+ ```bash
142
+ plain server --timeout 120
143
+ ```
144
+
145
+ #### How do I rotate log files?
146
+
147
+ Send `SIGUSR1` to the master process to reopen log files. This works with tools like `logrotate`.
148
+
149
+ ```bash
150
+ kill -USR1 $(cat /path/to/pidfile)
151
+ ```
152
+
153
+ ## Installation
154
+
155
+ The server module is included with Plain. No additional installation is required.
@@ -0,0 +1,9 @@
1
+ #
2
+ # This file is part of gunicorn released under the MIT license.
3
+ # See the LICENSE for more information.
4
+ #
5
+ # Vendored and modified for Plain.
6
+
7
+ from .app import ServerApplication
8
+
9
+ __all__ = ["ServerApplication"]
plain/server/app.py ADDED
@@ -0,0 +1,52 @@
1
+ #
2
+ #
3
+ # This file is part of gunicorn released under the MIT license.
4
+ # See the LICENSE for more information.
5
+ #
6
+ # Vendored and modified for Plain.
7
+
8
+ from __future__ import annotations
9
+
10
+ import sys
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ from .arbiter import Arbiter
14
+
15
+ if TYPE_CHECKING:
16
+ from .config import Config
17
+
18
+
19
+ class ServerApplication:
20
+ """
21
+ Plain's server application.
22
+
23
+ This class provides the interface for running the WSGI server.
24
+ """
25
+
26
+ def __init__(self, cfg: Config) -> None:
27
+ self.cfg: Config = cfg
28
+ self.callable: Any = None
29
+
30
+ def load(self) -> Any:
31
+ """Load the WSGI application."""
32
+ # Import locally to avoid circular dependencies and allow
33
+ # the WSGI module to handle Plain runtime setup
34
+ from plain.wsgi import app
35
+
36
+ return app
37
+
38
+ def wsgi(self) -> Any:
39
+ """Get the WSGI application."""
40
+ if self.callable is None:
41
+ self.callable = self.load()
42
+ return self.callable
43
+
44
+ def run(self) -> None:
45
+ """Run the server."""
46
+
47
+ try:
48
+ Arbiter(self).run()
49
+ except RuntimeError as e:
50
+ print(f"\nError: {e}\n", file=sys.stderr)
51
+ sys.stderr.flush()
52
+ sys.exit(1)