Skip to content
This repository was archived by the owner on Dec 9, 2022. It is now read-only.

Commit 05d3284

Browse files
committed
1.1.1
1 parent 2ab538d commit 05d3284

File tree

8 files changed

+57
-19
lines changed

8 files changed

+57
-19
lines changed

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ repos:
2424
hooks:
2525
- id: isort
2626
- repo: https://github.com/psf/black
27-
rev: 22.8.0
27+
rev: 22.10.0
2828
hooks:
2929
- id: black
3030
args: [--config=./pyproject.toml]
@@ -83,7 +83,7 @@ repos:
8383
additional_dependencies: [mkdocs_gen_files, anyio, starlette]
8484
exclude: "docs"
8585
- repo: https://github.com/dosisod/refurb
86-
rev: v1.2.0
86+
rev: v1.3.0
8787
hooks:
8888
- id: refurb
8989
exclude: "test_*|docs"
@@ -101,7 +101,7 @@ repos:
101101
- id: mypy
102102
additional_dependencies: [mkdocs_gen_files, anyio, starlette]
103103
- repo: https://github.com/RobertCraigie/pyright-python
104-
rev: v1.1.273
104+
rev: v1.1.274
105105
hooks:
106106
- id: pyright
107107
additional_dependencies: [mkdocs_gen_files, pytest, anyio, starlette]

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@
77
[1.1.0]
88

99
- add support for configurable `charset` in decoder.
10+
11+
[1.1.1]
12+
13+
- fix UnicodeDecodeError for file uploads.

poetry.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "starlite-multipart"
3-
version = "1.1.0"
3+
version = "1.1.1"
44
description = "Toolkit for working with multipart/formdata messages."
55
authors = ["Na'aman Hirschfeld <nhirschfeld@gmail.com>"]
66
maintainers = ["Na'aman Hirschfeld <nhirschfeld@gmail.com>", "Peter Schutt <peter.github@proton.me>", "Cody Fincher <cody.fincher@gmail.com>"]

starlite_multipart/parser.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ async def _parse_chunk(self) -> List[Tuple[str, Union[str, UploadFile]]]:
6262
if upload_file is None:
6363
data.extend(event.data)
6464
if not event.more_data:
65-
items.append((field_name, data.decode(self.charset)))
65+
try:
66+
items.append((field_name, data.decode(self.charset)))
67+
except UnicodeDecodeError:
68+
items.append((field_name, data.decode("latin-1")))
6669
data.clear()
6770
else:
6871
await upload_file.write(event.data)

tests/flower.jpeg

71.2 KB
Loading

tests/test_image_upload.py

Whitespace-only changes.

