plain 0.35.0__py3-none-any.whl → 0.37.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/README.md CHANGED
@@ -12,55 +12,55 @@ To quickly get started with Plain, visit [plainframework.com/start/](https://pla
12
12
 
13
13
  The `plain` package includes everything you need to start handling web requests with Python:
14
14
 
15
- - [assets](assets/README.md) - Serve static files and assets.
16
- - [cli](cli/README.md) - The `plain` CLI, powered by Click.
17
- - [csrf](csrf/README.md) - Cross-Site Request Forgery protection.
18
- - [forms](forms/README.md) - HTML forms and form validation.
19
- - [http](http/README.md) - HTTP request and response handling.
20
- - [logs](logs/README.md) - Logging configuration and utilities.
21
- - [preflight](preflight/README.md) - Preflight checks for your app.
22
- - [runtime](runtime/README.md) - Runtime settings and configuration.
23
- - [templates](templates/README.md) - Jinja2 templates and rendering.
24
- - [test](test/README.md) - Test utilities and fixtures.
25
- - [urls](urls/README.md) - URL routing and request dispatching.
26
- - [views](views/README.md) - Class-based views and request handlers.
15
+ - [assets](./assets/README.md) - Serve static files and assets.
16
+ - [cli](./cli/README.md) - The `plain` CLI, powered by Click.
17
+ - [csrf](./csrf/README.md) - Cross-Site Request Forgery protection.
18
+ - [forms](./forms/README.md) - HTML forms and form validation.
19
+ - [http](./http/README.md) - HTTP request and response handling.
20
+ - [logs](./logs/README.md) - Logging configuration and utilities.
21
+ - [preflight](./preflight/README.md) - Preflight checks for your app.
22
+ - [runtime](./runtime/README.md) - Runtime settings and configuration.
23
+ - [templates](./templates/README.md) - Jinja2 templates and rendering.
24
+ - [test](./test/README.md) - Test utilities and fixtures.
25
+ - [urls](./urls/README.md) - URL routing and request dispatching.
26
+ - [views](./views/README.md) - Class-based views and request handlers.
27
27
 
28
28
  ## Foundational Packages
29
29
 
30
- - [plain.models](/plain-models/README.md) - Define and interact with your database models.
31
- - [plain.cache](/plain-cache/README.md) - A database-driven general purpose cache.
32
- - [plain.email](/plain-email/README.md) - Send emails with SMTP or custom backends.
33
- - [plain.sessions](/plain-sessions/README.md) - User sessions and cookies.
34
- - [plain.worker](/plain-worker/README.md) - Backgrounb jobs stored in the database.
35
- - [plain.api](/plain-api/README.md) - Build APIs with Plain views.
30
+ - [plain.models](/plain-models/plain/models/README.md) - Define and interact with your database models.
31
+ - [plain.cache](/plain-cache/plain/cache/README.md) - A database-driven general purpose cache.
32
+ - [plain.email](/plain-email/plain/email/README.md) - Send emails with SMTP or custom backends.
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.
35
+ - [plain.api](/plain-api/plain/api/README.md) - Build APIs with Plain views.
36
36
 
37
37
  ## Auth Packages
38
38
 
39
- - [plain.auth](/plain-auth/README.md) - User authentication and authorization.
40
- - [plain.oauth](/plain-oauth/README.md) - OAuth authentication and API access.
41
- - [plain.passwords](/plain-passwords/README.md) - Password-based login and registration.
42
- - [plain.loginlink](/plain-loginlink/README.md) - Login links for passwordless authentication.
39
+ - [plain.auth](/plain-auth/plain/auth/README.md) - User authentication and authorization.
40
+ - [plain.oauth](/plain-oauth/plain/oauth/README.md) - OAuth authentication and API access.
41
+ - [plain.passwords](/plain-passwords/plain/passwords/README.md) - Password-based login and registration.
42
+ - [plain.loginlink](/plain-loginlink/plain/loginlink/README.md) - Login links for passwordless authentication.
43
43
 
44
44
  ## Admin Packages
45
45
 
46
- - [plain.admin](/plain-admin/README.md) - An admin interface for back-office tasks.
47
- - [plain.flags](/plain-flags/README.md) - Feature flags.
48
- - [plain.support](/plain-support/README.md) - Customer support forms.
49
- - [plain.redirection](/plain-redirection/README.md) - Redirects managed in the database.
50
- - [plain.pageviews](/plain-pageviews/README.md) - Basic self-hosted page view tracking and reporting.
46
+ - [plain.admin](/plain-admin/plain/admin/README.md) - An admin interface for back-office tasks.
47
+ - [plain.flags](/plain-flags/plain/flags/README.md) - Feature flags.
48
+ - [plain.support](/plain-support/plain/support/README.md) - Customer support forms.
49
+ - [plain.redirection](/plain-redirection/plain/redirection/README.md) - Redirects managed in the database.
50
+ - [plain.pageviews](/plain-pageviews/plain/pageviews/README.md) - Basic self-hosted page view tracking and reporting.
51
51
 
52
52
  ## Dev Packages
53
53
 
54
- - [plain.dev](/plain-dev/README.md) - A single command for local development.
55
- - [plain.pytest](/plain-pytest/README.md) - Pytest fixtures and helpers.
56
- - [plain.code](/plain-code/README.md) - Code formatting and linting.
57
- - [plain.tunnel](/plain-tunnel/README.md) - Expose your local server to the internet.
54
+ - [plain.dev](/plain-dev/plain/dev/README.md) - A single command for local development.
55
+ - [plain.pytest](/plain-pytest/plain/pytest/README.md) - Pytest fixtures and helpers.
56
+ - [plain.code](/plain-code/plain/code/README.md) - Code formatting and linting.
57
+ - [plain.tunnel](/plain-tunnel/plain/tunnel/README.md) - Expose your local server to the internet.
58
58
 
59
59
  ## Frontend Packages
60
60
 
61
- - [plain.tailwind](/plain-tailwind/README.md) - Tailwind CSS integration without Node.js.
62
- - [plain.htmx](/plain-htmx/README.md) - HTMX integrated into views and templates.
63
- - [plain.elements](/plain-elements/README.md) - Server-side HTML components.
64
- - [plain.pages](/plain-pages/README.md) - Static pages with Markdown and Jinja2.
65
- - [plain.esbuild](/plain-esbuild/README.md) - Simple JavaScript bundling and minification.
66
- - [plain.vendor](/plain-vendor/README.md) - Vendor JavaScript and CSS libraries.
61
+ - [plain.tailwind](/plain-tailwind/plain/tailwind/README.md) - Tailwind CSS integration without Node.js.
62
+ - [plain.htmx](/plain-htmx/plain/htmx/README.md) - HTMX integrated into views and templates.
63
+ - [plain.elements](/plain-elements/plain/elements/README.md) - Server-side HTML components.
64
+ - [plain.pages](/plain-pages/plain/pages/README.md) - Static pages with Markdown and Jinja2.
65
+ - [plain.esbuild](/plain-esbuild/plain/esbuild/README.md) - Simple JavaScript bundling and minification.
66
+ - [plain.vendor](/plain-vendor/plain/vendor/README.md) - Vendor JavaScript and CSS libraries.
plain/assets/views.py CHANGED
@@ -254,7 +254,7 @@ class AssetView(View):
254
254
 
255
255
  if not range_header.startswith("bytes="):
256
256
  return Response(
257
- status=416, headers=[("Content-Range", f"bytes */{file_size}")]
257
+ status_code=416, headers=[("Content-Range", f"bytes */{file_size}")]
258
258
  )
259
259
 
260
260
  range_values = range_header.split("=")[1].split("-")
@@ -263,7 +263,7 @@ class AssetView(View):
263
263
 
264
264
  if start >= file_size:
265
265
  return Response(
266
- status=416, headers=[("Content-Range", f"bytes */{file_size}")]
266
+ status_code=416, headers=[("Content-Range", f"bytes */{file_size}")]
267
267
  )
268
268
 
269
269
  end = min(end, file_size - 1)
@@ -272,7 +272,7 @@ class AssetView(View):
272
272
  f.seek(start)
273
273
  content = f.read(end - start + 1)
274
274
 
275
- response = StreamingResponse(BytesIO(content), status=206)
275
+ response = StreamingResponse(BytesIO(content), status_code=206)
276
276
  response.headers = self.update_headers(response.headers, path)
277
277
  response.headers["Content-Range"] = f"bytes {start}-{end}/{file_size}"
278
278
  response.headers["Content-Length"] = str(end - start + 1)
plain/cli/build.py ADDED
@@ -0,0 +1,107 @@
1
+ import shutil
2
+ import subprocess
3
+ import sys
4
+ import tomllib
5
+ from importlib.metadata import entry_points
6
+ from pathlib import Path
7
+
8
+ import click
9
+
10
+ import plain.runtime
11
+ from plain.assets.compile import compile_assets, get_compiled_path
12
+
13
+
14
+ @click.command()
15
+ @click.option(
16
+ "--keep-original/--no-keep-original",
17
+ "keep_original",
18
+ is_flag=True,
19
+ default=False,
20
+ help="Keep the original assets",
21
+ )
22
+ @click.option(
23
+ "--fingerprint/--no-fingerprint",
24
+ "fingerprint",
25
+ is_flag=True,
26
+ default=True,
27
+ help="Fingerprint the assets",
28
+ )
29
+ @click.option(
30
+ "--compress/--no-compress",
31
+ "compress",
32
+ is_flag=True,
33
+ default=True,
34
+ help="Compress the assets",
35
+ )
36
+ def build(keep_original, fingerprint, compress):
37
+ """Pre-deployment build step (compile assets, css, js, etc.)"""
38
+
39
+ if not keep_original and not fingerprint:
40
+ click.secho(
41
+ "You must either keep the original assets or fingerprint them.",
42
+ fg="red",
43
+ err=True,
44
+ )
45
+ sys.exit(1)
46
+
47
+ # Run user-defined build commands first
48
+ pyproject_path = plain.runtime.APP_PATH.parent / "pyproject.toml"
49
+ if pyproject_path.exists():
50
+ with pyproject_path.open("rb") as f:
51
+ pyproject = tomllib.load(f)
52
+
53
+ for name, data in (
54
+ pyproject.get("tool", {})
55
+ .get("plain", {})
56
+ .get("build", {})
57
+ .get("run", {})
58
+ .items()
59
+ ):
60
+ click.secho(f"Running {name} from pyproject.toml", bold=True)
61
+ result = subprocess.run(data["cmd"], shell=True)
62
+ print()
63
+ if result.returncode:
64
+ click.secho(f"Error in {name} (exit {result.returncode})", fg="red")
65
+ sys.exit(result.returncode)
66
+
67
+ # Then run installed package build steps (like tailwind, typically should run last...)
68
+ for entry_point in entry_points(group="plain.build"):
69
+ click.secho(f"Running {entry_point.name}", bold=True)
70
+ result = entry_point.load()()
71
+ print()
72
+
73
+ # Compile our assets
74
+ target_dir = get_compiled_path()
75
+ click.secho(f"Compiling assets to {target_dir}", bold=True)
76
+ if target_dir.exists():
77
+ click.secho("(clearing previously compiled assets)")
78
+ shutil.rmtree(target_dir)
79
+ target_dir.mkdir(parents=True, exist_ok=True)
80
+
81
+ total_files = 0
82
+ total_compiled = 0
83
+
84
+ for url_path, resolved_url_path, compiled_paths in compile_assets(
85
+ target_dir=target_dir,
86
+ keep_original=keep_original,
87
+ fingerprint=fingerprint,
88
+ compress=compress,
89
+ ):
90
+ if url_path == resolved_url_path:
91
+ click.secho(url_path, bold=True)
92
+ else:
93
+ click.secho(url_path, bold=True, nl=False)
94
+ click.secho(" → ", fg="yellow", nl=False)
95
+ click.echo(resolved_url_path)
96
+
97
+ print("\n".join(f" {Path(p).relative_to(Path.cwd())}" for p in compiled_paths))
98
+
99
+ total_files += 1
100
+ total_compiled += len(compiled_paths)
101
+
102
+ click.secho(
103
+ f"\nCompiled {total_files} assets into {total_compiled} files", fg="green"
104
+ )
105
+
106
+ # TODO could do a jinja pre-compile here too?
107
+ # environment.compile_templates() but it needs a target, ignore_errors=False