1+ import os
12import stat
23
34from baize import staticfiles
45from baize .datastructures import URL
56from baize .exceptions import HTTPException
6- from baize .typing import Receive , Scope , Send
7+ from baize .typing import ASGIApp , Receive , Scope , Send
78
89from .responses import FileResponse , RedirectResponse , Response
910
1011
11- class Files (staticfiles .BaseFiles ):
12+ class Files (staticfiles .BaseFiles [ ASGIApp ] ):
1213 """
1314 Provide the ASGI application to download files in the specified path or
1415 the specified directory under the specified package.
1516
1617 Support request range and cache (304 status code).
17-
18- NOTE: Need users handle HTTPException(404).
1918 """
2019
20+ def file_response (
21+ self ,
22+ filepath : str ,
23+ stat_result : os .stat_result ,
24+ if_none_match : str ,
25+ if_modified_since : str ,
26+ ) -> Response :
27+ if self .if_none_match (
28+ FileResponse .generate_etag (stat_result ), if_none_match
29+ ) or self .if_modified_since (stat_result .st_ctime , if_modified_since ):
30+ response = Response (304 )
31+ else :
32+ response = FileResponse (filepath , stat_result = stat_result )
33+ self .set_response_headers (response )
34+ return response
35+
2136 async def __call__ (self , scope : Scope , receive : Receive , send : Send ) -> None :
2237 if_none_match : str = ""
2338 if_modified_since : str = ""
@@ -30,28 +45,24 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
3045 stat_result , is_file = self .check_path_is_file (filepath )
3146 if is_file and stat_result :
3247 assert filepath is not None # Just for type check
33- if self .if_none_match (
34- FileResponse .generate_etag (stat_result ), if_none_match
35- ) or self .if_modified_since (stat_result .st_ctime , if_modified_since ):
36- response = Response (304 )
37- else :
38- response = FileResponse (filepath , stat_result = stat_result )
39- self .set_response_headers (response )
40- return await response (scope , receive , send )
48+ return await self .file_response (
49+ filepath , stat_result , if_none_match , if_modified_since
50+ )(scope , receive , send )
4151
42- raise HTTPException (404 )
52+ if self .handle_404 is None :
53+ raise HTTPException (404 )
54+ else :
55+ return await self .handle_404 (scope , receive , send )
4356
4457
45- class Pages (staticfiles .BasePages ):
58+ class Pages (staticfiles .BasePages [ ASGIApp ], Files ):
4659 """
4760 Provide the ASGI application to download files in the specified path or
4861 the specified directory under the specified package.
4962 Unlike `Files`, when you visit a directory, it will try to return the content
5063 of the file named `index.html` in that directory.
5164
5265 Support request range and cache (304 status code).
53-
54- NOTE: Need users handle HTTPException(404).
5566 """
5667
5768 async def __call__ (self , scope : Scope , receive : Receive , send : Send ) -> None :
@@ -75,17 +86,15 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
7586 if stat_result is not None :
7687 assert filepath is not None # Just for type check
7788 if is_file :
78- if self .if_none_match (
79- FileResponse .generate_etag (stat_result ), if_none_match
80- ) or self .if_modified_since (stat_result .st_ctime , if_modified_since ):
81- response = Response (304 )
82- else :
83- response = FileResponse (filepath , stat_result = stat_result )
84- self .set_response_headers (response )
85- return await response (scope , receive , send )
89+ return await self .file_response (
90+ filepath , stat_result , if_none_match , if_modified_since
91+ )(scope , receive , send )
8692 if stat .S_ISDIR (stat_result .st_mode ):
8793 url = URL (scope = scope )
8894 url = url .replace (scheme = "" , path = url .path + "/" )
8995 return await RedirectResponse (url )(scope , receive , send )
9096
91- raise HTTPException (404 )
97+ if self .handle_404 is None :
98+ raise HTTPException (404 )
99+ else :
100+ return await self .handle_404 (scope , receive , send )
0 commit comments