From 267c8ba38d8b937d2251f296db35e7e859e61b1d Mon Sep 17 00:00:00 2001 From: Amin Alaee Date: Fri, 3 Dec 2021 10:37:34 +0100 Subject: [PATCH 1/3] Add staticfiles packages with directory --- starlette/staticfiles.py | 19 ++++++++++++------- tests/test_staticfiles.py | 6 ++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/starlette/staticfiles.py b/starlette/staticfiles.py index f7057539f..cbd732801 100644 --- a/starlette/staticfiles.py +++ b/starlette/staticfiles.py @@ -49,7 +49,12 @@ def __init__( self.all_directories = self.get_directories(directory, packages) self.html = html self.config_checked = False - if check_dir and directory is not None and not os.path.isdir(directory): + if ( + check_dir + and directory is not None + and not packages + and not os.path.isdir(directory) + ): raise RuntimeError(f"Directory '{directory}' does not exist") def get_directories( @@ -62,19 +67,19 @@ def get_directories( directories = [] if directory is not None: directories.append(directory) + else: + directory = "statics" for package in packages or []: spec = importlib.util.find_spec(package) assert spec is not None, f"Package {package!r} could not be found." - assert ( - spec.origin is not None - ), f"Directory 'statics' in package {package!r} could not be found." + assert spec.origin is not None, f"Package {package!r} could not be found." package_directory = os.path.normpath( - os.path.join(spec.origin, "..", "statics") + os.path.join(spec.origin, "..", directory) ) assert os.path.isdir( package_directory - ), f"Directory 'statics' in package {package!r} could not be found." + ), f"Directory {directory!r} in package {package!r} could not be found." directories.append(package_directory) return directories @@ -188,7 +193,7 @@ async def check_config(self) -> None: pointed at a directory, so that we can raise loud errors rather than just returning 404 responses. """ - if self.directory is None: + if self.directory is None or self.packages: return try: diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index 8057af689..f2d0e366a 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -67,6 +67,12 @@ def test_staticfiles_with_package(test_client_factory): assert response.status_code == 200 assert response.text == "123\n" + app = StaticFiles(packages=["tests"], directory="statics") + client = test_client_factory(app) + response = client.get("/example.txt") + assert response.status_code == 200 + assert response.text == "123\n" + def test_staticfiles_post(tmpdir, test_client_factory): path = os.path.join(tmpdir, "example.txt") From 1f70ccd8f63b8a23ede46133cd7a252d813fcd52 Mon Sep 17 00:00:00 2001 From: Amin Alaee Date: Fri, 3 Dec 2021 10:46:21 +0100 Subject: [PATCH 2/3] update docstring --- starlette/staticfiles.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/starlette/staticfiles.py b/starlette/staticfiles.py index cbd732801..a431cc3b2 100644 --- a/starlette/staticfiles.py +++ b/starlette/staticfiles.py @@ -63,6 +63,10 @@ def get_directories( """ Given `directory` and `packages` arguments, return a list of all the directories that should be used for serving static files from. + + It can take a single directory, + or a list of `packages` and look for default `statics` in each package, + or a list of `packages` with a `directory` to look for in each package. """ directories = [] if directory is not None: From a5e0e7054c10665ff9a0d7c849455477ae5cb4b8 Mon Sep 17 00:00:00 2001 From: Amin Alaee Date: Mon, 6 Dec 2021 13:43:19 +0100 Subject: [PATCH 3/3] change to independant directory variable --- docs/staticfiles.md | 12 +++++++++++- starlette/staticfiles.py | 29 ++++++++++++----------------- tests/test_staticfiles.py | 2 +- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/docs/staticfiles.md b/docs/staticfiles.md index d8786af4d..3591b4f9f 100644 --- a/docs/staticfiles.md +++ b/docs/staticfiles.md @@ -6,7 +6,7 @@ Starlette also includes a `StaticFiles` class for serving files in a given direc Signature: `StaticFiles(directory=None, packages=None, check_dir=True)` * `directory` - A string or [os.Pathlike][pathlike] denoting a directory path. -* `packages` - A list of strings of python packages. +* `packages` - A list of strings or list of tuples of strings of python packages. * `html` - Run in HTML mode. Automatically loads `index.html` for directories if such file exist. * `check_dir` - Ensure that the directory exists upon instantiation. Defaults to `True`. @@ -48,6 +48,16 @@ routes=[ app = Starlette(routes=routes) ``` +By default `StaticFiles` will look for `statics` directory in each package, +you can change the default directory by specifying a tuple of strings. + +```python +routes=[ + ... + Mount('/static', app=StaticFiles(packages=[('bootstrap4', 'static')]), name="static"), +] +``` + You may prefer to include static files directly inside the "static" directory rather than using Python packaging to include static files, but it can be useful for bundling up reusable components. diff --git a/starlette/staticfiles.py b/starlette/staticfiles.py index a431cc3b2..76e435310 100644 --- a/starlette/staticfiles.py +++ b/starlette/staticfiles.py @@ -40,7 +40,7 @@ def __init__( self, *, directory: PathLike = None, - packages: typing.List[str] = None, + packages: typing.List[typing.Union[str, typing.Tuple[str, str]]] = None, html: bool = False, check_dir: bool = True, ) -> None: @@ -49,41 +49,36 @@ def __init__( self.all_directories = self.get_directories(directory, packages) self.html = html self.config_checked = False - if ( - check_dir - and directory is not None - and not packages - and not os.path.isdir(directory) - ): + if check_dir and directory is not None and not os.path.isdir(directory): raise RuntimeError(f"Directory '{directory}' does not exist") def get_directories( - self, directory: PathLike = None, packages: typing.List[str] = None + self, + directory: PathLike = None, + packages: typing.List[typing.Union[str, typing.Tuple[str, str]]] = None, ) -> typing.List[PathLike]: """ Given `directory` and `packages` arguments, return a list of all the directories that should be used for serving static files from. - - It can take a single directory, - or a list of `packages` and look for default `statics` in each package, - or a list of `packages` with a `directory` to look for in each package. """ directories = [] if directory is not None: directories.append(directory) - else: - directory = "statics" for package in packages or []: + if isinstance(package, tuple): + package, statics_dir = package + else: + statics_dir = "statics" spec = importlib.util.find_spec(package) assert spec is not None, f"Package {package!r} could not be found." assert spec.origin is not None, f"Package {package!r} could not be found." package_directory = os.path.normpath( - os.path.join(spec.origin, "..", directory) + os.path.join(spec.origin, "..", statics_dir) ) assert os.path.isdir( package_directory - ), f"Directory {directory!r} in package {package!r} could not be found." + ), f"Directory '{statics_dir!r}' in package {package!r} could not be found." directories.append(package_directory) return directories @@ -197,7 +192,7 @@ async def check_config(self) -> None: pointed at a directory, so that we can raise loud errors rather than just returning 404 responses. """ - if self.directory is None or self.packages: + if self.directory is None: return try: diff --git a/tests/test_staticfiles.py b/tests/test_staticfiles.py index f2d0e366a..bd7e0de02 100644 --- a/tests/test_staticfiles.py +++ b/tests/test_staticfiles.py @@ -67,7 +67,7 @@ def test_staticfiles_with_package(test_client_factory): assert response.status_code == 200 assert response.text == "123\n" - app = StaticFiles(packages=["tests"], directory="statics") + app = StaticFiles(packages=[("tests", "statics")]) client = test_client_factory(app) response = client.get("/example.txt") assert response.status_code == 200