# 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
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
"""
# 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()
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]]):
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)
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"),
)
)
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(
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
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(
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(
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(
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(
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(
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(
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(
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'))}"