plain 0.34.1__py3-none-any.whl → 0.36.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/assets/views.py +3 -3
- plain/cli/build.py +107 -0
- plain/cli/core.py +17 -685
- plain/cli/docs.py +211 -0
- plain/cli/preflight.py +127 -0
- plain/cli/scaffold.py +53 -0
- plain/cli/settings.py +60 -0
- plain/cli/shell.py +56 -0
- plain/cli/startup.py +24 -12
- plain/cli/urls.py +87 -0
- plain/cli/utils.py +15 -0
- plain/csrf/README.md +1 -1
- plain/csrf/middleware.py +3 -3
- plain/http/README.md +1 -1
- plain/http/response.py +15 -9
- plain/runtime/global_settings.py +8 -1
- plain/test/client.py +8 -8
- plain/views/README.md +1 -1
- plain/views/base.py +3 -2
- plain/views/objects.py +3 -65
- plain/views/redirect.py +5 -0
- plain/views/templates.py +11 -9
- {plain-0.34.1.dist-info → plain-0.36.0.dist-info}/METADATA +1 -1
- {plain-0.34.1.dist-info → plain-0.36.0.dist-info}/RECORD +27 -19
- {plain-0.34.1.dist-info → plain-0.36.0.dist-info}/WHEEL +0 -0
- {plain-0.34.1.dist-info → plain-0.36.0.dist-info}/entry_points.txt +0 -0
- {plain-0.34.1.dist-info → plain-0.36.0.dist-info}/licenses/LICENSE +0 -0
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
|
-
|
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
|
-
|
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),
|
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
|