tests/test_multipart_parser.py

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Tests in this file have been adapted from Starlette."""
22

33
import os
4+
from os.path import abspath, dirname, join
45
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Tuple, Union
56

67
import pytest
@@ -55,7 +56,7 @@ def test_client_factory() -> Callable[[Any], TestClient]:
5556
FORCE_MULTIPART = ForceMultipartDict()
5657

5758

58-
async def app(scope: "Scope", receive: "Receive", send: "Send") -> None:
59+
async def standard_app(scope: "Scope", receive: "Receive", send: "Send") -> None:
5960
request = Request(scope, receive)
6061
data = await request.form()
6162
output = {}
@@ -130,7 +131,7 @@ async def app_read_body(scope: "Scope", receive: "Receive", send: "Send") -> Non
130131

131132

132133
def test_multipart_request_data(test_client_factory: Callable[[Any], TestClient]) -> None:
133-
client = test_client_factory(app)
134+
client = test_client_factory(standard_app)
134135
response = client.post("/", data={"some": "data"}, files=FORCE_MULTIPART)
135136
assert response.json() == {"some": "data"}
136137

@@ -140,7 +141,7 @@ def test_multipart_request_files(tmpdir: Any, test_client_factory: Callable[[Any
140141
with open(path, "wb") as file:
141142
file.write(b"<file content>")
142143

143-
client = test_client_factory(app)
144+
client = test_client_factory(standard_app)
144145
with open(path, "rb") as f:
145146
response = client.post("/", files={"test": f})
146147
assert response.json() == {
@@ -159,7 +160,7 @@ def test_multipart_request_files_with_content_type(
159160
with open(path, "wb") as file:
160161
file.write(b"<file content>")
161162

162-
client = test_client_factory(app)
163+
client = test_client_factory(standard_app)
163164
with open(path, "rb") as f:
164165
response = client.post("/", files={"test": ("test.txt", f, "text/plain")})
165166
assert response.json() == {
@@ -180,7 +181,7 @@ def test_multipart_request_multiple_files(tmpdir: Any, test_client_factory: Call
180181
with open(path2, "wb") as file:
181182
file.write(b"<file2 content>")
182183

183-
client = test_client_factory(app)
184+
client = test_client_factory(standard_app)
184185
with open(path1, "rb") as f1, open(path2, "rb") as f2:
185186
response = client.post("/", files={"test1": f1, "test2": ("test2.txt", f2, "text/plain")})
186187
assert response.json() == {
@@ -266,7 +267,7 @@ def test_multi_items(tmpdir: Any, test_client_factory: Callable[[Any], TestClien
266267

267268

268269
def test_multipart_request_mixed_files_and_data(test_client_factory: Callable[[Any], TestClient]) -> None:
269-
client = test_client_factory(app)
270+
client = test_client_factory(standard_app)
270271
response = client.post(
271272
"/",
272273
data=(
@@ -299,7 +300,7 @@ def test_multipart_request_mixed_files_and_data(test_client_factory: Callable[[A
299300

300301

301302
def test_multipart_request_with_charset_for_filename(test_client_factory: Callable[[Any], TestClient]) -> None:
302-
client = test_client_factory(app)
303+
client = test_client_factory(standard_app)
303304
response = client.post(
304305
"/",
305306
data=(
@@ -322,7 +323,7 @@ def test_multipart_request_with_charset_for_filename(test_client_factory: Callab
322323

323324

324325
def test_multipart_request_without_charset_for_filename(test_client_factory: Callable[[Any], TestClient]) -> None:
325-
client = test_client_factory(app)
326+
client = test_client_factory(standard_app)
326327
response = client.post(
327328
"/",
328329
data=(
@@ -345,7 +346,7 @@ def test_multipart_request_without_charset_for_filename(test_client_factory: Cal
345346

346347

347348
def test_multipart_request_with_encoded_value(test_client_factory: Callable[[Any], TestClient]) -> None:
348-
client = test_client_factory(app)
349+
client = test_client_factory(standard_app)
349350
response = client.post(
350351
"/",
351352
data=(
@@ -361,7 +362,7 @@ def test_multipart_request_with_encoded_value(test_client_factory: Callable[[Any
361362

362363

363364
def test_no_request_data(test_client_factory: Callable[[Any], TestClient]) -> None:
364-
client = test_client_factory(app)
365+
client = test_client_factory(standard_app)
365366
response = client.post("/")
366367
assert response.json() == {}
367368

@@ -385,7 +386,7 @@ def test_postman_multipart_form_data(test_client_factory: Callable[[Any], TestCl
385386
"content-length": "2455",
386387
}
387388

388-
client = test_client_factory(app)
389+
client = test_client_factory(standard_app)
389390
response = client.post("/", data=postman_body, headers=postman_headers)
390391
assert response.json() == {
391392
"attributes": {
@@ -399,3 +400,33 @@ def test_postman_multipart_form_data(test_client_factory: Callable[[Any], TestCl
399400
"content_type": "application/octet-stream",
400401
},
401402
}
403+
404+
405+
def test_image_upload(test_client_factory: Callable[[Any], TestClient]) -> None:
406+
async def test_app(scope: "Scope", receive: "Receive", send: "Send") -> None:
407+
request = Request(scope, receive)
408+
data = await request.form()
409+
output: Dict[str, list] = {} # type: ignore
410+
for key, value in data.multi_items():
411+
if key not in output:
412+
output[key] = []
413+
if isinstance(value, UploadFile):
414+
content = await value.read()
415+
output[key].append(
416+
{
417+
"filename": value.filename,
418+
"content": content.decode("latin-1"),
419+
"content_type": value.content_type,
420+
}
421+
)
422+
else:
423+
output[key].append(value)
424+
await request.close()
425+
response = JSONResponse(output)
426+
await response(scope, receive, send)
427+
428+
client = test_client_factory(test_app)
429+
430+
with open(join(dirname(abspath(__file__)), "flower.jpeg"), "rb") as f:
431+
data = f.read()
432+
client.post("http://localhost:8000/", files={"flower": data})

0 commit comments

Comments
 (0)