def__init__(self,*,directory:PathLike|None=None,packages:list[str|tuple[str,str]]|None=None,html:bool=False,check_dir:bool=True,follow_symlink:bool=False,)->None:self.directory=directoryself.packages=packagesself.all_directories=self.get_directories(directory,packages)self.html=htmlself.config_checked=Falseself.follow_symlink=follow_symlinkifcheck_diranddirectoryisnotNoneandnotos.path.isdir(directory):raiseRuntimeError(f"Directory '{directory}' does not exist")
defget_directories(self,directory:PathLike|None=None,packages:list[str|tuple[str,str]]|None=None,)->list[PathLike]:""" Given `directory` and `packages` arguments, return a list of all the directories that should be used for serving static files from. """directories=[]ifdirectoryisnotNone:directories.append(directory)forpackageinpackagesor[]:ifisinstance(package,tuple):package,statics_dir=packageelse:statics_dir="statics"spec=importlib.util.find_spec(package)assertspecisnotNone,f"Package {package!r} could not be found."assertspec.originisnotNone,f"Package {package!r} could not be found."package_directory=os.path.normpath(os.path.join(spec.origin,"..",statics_dir))assertos.path.isdir(package_directory),f"Directory '{statics_dir!r}' in package {package!r} could not be found."directories.append(package_directory)returndirectories
Given the ASGI scope, return the path string to serve up,
with OS specific path separators, and any '..', '.' components removed.
PARAMETER
DESCRIPTION
scope
TYPE:Scope
Source code in starlette/staticfiles.py
106107108109110111112
defget_path(self,scope:Scope)->str:""" Given the ASGI scope, return the `path` string to serve up, with OS specific path separators, and any '..', '.' components removed. """route_path=get_route_path(scope)returnos.path.normpath(os.path.join(*route_path.split("/")))# noqa: E501
asyncdefget_response(self,path:str,scope:Scope)->Response:""" Returns an HTTP response, given the incoming path, method and request headers. """ifscope["method"]notin("GET","HEAD"):raiseHTTPException(status_code=405)try:full_path,stat_result=awaitanyio.to_thread.run_sync(self.lookup_path,path)exceptPermissionError:raiseHTTPException(status_code=401)exceptOSError:raiseifstat_resultandstat.S_ISREG(stat_result.st_mode):# We have a static file to serve.returnself.file_response(full_path,stat_result,scope)elifstat_resultandstat.S_ISDIR(stat_result.st_mode)andself.html:# We're in HTML mode, and have got a directory URL.# Check if we have 'index.html' file to serve.index_path=os.path.join(path,"index.html")full_path,stat_result=awaitanyio.to_thread.run_sync(self.lookup_path,index_path)ifstat_resultisnotNoneandstat.S_ISREG(stat_result.st_mode):ifnotscope["path"].endswith("/"):# Directory URLs should redirect to always end in "/".url=URL(scope=scope)url=url.replace(path=url.path+"/")returnRedirectResponse(url=url)returnself.file_response(full_path,stat_result,scope)ifself.html:# Check for '404.html' if we're in HTML mode.full_path,stat_result=awaitanyio.to_thread.run_sync(self.lookup_path,"404.html")ifstat_resultandstat.S_ISREG(stat_result.st_mode):returnFileResponse(full_path,stat_result=stat_result,status_code=404)raiseHTTPException(status_code=404)
deflookup_path(self,path:str)->tuple[str,os.stat_result|None]:fordirectoryinself.all_directories:joined_path=os.path.join(directory,path)ifself.follow_symlink:full_path=os.path.abspath(joined_path)else:full_path=os.path.realpath(joined_path)directory=os.path.realpath(directory)ifos.path.commonpath([full_path,directory])!=directory:# Don't allow misbehaving clients to break out of the static files# directory.continuetry:returnfull_path,os.stat(full_path)except(FileNotFoundError,NotADirectoryError):continuereturn"",None
Perform a one-off configuration check that StaticFiles is actually
pointed at a directory, so that we can raise loud errors rather than
just returning 404 responses.
asyncdefcheck_config(self)->None:""" Perform a one-off configuration check that StaticFiles is actually pointed at a directory, so that we can raise loud errors rather than just returning 404 responses. """ifself.directoryisNone:returntry:stat_result=awaitanyio.to_thread.run_sync(os.stat,self.directory)exceptFileNotFoundError:raiseRuntimeError(f"StaticFiles directory '{self.directory}' does not exist.")ifnot(stat.S_ISDIR(stat_result.st_mode)orstat.S_ISLNK(stat_result.st_mode)):raiseRuntimeError(f"StaticFiles path '{self.directory}' is not a directory.")
defis_not_modified(self,response_headers:Headers,request_headers:Headers)->bool:""" Given the request and response headers, return `True` if an HTTP "Not Modified" response could be returned instead. """try:if_none_match=request_headers["if-none-match"]etag=response_headers["etag"]ifetagin[tag.strip(" W/")fortaginif_none_match.split(",")]:returnTrueexceptKeyError:passtry:if_modified_since=parsedate(request_headers["if-modified-since"])last_modified=parsedate(response_headers["last-modified"])if(if_modified_sinceisnotNoneandlast_modifiedisnotNoneandif_modified_since>=last_modified):returnTrueexceptKeyError:passreturnFalse