python-multipart 0.0.17__tar.gz → 0.0.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_multipart-0.0.17 → python_multipart-0.0.18}/CHANGELOG.md +4 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/PKG-INFO +2 -3
- {python_multipart-0.0.17 → python_multipart-0.0.18}/pyproject.toml +2 -2
- {python_multipart-0.0.17 → python_multipart-0.0.18}/python_multipart/__init__.py +1 -1
- {python_multipart-0.0.17 → python_multipart-0.0.18}/python_multipart/multipart.py +4 -4
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_multipart.py +39 -1
- {python_multipart-0.0.17 → python_multipart-0.0.18}/.gitignore +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/LICENSE.txt +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/README.md +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/multipart/__init__.py +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/multipart/decoders.py +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/multipart/exceptions.py +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/multipart/multipart.py +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/python_multipart/decoders.py +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/python_multipart/exceptions.py +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/python_multipart/py.typed +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/__init__.py +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/compat.py +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CRLF_in_header.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CRLF_in_header.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CR_in_header.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CR_in_header.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CR_in_header_value.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CR_in_header_value.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary_without_CR.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary_without_CR.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary_without_LF.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary_without_LF.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary_without_final_hyphen.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary_without_final_hyphen.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_end_of_headers.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_end_of_headers.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_header_char.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_header_char.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_initial_boundary.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_initial_boundary.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/base64_encoding.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/base64_encoding.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/empty_header.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/empty_header.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/header_with_number.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/header_with_number.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/mixed_plain_and_base64_encoding.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/mixed_plain_and_base64_encoding.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/multiple_fields.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/multiple_fields.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/multiple_files.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/multiple_files.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/quoted_printable_encoding.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/quoted_printable_encoding.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_blocks.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_blocks.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_longer.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_longer.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_single_file.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_single_file.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_with_leading_newlines.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_with_leading_newlines.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_file.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_file.yaml +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/utf8_filename.http +0 -0
- {python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/utf8_filename.yaml +0 -0
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.0.18 (2024-11-28)
|
|
4
|
+
|
|
5
|
+
* Hard break if found data after last boundary on `MultipartParser` [#189](https://github.com/Kludex/python-multipart/pull/189).
|
|
6
|
+
|
|
3
7
|
## 0.0.17 (2024-10-31)
|
|
4
8
|
|
|
5
9
|
* Handle PermissionError in fallback code for old import name [#182](https://github.com/Kludex/python-multipart/pull/182).
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: python-multipart
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.18
|
|
4
4
|
Summary: A streaming multipart parser for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/Kludex/python-multipart
|
|
6
6
|
Project-URL: Documentation, https://kludex.github.io/python-multipart/
|
|
7
7
|
Project-URL: Changelog, https://github.com/Kludex/python-multipart/blob/master/CHANGELOG.md
|
|
8
8
|
Project-URL: Source, https://github.com/Kludex/python-multipart
|
|
9
9
|
Author-email: Andrew Dunham <andrew@du.nham.ca>, Marcelo Trylesinski <marcelotryle@gmail.com>
|
|
10
|
-
License
|
|
11
|
-
License-File: LICENSE.txt
|
|
10
|
+
License: Apache-2.0
|
|
12
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
12
|
Classifier: Environment :: Web Environment
|
|
14
13
|
Classifier: Intended Audience :: Developers
|
|
@@ -44,7 +44,7 @@ dev-dependencies = [
|
|
|
44
44
|
"PyYAML==6.0.1",
|
|
45
45
|
"invoke==2.2.0",
|
|
46
46
|
"pytest-timeout==2.3.1",
|
|
47
|
-
"ruff==0.
|
|
47
|
+
"ruff==0.8.0",
|
|
48
48
|
"mypy",
|
|
49
49
|
"types-PyYAML",
|
|
50
50
|
"atheris==2.3.0; python_version != '3.12'",
|
|
@@ -122,4 +122,4 @@ exclude_lines = [
|
|
|
122
122
|
]
|
|
123
123
|
|
|
124
124
|
[tool.check-sdist]
|
|
125
|
-
git-only = ["docs", "fuzz", "scripts", "mkdocs.yml", "uv.lock"]
|
|
125
|
+
git-only = ["docs", "fuzz", "scripts", "mkdocs.yml", "uv.lock", "SECURITY.md"]
|
|
@@ -1105,7 +1105,6 @@ class MultipartParser(BaseParser):
|
|
|
1105
1105
|
# Skip leading newlines
|
|
1106
1106
|
if c == CR or c == LF:
|
|
1107
1107
|
i += 1
|
|
1108
|
-
self.logger.debug("Skipping leading CR/LF at %d", i)
|
|
1109
1108
|
continue
|
|
1110
1109
|
|
|
1111
1110
|
# index is used as in index into our boundary. Set to 0.
|
|
@@ -1398,9 +1397,10 @@ class MultipartParser(BaseParser):
|
|
|
1398
1397
|
i -= 1
|
|
1399
1398
|
|
|
1400
1399
|
elif state == MultipartState.END:
|
|
1401
|
-
#
|
|
1402
|
-
|
|
1403
|
-
|
|
1400
|
+
# Skip data after the last boundary.
|
|
1401
|
+
self.logger.warning("Skipping data after last boundary")
|
|
1402
|
+
i = length
|
|
1403
|
+
break
|
|
1404
1404
|
|
|
1405
1405
|
else: # pragma: no cover (error case)
|
|
1406
1406
|
# We got into a strange state somehow! Just stop processing.
|
|
@@ -825,7 +825,7 @@ class TestFormParser(unittest.TestCase):
|
|
|
825
825
|
return
|
|
826
826
|
|
|
827
827
|
# No error!
|
|
828
|
-
self.assertEqual(processed, len(param["test"]))
|
|
828
|
+
self.assertEqual(processed, len(param["test"]), param["name"])
|
|
829
829
|
|
|
830
830
|
# Assert that the parser gave us the appropriate fields/files.
|
|
831
831
|
for e in param["result"]["expected"]:
|
|
@@ -1210,6 +1210,44 @@ class TestFormParser(unittest.TestCase):
|
|
|
1210
1210
|
self.assertEqual(fields[2].field_name, b"baz")
|
|
1211
1211
|
self.assertEqual(fields[2].value, b"asdf")
|
|
1212
1212
|
|
|
1213
|
+
def test_multipart_parser_newlines_before_first_boundary(self) -> None:
|
|
1214
|
+
"""This test makes sure that the parser does not handle when there is junk data after the last boundary."""
|
|
1215
|
+
num = 5_000_000
|
|
1216
|
+
data = (
|
|
1217
|
+
"\r\n" * num + "--boundary\r\n"
|
|
1218
|
+
'Content-Disposition: form-data; name="file"; filename="filename.txt"\r\n'
|
|
1219
|
+
"Content-Type: text/plain\r\n\r\n"
|
|
1220
|
+
"hello\r\n"
|
|
1221
|
+
"--boundary--"
|
|
1222
|
+
)
|
|
1223
|
+
|
|
1224
|
+
files: list[File] = []
|
|
1225
|
+
|
|
1226
|
+
def on_file(f: FileProtocol) -> None:
|
|
1227
|
+
files.append(cast(File, f))
|
|
1228
|
+
|
|
1229
|
+
f = FormParser("multipart/form-data", on_field=Mock(), on_file=on_file, boundary="boundary")
|
|
1230
|
+
f.write(data.encode("latin-1"))
|
|
1231
|
+
|
|
1232
|
+
def test_multipart_parser_data_after_last_boundary(self) -> None:
|
|
1233
|
+
"""This test makes sure that the parser does not handle when there is junk data after the last boundary."""
|
|
1234
|
+
num = 50_000_000
|
|
1235
|
+
data = (
|
|
1236
|
+
"--boundary\r\n"
|
|
1237
|
+
'Content-Disposition: form-data; name="file"; filename="filename.txt"\r\n'
|
|
1238
|
+
"Content-Type: text/plain\r\n\r\n"
|
|
1239
|
+
"hello\r\n"
|
|
1240
|
+
"--boundary--" + "-" * num + "\r\n"
|
|
1241
|
+
)
|
|
1242
|
+
|
|
1243
|
+
files: list[File] = []
|
|
1244
|
+
|
|
1245
|
+
def on_file(f: FileProtocol) -> None:
|
|
1246
|
+
files.append(cast(File, f))
|
|
1247
|
+
|
|
1248
|
+
f = FormParser("multipart/form-data", on_field=Mock(), on_file=on_file, boundary="boundary")
|
|
1249
|
+
f.write(data.encode("latin-1"))
|
|
1250
|
+
|
|
1213
1251
|
def test_max_size_multipart(self) -> None:
|
|
1214
1252
|
# Load test data.
|
|
1215
1253
|
test_file = "single_field_single_file.http"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CRLF_in_header.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CRLF_in_header.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CR_in_header_value.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/CR_in_header_value.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/almost_match_boundary.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_end_of_headers.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_end_of_headers.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_header_char.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_header_char.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_initial_boundary.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/bad_initial_boundary.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/base64_encoding.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/base64_encoding.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/header_with_number.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/header_with_number.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/multiple_fields.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/multiple_fields.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/multiple_files.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/multiple_files.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_blocks.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_blocks.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_longer.http
RENAMED
|
File without changes
|
{python_multipart-0.0.17 → python_multipart-0.0.18}/tests/test_data/http/single_field_longer.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|