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.
- plain/CHANGELOG.md +684 -0
- plain/README.md +1 -1
- plain/assets/compile.py +25 -12
- plain/assets/finders.py +24 -17
- plain/assets/fingerprints.py +10 -7
- plain/assets/urls.py +1 -1
- plain/assets/views.py +47 -33
- plain/chores/README.md +25 -23
- plain/chores/__init__.py +2 -1
- plain/chores/core.py +27 -0
- plain/chores/registry.py +23 -53
- plain/cli/README.md +185 -16
- plain/cli/__init__.py +2 -1
- plain/cli/agent.py +236 -0
- plain/cli/build.py +7 -8
- plain/cli/changelog.py +11 -5
- plain/cli/chores.py +32 -34
- plain/cli/core.py +112 -28
- plain/cli/docs.py +52 -11
- plain/cli/formatting.py +40 -17
- plain/cli/install.py +10 -54
- plain/cli/{agent/llmdocs.py → llmdocs.py} +21 -9
- plain/cli/output.py +6 -2
- plain/cli/preflight.py +175 -102
- plain/cli/print.py +4 -4
- plain/cli/registry.py +95 -26
- plain/cli/request.py +206 -0
- plain/cli/runtime.py +45 -0
- plain/cli/scaffold.py +2 -7
- plain/cli/server.py +153 -0
- plain/cli/settings.py +53 -49
- plain/cli/shell.py +15 -12
- plain/cli/startup.py +9 -8
- plain/cli/upgrade.py +17 -104
- plain/cli/urls.py +12 -7
- plain/cli/utils.py +3 -3
- plain/csrf/README.md +65 -40
- plain/csrf/middleware.py +53 -43
- plain/debug.py +5 -2
- plain/exceptions.py +22 -114
- plain/forms/README.md +453 -24
- plain/forms/__init__.py +55 -4
- plain/forms/boundfield.py +15 -8
- plain/forms/exceptions.py +1 -1
- plain/forms/fields.py +346 -143
- plain/forms/forms.py +75 -45
- plain/http/README.md +356 -9
- plain/http/__init__.py +41 -26
- plain/http/cookie.py +15 -7
- plain/http/exceptions.py +65 -0
- plain/http/middleware.py +32 -0
- plain/http/multipartparser.py +99 -88
- plain/http/request.py +362 -250
- plain/http/response.py +99 -197
- plain/internal/__init__.py +8 -1
- plain/internal/files/base.py +35 -19
- plain/internal/files/locks.py +19 -11
- plain/internal/files/move.py +8 -3
- plain/internal/files/temp.py +25 -6
- plain/internal/files/uploadedfile.py +47 -28
- plain/internal/files/uploadhandler.py +64 -58
- plain/internal/files/utils.py +24 -10
- plain/internal/handlers/base.py +34 -23
- plain/internal/handlers/exception.py +68 -65
- plain/internal/handlers/wsgi.py +65 -54
- plain/internal/middleware/headers.py +37 -11
- plain/internal/middleware/hosts.py +11 -13
- plain/internal/middleware/https.py +17 -7
- plain/internal/middleware/slash.py +14 -9
- plain/internal/reloader.py +77 -0
- plain/json.py +2 -1
- plain/logs/README.md +161 -62
- plain/logs/__init__.py +1 -1
- plain/logs/{loggers.py → app.py} +71 -67
- plain/logs/configure.py +63 -14
- plain/logs/debug.py +17 -6
- plain/logs/filters.py +15 -0
- plain/logs/formatters.py +7 -4
- plain/packages/README.md +105 -23
- plain/packages/config.py +15 -7
- plain/packages/registry.py +40 -15
- plain/paginator.py +31 -21
- plain/preflight/README.md +208 -23
- plain/preflight/__init__.py +5 -24
- plain/preflight/checks.py +12 -0
- plain/preflight/files.py +19 -13
- plain/preflight/registry.py +80 -58
- plain/preflight/results.py +37 -0
- plain/preflight/security.py +65 -71
- plain/preflight/settings.py +54 -0
- plain/preflight/urls.py +10 -48
- plain/runtime/README.md +115 -47
- plain/runtime/__init__.py +10 -6
- plain/runtime/global_settings.py +43 -33
- plain/runtime/secret.py +20 -0
- plain/runtime/user_settings.py +110 -38
- plain/runtime/utils.py +1 -1
- plain/server/LICENSE +35 -0
- plain/server/README.md +155 -0
- plain/server/__init__.py +9 -0
- plain/server/app.py +52 -0
- plain/server/arbiter.py +555 -0
- plain/server/config.py +118 -0
- plain/server/errors.py +31 -0
- plain/server/glogging.py +292 -0
- plain/server/http/__init__.py +12 -0
- plain/server/http/body.py +283 -0
- plain/server/http/errors.py +155 -0
- plain/server/http/message.py +400 -0
- plain/server/http/parser.py +70 -0
- plain/server/http/unreader.py +88 -0
- plain/server/http/wsgi.py +421 -0
- plain/server/pidfile.py +92 -0
- plain/server/sock.py +240 -0
- plain/server/util.py +317 -0
- plain/server/workers/__init__.py +6 -0
- plain/server/workers/base.py +304 -0
- plain/server/workers/sync.py +212 -0
- plain/server/workers/thread.py +399 -0
- plain/server/workers/workertmp.py +50 -0
- plain/signals/README.md +170 -1
- plain/signals/__init__.py +0 -1
- plain/signals/dispatch/dispatcher.py +49 -27
- plain/signing.py +131 -35
- plain/skills/README.md +36 -0
- plain/skills/plain-docs/SKILL.md +25 -0
- plain/skills/plain-install/SKILL.md +26 -0
- plain/skills/plain-request/SKILL.md +39 -0
- plain/skills/plain-shell/SKILL.md +24 -0
- plain/skills/plain-upgrade/SKILL.md +35 -0
- plain/templates/README.md +211 -20
- plain/templates/jinja/__init__.py +14 -27
- plain/templates/jinja/environments.py +5 -4
- plain/templates/jinja/extensions.py +12 -5
- plain/templates/jinja/filters.py +7 -2
- plain/templates/jinja/globals.py +2 -2
- plain/test/README.md +184 -22
- plain/test/client.py +340 -222
- plain/test/encoding.py +9 -6
- plain/test/exceptions.py +7 -2
- plain/urls/README.md +157 -73
- plain/urls/converters.py +18 -15
- plain/urls/exceptions.py +2 -2
- plain/urls/patterns.py +56 -40
- plain/urls/resolvers.py +38 -28
- plain/urls/utils.py +5 -1
- plain/utils/README.md +250 -3
- plain/utils/cache.py +17 -11
- plain/utils/crypto.py +21 -5
- plain/utils/datastructures.py +89 -56
- plain/utils/dateparse.py +9 -6
- plain/utils/deconstruct.py +15 -7
- plain/utils/decorators.py +5 -1
- plain/utils/dotenv.py +373 -0
- plain/utils/duration.py +8 -4
- plain/utils/encoding.py +14 -7
- plain/utils/functional.py +66 -49
- plain/utils/hashable.py +5 -1
- plain/utils/html.py +36 -22
- plain/utils/http.py +16 -9
- plain/utils/inspect.py +14 -6
- plain/utils/ipv6.py +7 -3
- plain/utils/itercompat.py +6 -1
- plain/utils/module_loading.py +7 -3
- plain/utils/regex_helper.py +37 -23
- plain/utils/safestring.py +14 -6
- plain/utils/text.py +41 -23
- plain/utils/timezone.py +33 -22
- plain/utils/tree.py +35 -19
- plain/validators.py +94 -52
- plain/views/README.md +156 -79
- plain/views/__init__.py +0 -1
- plain/views/base.py +25 -18
- plain/views/errors.py +13 -5
- plain/views/exceptions.py +4 -1
- plain/views/forms.py +6 -6
- plain/views/objects.py +52 -49
- plain/views/redirect.py +18 -15
- plain/views/templates.py +5 -3
- plain/wsgi.py +3 -1
- {plain-0.66.0.dist-info → plain-0.101.2.dist-info}/METADATA +4 -2
- plain-0.101.2.dist-info/RECORD +201 -0
- {plain-0.66.0.dist-info → plain-0.101.2.dist-info}/WHEEL +1 -1
- plain-0.101.2.dist-info/entry_points.txt +2 -0
- plain/AGENTS.md +0 -18
- plain/cli/agent/__init__.py +0 -20
- plain/cli/agent/docs.py +0 -80
- plain/cli/agent/md.py +0 -87
- plain/cli/agent/prompt.py +0 -45
- plain/cli/agent/request.py +0 -181
- plain/csrf/views.py +0 -31
- plain/logs/utils.py +0 -46
- plain/preflight/messages.py +0 -81
- plain/templates/AGENTS.md +0 -3
- plain-0.66.0.dist-info/RECORD +0 -168
- plain-0.66.0.dist-info/entry_points.txt +0 -4
- {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.
|
plain/server/__init__.py
ADDED
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)
|