1
+ from __future__ import annotations
2
+
1
3
import argparse
2
4
from hashlib import sha256
3
5
import json
9
11
10
12
from starlette .responses import Response
11
13
from starlette .staticfiles import StaticFiles
14
+ from starlette .types import Scope
12
15
from typing_extensions import override
13
16
17
+ #: A StaticFilesManifest maps the relative path to the static files root
18
+ #: (i.e. the input value of a template requiring a static file) to the relative
19
+ #: path that should be rendered in a template, and the full path of the file on
20
+ #: disk.
21
+ StaticFilesManifest : typing .TypeAlias = dict [str , tuple [str , pathlib .Path ]]
22
+
14
23
15
- def compile_static_files (* , destination : pathlib .Path , sources : typing . Sequence [ pathlib . Path ]) :
24
+ def compile_static_files (* , destination : pathlib .Path , manifest : StaticFilesManifest ) -> None :
16
25
"""Compile a static directory from one or more source directories."""
17
26
# This function is designed to write the static files, could be useful for serving static
18
27
# files via apache/nginx/etc.
19
- manifest = generate_manifest (sources )
20
28
file_map : dict [str , dict [str , str ]] = {'file-map' : {}}
21
29
22
30
for input_filename , (hashed_relpath , source_path ) in manifest .items ():
@@ -29,7 +37,7 @@ def compile_static_files(*, destination: pathlib.Path, sources: typing.Sequence[
29
37
(destination / '.gitignore' ).write_text ('*' )
30
38
31
39
32
- def generate_manifest (sources : typing .Sequence [pathlib .Path ]) -> dict [ str , tuple [ str , pathlib . Path ]] :
40
+ def generate_manifest (sources : typing .Sequence [pathlib .Path ]) -> StaticFilesManifest :
33
41
"""
34
42
Generate a manifest which maps template_rel_path to a (hashed_relpath, full_path) tuple.
35
43
"""
@@ -54,7 +62,7 @@ def generate_manifest(sources: typing.Sequence[pathlib.Path]) -> dict[str, tuple
54
62
55
63
56
64
class HashedStaticFileHandler (StaticFiles ):
57
- def __init__ (self , * , manifest , ** kwargs ):
65
+ def __init__ (self , * , manifest : StaticFilesManifest , ** kwargs ) -> None :
58
66
super ().__init__ (** kwargs )
59
67
self .manifest = manifest
60
68
self ._inverted_manifest = {src : path for src , path in manifest .values ()}
@@ -64,10 +72,10 @@ def lookup_path(self, path: str) -> tuple[str, os.stat_result | None]:
64
72
actual_path = self ._inverted_manifest .get (path )
65
73
if actual_path is None :
66
74
return super ().lookup_path (path )
67
- return actual_path , os .stat (actual_path )
75
+ return str ( actual_path ) , os .stat (actual_path )
68
76
69
77
@override
70
- async def get_response (self , path : str , scope ) -> Response :
78
+ async def get_response (self , path : str , scope : Scope ) -> Response :
71
79
response = await super ().get_response (path , scope )
72
80
if response .status_code in [200 , 304 ]:
73
81
response .headers ["Cache-Control" ] = "public, max-age=31536000, immutable"
@@ -95,7 +103,8 @@ def main(argv: typing.Sequence[str]) -> None:
95
103
96
104
def handle_compile (args : argparse .Namespace ):
97
105
print (f'Writing static files to { args .destination } ' )
98
- compile_static_files (destination = args .destination , sources = args .source )
106
+ manifest = generate_manifest (args .source )
107
+ compile_static_files (destination = args .destination , manifest = manifest )
99
108
100
109
101
110
if __name__ == '__main__' :
0 commit comments