Skip to content

Commit

Permalink
Merge branch 'master' into python35-port
Browse files Browse the repository at this point in the history
  • Loading branch information
em92 committed Sep 11, 2020
2 parents 55c67c3 + 31ad4e5 commit 9693a3f
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 12 deletions.
6 changes: 6 additions & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.13.8

* Revert `Queue(maxsize=1)` fix for `BaseHTTPMiddleware` middleware classes and streaming responses.

* The `StaticFiles` constructor now allows `pathlib.Path` in addition to strings for its `directory` argument.

## 0.13.7

* Fix high memory usage when using `BaseHTTPMiddleware` middleware classes and streaming responses.
Expand Down
4 changes: 3 additions & 1 deletion docs/staticfiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,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 denoting a directory path.
* `directory` - A string or [os.Pathlike][pathlike] denoting a directory path.
* `packages` - A list 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`.
Expand Down Expand Up @@ -51,3 +51,5 @@ app = Starlette(routes=routes)
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.

[pathlike]: https://docs.python.org/3/library/os.html#os.PathLike
2 changes: 1 addition & 1 deletion starlette/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.13.7"
__version__ = "0.13.8"
2 changes: 1 addition & 1 deletion starlette/middleware/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:

async def call_next(self, request: Request) -> Response:
loop = asyncio.get_event_loop()
queue = asyncio.Queue(maxsize=1) # type: ignore
queue = asyncio.Queue() # type: ignore

scope = request.scope
receive = request.receive
Expand Down
14 changes: 12 additions & 2 deletions starlette/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import json
import os
import stat
import sys
import typing
from email.utils import formatdate
from mimetypes import guess_type
from mimetypes import guess_type as mimetypes_guess_type
from urllib.parse import quote, quote_plus

from async_generator import isasyncgen
Expand All @@ -31,6 +32,15 @@
ujson = None # type: ignore


# Compatibility wrapper for `mimetypes.guess_type` to support `os.PathLike` on <py3.8
def guess_type(
url: typing.Union[str, "os.PathLike[str]"], strict: bool = True
) -> typing.Tuple[typing.Optional[str], typing.Optional[str]]:
if sys.version_info < (3, 8): # pragma: no cover
url = os.fspath(url)
return mimetypes_guess_type(url, strict)


class Response:
media_type = None
charset = "utf-8"
Expand Down Expand Up @@ -240,7 +250,7 @@ class FileResponse(Response):

def __init__(
self,
path: str,
path: typing.Union[str, "os.PathLike[str]"],
status_code: int = 200,
headers: dict = None,
media_type: str = None,
Expand Down
18 changes: 11 additions & 7 deletions starlette/staticfiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
)
from starlette.types import Receive, Scope, Send

PathLike = typing.Union[str, "os.PathLike[str]"]


class NotModifiedResponse(Response):
NOT_MODIFIED_HEADERS = (
Expand All @@ -41,7 +43,7 @@ class StaticFiles:
def __init__(
self,
*,
directory: str = None,
directory: PathLike = None,
packages: typing.List[str] = None,
html: bool = False,
check_dir: bool = True
Expand All @@ -55,8 +57,8 @@ def __init__(
raise RuntimeError("Directory '{}' does not exist".format(directory))

def get_directories(
self, directory: str = None, packages: typing.List[str] = None
) -> typing.List[str]:
self, directory: PathLike = None, packages: typing.List[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.
Expand All @@ -75,13 +77,15 @@ def get_directories(
), "Directory 'statics' in package {} could not be found.".format(
repr(package)
)
directory = os.path.normpath(os.path.join(spec.origin, "..", "statics"))
package_directory = os.path.normpath(
os.path.join(spec.origin, "..", "statics")
)
assert os.path.isdir(
directory
package_directory
), "Directory 'statics' in package {} could not be found.".format(
repr(package)
)
directories.append(directory)
directories.append(package_directory)

return directories

Expand Down Expand Up @@ -160,7 +164,7 @@ async def lookup_path(

def file_response(
self,
full_path: str,
full_path: PathLike,
stat_result: os.stat_result,
scope: Scope,
status_code: int = 200,
Expand Down
14 changes: 14 additions & 0 deletions tests/test_staticfiles.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
import os
import pathlib
import time

import pytest
Expand All @@ -23,6 +24,19 @@ def test_staticfiles(tmpdir):
assert response.text == "<file content>"


def test_staticfiles_with_pathlib(tmpdir):
base_dir = pathlib.Path(tmpdir)
path = base_dir / "example.txt"
with open(path, "w") as file:
file.write("<file content>")

app = StaticFiles(directory=base_dir)
client = TestClient(app)
response = client.get("/example.txt")
assert response.status_code == 200
assert response.text == "<file content>"


def test_staticfiles_head_with_middleware(tmpdir):
"""
see https://github.com/encode/starlette/pull/935
Expand Down

0 comments on commit 9693a3f

Please sign in to comment.