python-multipart 0.0.22__tar.gz → 0.0.24__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.22 → python_multipart-0.0.24}/CHANGELOG.md +10 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/PKG-INFO +3 -2
- {python_multipart-0.0.22 → python_multipart-0.0.24}/multipart/__init__.py +1 -1
- {python_multipart-0.0.22 → python_multipart-0.0.24}/pyproject.toml +14 -11
- {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/__init__.py +1 -5
- {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/multipart.py +5 -8
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_multipart.py +11 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/.gitignore +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/LICENSE.txt +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/README.md +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/multipart/decoders.py +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/multipart/exceptions.py +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/multipart/multipart.py +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/decoders.py +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/exceptions.py +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/py.typed +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/__init__.py +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/compat.py +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CRLF_in_header.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CRLF_in_header.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header_value.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header_value.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_CR.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_CR.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_LF.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_LF.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_final_hyphen.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_final_hyphen.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_end_of_headers.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_end_of_headers.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_header_char.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_header_char.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_initial_boundary.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_initial_boundary.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/base64_encoding.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/base64_encoding.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_header.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_header.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_message.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_message.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_message_with_bad_end.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_message_with_bad_end.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/header_with_number.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/header_with_number.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/mixed_plain_and_base64_encoding.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/mixed_plain_and_base64_encoding.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_fields.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_fields.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_files.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_files.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/quoted_printable_encoding.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/quoted_printable_encoding.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_blocks.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_blocks.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_longer.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_longer.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_single_file.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_single_file.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_with_leading_newlines.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_with_leading_newlines.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_file.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_file.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/utf8_filename.http +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/utf8_filename.yaml +0 -0
- {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_file.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.0.24 (2026-04-05)
|
|
4
|
+
|
|
5
|
+
* Validate `chunk_size` in `parse_form()` [#244](https://github.com/Kludex/python-multipart/pull/244).
|
|
6
|
+
|
|
7
|
+
## 0.0.23 (2026-04-05)
|
|
8
|
+
|
|
9
|
+
* Remove unused `trust_x_headers` parameter and `X-File-Name` fallback [#196](https://github.com/Kludex/python-multipart/pull/196).
|
|
10
|
+
* Return processed length from `QuerystringParser._internal_write` [#229](https://github.com/Kludex/python-multipart/pull/229).
|
|
11
|
+
* Cleanup metadata dunders from `__init__.py` [#227](https://github.com/Kludex/python-multipart/pull/227).
|
|
12
|
+
|
|
3
13
|
## 0.0.22 (2026-01-25)
|
|
4
14
|
|
|
5
15
|
* Drop directory path from filename in `File` [9433f4b](https://github.com/Kludex/python-multipart/commit/9433f4bbc9652bdde82bbe380984e32f8cfc89c4).
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-multipart
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.24
|
|
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
|
-
Author-email: Andrew Dunham <andrew@du.nham.ca
|
|
9
|
+
Author-email: Andrew Dunham <andrew@du.nham.ca>
|
|
10
|
+
Maintainer-email: Marcelo Trylesinski <marcelotryle@gmail.com>
|
|
10
11
|
License-Expression: Apache-2.0
|
|
11
12
|
License-File: LICENSE.txt
|
|
12
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -21,4 +21,4 @@ for p in sys.path:
|
|
|
21
21
|
else:
|
|
22
22
|
warnings.warn("Please use `import python_multipart` instead.", PendingDeprecationWarning, stacklevel=2)
|
|
23
23
|
from python_multipart import *
|
|
24
|
-
from python_multipart import __all__,
|
|
24
|
+
from python_multipart import __all__, __version__
|
|
@@ -11,6 +11,8 @@ license = "Apache-2.0"
|
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
authors = [
|
|
13
13
|
{ name = "Andrew Dunham", email = "andrew@du.nham.ca" },
|
|
14
|
+
]
|
|
15
|
+
maintainers = [
|
|
14
16
|
{ name = "Marcelo Trylesinski", email = "marcelotryle@gmail.com" },
|
|
15
17
|
]
|
|
16
18
|
classifiers = [
|
|
@@ -33,18 +35,18 @@ dependencies = []
|
|
|
33
35
|
[dependency-groups]
|
|
34
36
|
dev = [
|
|
35
37
|
"atomicwrites==1.4.1",
|
|
36
|
-
"attrs==
|
|
37
|
-
"coverage==7.
|
|
38
|
-
"more-itertools==10.
|
|
39
|
-
"pbr==
|
|
40
|
-
"pluggy==1.
|
|
38
|
+
"attrs==26.1.0",
|
|
39
|
+
"coverage==7.13.5",
|
|
40
|
+
"more-itertools==10.8.0",
|
|
41
|
+
"pbr==7.0.3",
|
|
42
|
+
"pluggy==1.6.0",
|
|
41
43
|
"py==1.11.0",
|
|
42
|
-
"pytest==
|
|
43
|
-
"pytest-cov==
|
|
44
|
-
"PyYAML==6.0.
|
|
45
|
-
"invoke==2.2.
|
|
46
|
-
"pytest-timeout==2.
|
|
47
|
-
"ruff==0.
|
|
44
|
+
"pytest==9.0.2",
|
|
45
|
+
"pytest-cov==7.1.0",
|
|
46
|
+
"PyYAML==6.0.3",
|
|
47
|
+
"invoke==2.2.1",
|
|
48
|
+
"pytest-timeout==2.4.0",
|
|
49
|
+
"ruff==0.15.8",
|
|
48
50
|
"mypy",
|
|
49
51
|
"types-PyYAML",
|
|
50
52
|
"atheris==2.3.0; python_version <= '3.11'",
|
|
@@ -53,6 +55,7 @@ dev = [
|
|
|
53
55
|
"mkdocs-material",
|
|
54
56
|
"mkdocstrings-python",
|
|
55
57
|
"mkdocs-autorefs",
|
|
58
|
+
"pymdown-extensions>=10.21.2",
|
|
56
59
|
]
|
|
57
60
|
|
|
58
61
|
[tool.uv.pip]
|
|
@@ -936,7 +936,7 @@ class QuerystringParser(BaseParser):
|
|
|
936
936
|
|
|
937
937
|
self.state = state
|
|
938
938
|
self._found_sep = found_sep
|
|
939
|
-
return
|
|
939
|
+
return length
|
|
940
940
|
|
|
941
941
|
def finalize(self) -> None:
|
|
942
942
|
"""Finalize this parser, which signals to that we are finished parsing,
|
|
@@ -1784,7 +1784,6 @@ def create_form_parser(
|
|
|
1784
1784
|
headers: dict[str, bytes],
|
|
1785
1785
|
on_field: OnFieldCallback | None,
|
|
1786
1786
|
on_file: OnFileCallback | None,
|
|
1787
|
-
trust_x_headers: bool = False,
|
|
1788
1787
|
config: dict[Any, Any] = {},
|
|
1789
1788
|
) -> FormParser:
|
|
1790
1789
|
"""This function is a helper function to aid in creating a FormParser
|
|
@@ -1797,8 +1796,6 @@ def create_form_parser(
|
|
|
1797
1796
|
headers: A dictionary-like object of HTTP headers. The only required header is Content-Type.
|
|
1798
1797
|
on_field: Callback to call with each parsed field.
|
|
1799
1798
|
on_file: Callback to call with each parsed file.
|
|
1800
|
-
trust_x_headers: Whether or not to trust information received from certain X-Headers - for example, the file
|
|
1801
|
-
name from X-File-Name.
|
|
1802
1799
|
config: Configuration variables to pass to the FormParser.
|
|
1803
1800
|
"""
|
|
1804
1801
|
content_type: str | bytes | None = headers.get("Content-Type")
|
|
@@ -1814,11 +1811,8 @@ def create_form_parser(
|
|
|
1814
1811
|
# We need content_type to be a string, not a bytes object.
|
|
1815
1812
|
content_type = content_type.decode("latin-1")
|
|
1816
1813
|
|
|
1817
|
-
# File names are optional.
|
|
1818
|
-
file_name = headers.get("X-File-Name")
|
|
1819
|
-
|
|
1820
1814
|
# Instantiate a form parser.
|
|
1821
|
-
form_parser = FormParser(content_type, on_field, on_file, boundary=boundary,
|
|
1815
|
+
form_parser = FormParser(content_type, on_field, on_file, boundary=boundary, config=config)
|
|
1822
1816
|
|
|
1823
1817
|
# Return our parser.
|
|
1824
1818
|
return form_parser
|
|
@@ -1844,6 +1838,9 @@ def parse_form(
|
|
|
1844
1838
|
chunk_size: The maximum size to read from the input stream and write to the parser at one time.
|
|
1845
1839
|
Defaults to 1 MiB.
|
|
1846
1840
|
"""
|
|
1841
|
+
if chunk_size < 1:
|
|
1842
|
+
raise ValueError(f"chunk_size must be a positive number, not {chunk_size!r}")
|
|
1843
|
+
|
|
1847
1844
|
# Create our form parser.
|
|
1848
1845
|
parser = create_form_parser(headers, on_field, on_file)
|
|
1849
1846
|
|
|
@@ -1371,6 +1371,7 @@ class TestFormParser(unittest.TestCase):
|
|
|
1371
1371
|
self.assertEqual(calls, 3)
|
|
1372
1372
|
|
|
1373
1373
|
|
|
1374
|
+
@parametrize_class
|
|
1374
1375
|
class TestHelperFunctions(unittest.TestCase):
|
|
1375
1376
|
def test_create_form_parser(self) -> None:
|
|
1376
1377
|
r = create_form_parser({"Content-Type": b"application/octet-stream"}, None, None)
|
|
@@ -1412,6 +1413,16 @@ class TestHelperFunctions(unittest.TestCase):
|
|
|
1412
1413
|
self.assertEqual(len(files), 1)
|
|
1413
1414
|
self.assertEqual(files[0].size, 10) # type: ignore[attr-defined]
|
|
1414
1415
|
|
|
1416
|
+
def test_parse_form_invalid_chunk_size(self) -> None:
|
|
1417
|
+
with self.assertRaisesRegex(ValueError, "chunk_size must be a positive number, not 0"):
|
|
1418
|
+
parse_form(
|
|
1419
|
+
{"Content-Type": b"application/octet-stream"},
|
|
1420
|
+
BytesIO(b"123456789012345"),
|
|
1421
|
+
lambda _: None,
|
|
1422
|
+
lambda _: None,
|
|
1423
|
+
chunk_size=0,
|
|
1424
|
+
)
|
|
1425
|
+
|
|
1415
1426
|
|
|
1416
1427
|
def suite() -> unittest.TestSuite:
|
|
1417
1428
|
suite = unittest.TestSuite()
|
|
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.22 → python_multipart-0.0.24}/tests/test_data/http/CRLF_in_header.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CRLF_in_header.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header_value.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header_value.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/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.22 → python_multipart-0.0.24}/tests/test_data/http/bad_end_of_headers.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_end_of_headers.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_header_char.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_header_char.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_initial_boundary.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_initial_boundary.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/base64_encoding.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/base64_encoding.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.22 → python_multipart-0.0.24}/tests/test_data/http/header_with_number.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/header_with_number.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_fields.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_fields.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_files.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/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.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_blocks.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_blocks.yaml
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_longer.http
RENAMED
|
File without changes
|
{python_multipart-0.0.22 → python_multipart-0.0.24}/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
|
|
File without changes
|