Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

# Testing
coverage==7.6.10
importlib-metadata==8.5.0
importlib-metadata==8.6.1
mypy==1.14.1
ruff==0.8.5
ruff==0.9.4
typing_extensions==4.12.2
types-contextvars==2.4.7.3
types-PyYAML==6.0.12.20241230
Expand All @@ -14,11 +14,11 @@ pytest==8.3.4
trio==0.28.0

# Documentation
black==24.10.0
black==25.1.0
mkdocs==1.6.1
mkdocs-material==9.5.49
mkdocs-material==9.6.1
mkdocstrings-python==1.13.0

# Packaging
build==1.2.2.post1
twine==6.0.1
twine==6.1.0
6 changes: 3 additions & 3 deletions starlette/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ def __init__(
"""
# The lifespan context function is a newer style that replaces
# on_startup / on_shutdown handlers. Use one or the other, not both.
assert lifespan is None or (
on_startup is None and on_shutdown is None
), "Use either 'lifespan' or 'on_startup'/'on_shutdown', not both."
assert lifespan is None or (on_startup is None and on_shutdown is None), (
"Use either 'lifespan' or 'on_startup'/'on_shutdown', not both."
)

self.debug = debug
self.state = State()
Expand Down
7 changes: 1 addition & 6 deletions starlette/datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,7 @@ async def close(self) -> None:
await run_in_threadpool(self.file.close)

def __repr__(self) -> str:
return (
f"{self.__class__.__name__}("
f"filename={self.filename!r}, "
f"size={self.size!r}, "
f"headers={self.headers!r})"
)
return f"{self.__class__.__name__}(filename={self.filename!r}, size={self.size!r}, headers={self.headers!r})"


class FormData(ImmutableMultiDict[str, typing.Union[UploadFile, str]]):
Expand Down
6 changes: 3 additions & 3 deletions starlette/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ async def _get_form(
max_part_size: int = 1024 * 1024,
) -> FormData:
if self._form is None: # pragma: no branch
assert (
parse_options_header is not None
), "The `python-multipart` library must be installed to use form parsing."
assert parse_options_header is not None, (
"The `python-multipart` library must be installed to use form parsing."
)
content_type_header = self.headers.get("Content-Type")
content_type: bytes
content_type, _ = parse_options_header(content_type_header)
Expand Down
5 changes: 1 addition & 4 deletions starlette/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,9 +529,6 @@ def generate_multipart(
return (
content_length,
lambda start, end: (
f"--{boundary}\n"
f"Content-Type: {content_type}\n"
f"Content-Range: bytes {start}-{end-1}/{max_size}\n"
"\n"
f"--{boundary}\nContent-Type: {content_type}\nContent-Range: bytes {start}-{end - 1}/{max_size}\n\n"
).encode("latin-1"),
)
3 changes: 1 addition & 2 deletions starlette/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,8 +623,7 @@ def __init__(
)
elif inspect.isgeneratorfunction(lifespan):
warnings.warn(
"generator function lifespans are deprecated, "
"use an @contextlib.asynccontextmanager function instead",
"generator function lifespans are deprecated, use an @contextlib.asynccontextmanager function instead",
DeprecationWarning,
)
self.lifespan_context = _wrap_gen_lifespan_context(
Expand Down
6 changes: 3 additions & 3 deletions starlette/staticfiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ def get_directories(
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, "..", statics_dir))
assert os.path.isdir(
package_directory
), f"Directory '{statics_dir!r}' in package {package!r} could not be found."
assert os.path.isdir(package_directory), (
f"Directory '{statics_dir!r}' in package {package!r} could not be found."
)
directories.append(package_directory)

return directories
Expand Down
18 changes: 9 additions & 9 deletions tests/test_formparsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ def test_too_many_fields_raise(
client = test_client_factory(app)
fields = []
for i in range(1001):
fields.append("--B\r\n" f'Content-Disposition: form-data; name="N{i}";\r\n\r\n' "\r\n")
fields.append(f'--B\r\nContent-Disposition: form-data; name="N{i}";\r\n\r\n\r\n')
data = "".join(fields).encode("utf-8")
with expectation:
res = client.post(
Expand All @@ -511,7 +511,7 @@ def test_too_many_files_raise(
client = test_client_factory(app)
fields = []
for i in range(1001):
fields.append("--B\r\n" f'Content-Disposition: form-data; name="N{i}"; filename="F{i}";\r\n\r\n' "\r\n")
fields.append(f'--B\r\nContent-Disposition: form-data; name="N{i}"; filename="F{i}";\r\n\r\n\r\n')
data = "".join(fields).encode("utf-8")
with expectation:
res = client.post(
Expand Down Expand Up @@ -540,7 +540,7 @@ def test_too_many_files_single_field_raise(
for i in range(1001):
# This uses the same field name "N" for all files, equivalent to a
# multifile upload form field
fields.append("--B\r\n" f'Content-Disposition: form-data; name="N"; filename="F{i}";\r\n\r\n' "\r\n")
fields.append(f'--B\r\nContent-Disposition: form-data; name="N"; filename="F{i}";\r\n\r\n\r\n')
data = "".join(fields).encode("utf-8")
with expectation:
res = client.post(
Expand All @@ -567,8 +567,8 @@ def test_too_many_files_and_fields_raise(
client = test_client_factory(app)
fields = []
for i in range(1001):
fields.append("--B\r\n" f'Content-Disposition: form-data; name="F{i}"; filename="F{i}";\r\n\r\n' "\r\n")
fields.append("--B\r\n" f'Content-Disposition: form-data; name="N{i}";\r\n\r\n' "\r\n")
fields.append(f'--B\r\nContent-Disposition: form-data; name="F{i}"; filename="F{i}";\r\n\r\n\r\n')
fields.append(f'--B\r\nContent-Disposition: form-data; name="N{i}";\r\n\r\n\r\n')
data = "".join(fields).encode("utf-8")
with expectation:
res = client.post(
Expand Down Expand Up @@ -598,7 +598,7 @@ def test_max_fields_is_customizable_low_raises(
client = test_client_factory(app)
fields = []
for i in range(2):
fields.append("--B\r\n" f'Content-Disposition: form-data; name="N{i}";\r\n\r\n' "\r\n")
fields.append(f'--B\r\nContent-Disposition: form-data; name="N{i}";\r\n\r\n\r\n')
data = "".join(fields).encode("utf-8")
with expectation:
res = client.post(
Expand Down Expand Up @@ -628,7 +628,7 @@ def test_max_files_is_customizable_low_raises(
client = test_client_factory(app)
fields = []
for i in range(2):
fields.append("--B\r\n" f'Content-Disposition: form-data; name="F{i}"; filename="F{i}";\r\n\r\n' "\r\n")
fields.append(f'--B\r\nContent-Disposition: form-data; name="F{i}"; filename="F{i}";\r\n\r\n\r\n')
data = "".join(fields).encode("utf-8")
with expectation:
res = client.post(
Expand All @@ -644,8 +644,8 @@ def test_max_fields_is_customizable_high(test_client_factory: TestClientFactory)
client = test_client_factory(make_app_max_parts(max_fields=2000, max_files=2000))
fields = []
for i in range(2000):
fields.append("--B\r\n" f'Content-Disposition: form-data; name="N{i}";\r\n\r\n' "\r\n")
fields.append("--B\r\n" f'Content-Disposition: form-data; name="F{i}"; filename="F{i}";\r\n\r\n' "\r\n")
fields.append(f'--B\r\nContent-Disposition: form-data; name="N{i}";\r\n\r\n\r\n')
fields.append(f'--B\r\nContent-Disposition: form-data; name="F{i}"; filename="F{i}";\r\n\r\n\r\n')
data = "".join(fields).encode("utf-8")
data += b"--B--\r\n"
res = client.post(
Expand Down
4 changes: 2 additions & 2 deletions tests/test_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,12 +687,12 @@ def test_file_response_range_invalid(file_response_client: TestClient) -> None:


def test_file_response_range_head_max(file_response_client: TestClient) -> None:
response = file_response_client.head("/", headers={"Range": f"bytes=0-{len(README.encode('utf8'))+1}"})
response = file_response_client.head("/", headers={"Range": f"bytes=0-{len(README.encode('utf8')) + 1}"})
assert response.status_code == 206


def test_file_response_range_416(file_response_client: TestClient) -> None:
response = file_response_client.head("/", headers={"Range": f"bytes={len(README.encode('utf8'))+1}-"})
response = file_response_client.head("/", headers={"Range": f"bytes={len(README.encode('utf8')) + 1}-"})
assert response.status_code == 416
assert response.headers["Content-Range"] == f"*/{len(README.encode('utf8'))}"

Expand Down