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.
Files changed (71) hide show
  1. {python_multipart-0.0.22 → python_multipart-0.0.24}/CHANGELOG.md +10 -0
  2. {python_multipart-0.0.22 → python_multipart-0.0.24}/PKG-INFO +3 -2
  3. {python_multipart-0.0.22 → python_multipart-0.0.24}/multipart/__init__.py +1 -1
  4. {python_multipart-0.0.22 → python_multipart-0.0.24}/pyproject.toml +14 -11
  5. {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/__init__.py +1 -5
  6. {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/multipart.py +5 -8
  7. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_multipart.py +11 -0
  8. {python_multipart-0.0.22 → python_multipart-0.0.24}/.gitignore +0 -0
  9. {python_multipart-0.0.22 → python_multipart-0.0.24}/LICENSE.txt +0 -0
  10. {python_multipart-0.0.22 → python_multipart-0.0.24}/README.md +0 -0
  11. {python_multipart-0.0.22 → python_multipart-0.0.24}/multipart/decoders.py +0 -0
  12. {python_multipart-0.0.22 → python_multipart-0.0.24}/multipart/exceptions.py +0 -0
  13. {python_multipart-0.0.22 → python_multipart-0.0.24}/multipart/multipart.py +0 -0
  14. {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/decoders.py +0 -0
  15. {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/exceptions.py +0 -0
  16. {python_multipart-0.0.22 → python_multipart-0.0.24}/python_multipart/py.typed +0 -0
  17. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/__init__.py +0 -0
  18. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/compat.py +0 -0
  19. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CRLF_in_header.http +0 -0
  20. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CRLF_in_header.yaml +0 -0
  21. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header.http +0 -0
  22. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header.yaml +0 -0
  23. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header_value.http +0 -0
  24. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/CR_in_header_value.yaml +0 -0
  25. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary.http +0 -0
  26. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary.yaml +0 -0
  27. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_CR.http +0 -0
  28. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_CR.yaml +0 -0
  29. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_LF.http +0 -0
  30. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_LF.yaml +0 -0
  31. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_final_hyphen.http +0 -0
  32. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/almost_match_boundary_without_final_hyphen.yaml +0 -0
  33. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_end_of_headers.http +0 -0
  34. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_end_of_headers.yaml +0 -0
  35. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_header_char.http +0 -0
  36. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_header_char.yaml +0 -0
  37. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_initial_boundary.http +0 -0
  38. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/bad_initial_boundary.yaml +0 -0
  39. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/base64_encoding.http +0 -0
  40. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/base64_encoding.yaml +0 -0
  41. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_header.http +0 -0
  42. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_header.yaml +0 -0
  43. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_message.http +0 -0
  44. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_message.yaml +0 -0
  45. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_message_with_bad_end.http +0 -0
  46. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/empty_message_with_bad_end.yaml +0 -0
  47. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/header_with_number.http +0 -0
  48. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/header_with_number.yaml +0 -0
  49. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/mixed_plain_and_base64_encoding.http +0 -0
  50. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/mixed_plain_and_base64_encoding.yaml +0 -0
  51. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_fields.http +0 -0
  52. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_fields.yaml +0 -0
  53. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_files.http +0 -0
  54. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/multiple_files.yaml +0 -0
  55. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/quoted_printable_encoding.http +0 -0
  56. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/quoted_printable_encoding.yaml +0 -0
  57. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field.http +0 -0
  58. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field.yaml +0 -0
  59. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_blocks.http +0 -0
  60. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_blocks.yaml +0 -0
  61. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_longer.http +0 -0
  62. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_longer.yaml +0 -0
  63. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_single_file.http +0 -0
  64. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_single_file.yaml +0 -0
  65. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_with_leading_newlines.http +0 -0
  66. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_field_with_leading_newlines.yaml +0 -0
  67. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_file.http +0 -0
  68. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/single_file.yaml +0 -0
  69. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/utf8_filename.http +0 -0
  70. {python_multipart-0.0.22 → python_multipart-0.0.24}/tests/test_data/http/utf8_filename.yaml +0 -0
  71. {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.22
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>, Marcelo Trylesinski <marcelotryle@gmail.com>
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__, __author__, __copyright__, __license__, __version__
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==23.2.0",
37
- "coverage==7.4.4",
38
- "more-itertools==10.2.0",
39
- "pbr==6.0.0",
40
- "pluggy==1.4.0",
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==8.1.1",
43
- "pytest-cov==5.0.0",
44
- "PyYAML==6.0.1",
45
- "invoke==2.2.0",
46
- "pytest-timeout==2.3.1",
47
- "ruff==0.11.7",
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]
@@ -1,8 +1,4 @@
1
- # This is the canonical package information.
2
- __author__ = "Andrew Dunham"
3
- __license__ = "Apache"
4
- __copyright__ = "Copyright (c) 2012-2013, Andrew Dunham"
5
- __version__ = "0.0.22"
1
+ __version__ = "0.0.24"
6
2
 
7
3
  from .multipart import (
8
4
  BaseParser,
@@ -936,7 +936,7 @@ class QuerystringParser(BaseParser):
936
936
 
937
937
  self.state = state
938
938
  self._found_sep = found_sep
939
- return len(data)
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, file_name=file_name, config=config)
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()