python-multipart 0.0.8__tar.gz → 0.0.10__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 (64) hide show
  1. python_multipart-0.0.10/.gitignore +200 -0
  2. python_multipart-0.0.10/CHANGELOG.md +40 -0
  3. python_multipart-0.0.10/PKG-INFO +49 -0
  4. python_multipart-0.0.10/README.md +22 -0
  5. {python_multipart-0.0.8 → python_multipart-0.0.10}/multipart/__init__.py +12 -2
  6. {python_multipart-0.0.8 → python_multipart-0.0.10}/multipart/decoders.py +25 -24
  7. {python_multipart-0.0.8 → python_multipart-0.0.10}/multipart/exceptions.py +4 -16
  8. {python_multipart-0.0.8 → python_multipart-0.0.10}/multipart/multipart.py +673 -729
  9. python_multipart-0.0.10/pyproject.toml +104 -0
  10. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/compat.py +9 -34
  11. python_multipart-0.0.10/tests/test_data/http/bad_header_char.http +5 -0
  12. python_multipart-0.0.10/tests/test_data/http/header_with_number.http +11 -0
  13. python_multipart-0.0.10/tests/test_data/http/header_with_number.yaml +7 -0
  14. python_multipart-0.0.10/tests/test_data/http/mixed_plain_and_base64_encoding.http +23 -0
  15. python_multipart-0.0.10/tests/test_data/http/mixed_plain_and_base64_encoding.yaml +18 -0
  16. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_multipart.py +361 -362
  17. python_multipart-0.0.8/.gitignore +0 -61
  18. python_multipart-0.0.8/PKG-INFO +0 -69
  19. python_multipart-0.0.8/README.rst +0 -28
  20. python_multipart-0.0.8/pyproject.toml +0 -60
  21. python_multipart-0.0.8/tests/test_data/http/bad_header_char.http +0 -5
  22. {python_multipart-0.0.8 → python_multipart-0.0.10}/LICENSE.txt +0 -0
  23. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/__init__.py +0 -0
  24. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/CR_in_header.http +0 -0
  25. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/CR_in_header.yaml +0 -0
  26. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/CR_in_header_value.http +0 -0
  27. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/CR_in_header_value.yaml +0 -0
  28. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/almost_match_boundary.http +0 -0
  29. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/almost_match_boundary.yaml +0 -0
  30. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/almost_match_boundary_without_CR.http +0 -0
  31. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/almost_match_boundary_without_CR.yaml +0 -0
  32. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/almost_match_boundary_without_LF.http +0 -0
  33. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/almost_match_boundary_without_LF.yaml +0 -0
  34. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/almost_match_boundary_without_final_hyphen.http +0 -0
  35. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/almost_match_boundary_without_final_hyphen.yaml +0 -0
  36. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/bad_end_of_headers.http +0 -0
  37. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/bad_end_of_headers.yaml +0 -0
  38. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/bad_header_char.yaml +0 -0
  39. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/bad_initial_boundary.http +0 -0
  40. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/bad_initial_boundary.yaml +0 -0
  41. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/base64_encoding.http +0 -0
  42. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/base64_encoding.yaml +0 -0
  43. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/empty_header.http +0 -0
  44. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/empty_header.yaml +0 -0
  45. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/multiple_fields.http +0 -0
  46. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/multiple_fields.yaml +0 -0
  47. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/multiple_files.http +0 -0
  48. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/multiple_files.yaml +0 -0
  49. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/quoted_printable_encoding.http +0 -0
  50. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/quoted_printable_encoding.yaml +0 -0
  51. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field.http +0 -0
  52. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field.yaml +0 -0
  53. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field_blocks.http +0 -0
  54. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field_blocks.yaml +0 -0
  55. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field_longer.http +0 -0
  56. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field_longer.yaml +0 -0
  57. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field_single_file.http +0 -0
  58. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field_single_file.yaml +0 -0
  59. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field_with_leading_newlines.http +0 -0
  60. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_field_with_leading_newlines.yaml +0 -0
  61. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_file.http +0 -0
  62. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/single_file.yaml +0 -0
  63. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/utf8_filename.http +0 -0
  64. {python_multipart-0.0.8 → python_multipart-0.0.10}/tests/test_data/http/utf8_filename.yaml +0 -0
@@ -0,0 +1,200 @@
1
+ # Project-specific ignores.
2
+ tmp/
3
+
4
+ # Installer logs
5
+ pip-log.txt
6
+
7
+ # Unit test / coverage reports
8
+ .coverage*
9
+ .tox
10
+ nosetests.xml
11
+
12
+ # CTags files
13
+ tags
14
+
15
+ #Translations
16
+ *.mo
17
+
18
+ #Mr Developer
19
+ .mr.developer.cfg
20
+
21
+ # SublimeText project files
22
+ /*.sublime-project
23
+ *.sublime-workspace
24
+
25
+ # Windows image file caches
26
+ Thumbs.db
27
+
28
+ # Folder config file
29
+ Desktop.ini
30
+
31
+ # Recycle Bin used on file shares
32
+ $RECYCLE.BIN/
33
+
34
+ # Linux ignores
35
+ *~
36
+
37
+ # OS X ignores
38
+ .DS_Store
39
+
40
+
41
+ # Byte-compiled / optimized / DLL files
42
+ __pycache__/
43
+ *.py[cod]
44
+ *$py.class
45
+
46
+ # C extensions
47
+ *.so
48
+
49
+ # Distribution / packaging
50
+ .Python
51
+ build/
52
+ develop-eggs/
53
+ dist/
54
+ downloads/
55
+ eggs/
56
+ .eggs/
57
+ lib/
58
+ lib64/
59
+ parts/
60
+ sdist/
61
+ var/
62
+ wheels/
63
+ share/python-wheels/
64
+ *.egg-info/
65
+ .installed.cfg
66
+ *.egg
67
+ MANIFEST
68
+
69
+ # PyInstaller
70
+ # Usually these files are written by a python script from a template
71
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
72
+ *.manifest
73
+ *.spec
74
+
75
+ # Installer logs
76
+ pip-log.txt
77
+ pip-delete-this-directory.txt
78
+
79
+ # Unit test / coverage reports
80
+ htmlcov/
81
+ .tox/
82
+ .nox/
83
+ .coverage
84
+ .coverage.*
85
+ .cache
86
+ nosetests.xml
87
+ coverage.xml
88
+ *.cover
89
+ *.py,cover
90
+ .hypothesis/
91
+ .pytest_cache/
92
+ cover/
93
+
94
+ # Translations
95
+ *.mo
96
+ *.pot
97
+
98
+ # Django stuff:
99
+ *.log
100
+ local_settings.py
101
+ db.sqlite3
102
+ db.sqlite3-journal
103
+
104
+ # Flask stuff:
105
+ instance/
106
+ .webassets-cache
107
+
108
+ # Scrapy stuff:
109
+ .scrapy
110
+
111
+ # Sphinx documentation
112
+ docs/_build/
113
+
114
+ # PyBuilder
115
+ .pybuilder/
116
+ target/
117
+
118
+ # Jupyter Notebook
119
+ .ipynb_checkpoints
120
+
121
+ # IPython
122
+ profile_default/
123
+ ipython_config.py
124
+
125
+ # pyenv
126
+ # For a library or package, you might want to ignore these files since the code is
127
+ # intended to run in multiple environments; otherwise, check them in:
128
+ # .python-version
129
+
130
+ # pipenv
131
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
132
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
133
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
134
+ # install all needed dependencies.
135
+ #Pipfile.lock
136
+
137
+ # poetry
138
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
139
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
140
+ # commonly ignored for libraries.
141
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
142
+ #poetry.lock
143
+
144
+ # pdm
145
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
146
+ #pdm.lock
147
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
148
+ # in version control.
149
+ # https://pdm.fming.dev/#use-with-ide
150
+ .pdm.toml
151
+
152
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
153
+ __pypackages__/
154
+
155
+ # Celery stuff
156
+ celerybeat-schedule
157
+ celerybeat.pid
158
+
159
+ # SageMath parsed files
160
+ *.sage.py
161
+
162
+ # Environments
163
+ .env
164
+ .venv
165
+ env/
166
+ venv/
167
+ ENV/
168
+ env.bak/
169
+ venv.bak/
170
+
171
+ # Spyder project settings
172
+ .spyderproject
173
+ .spyproject
174
+
175
+ # Rope project settings
176
+ .ropeproject
177
+
178
+ # mkdocs documentation
179
+ /site
180
+
181
+ # mypy
182
+ .mypy_cache/
183
+ .dmypy.json
184
+ dmypy.json
185
+
186
+ # Pyre type checker
187
+ .pyre/
188
+
189
+ # pytype static type analyzer
190
+ .pytype/
191
+
192
+ # Cython debug symbols
193
+ cython_debug/
194
+
195
+ # PyCharm
196
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
197
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
198
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
199
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
200
+ #.idea/
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ ## 0.0.10 (2024-09-21)
4
+
5
+ * Support `on_header_begin` [#103](https://github.com/Kludex/python-multipart/pull/103).
6
+ * Improve type hints on `FormParser` [#104](https://github.com/Kludex/python-multipart/pull/104).
7
+ * Fix `OnFileCallback` type [#106](https://github.com/Kludex/python-multipart/pull/106).
8
+ * Improve type hints [#110](https://github.com/Kludex/python-multipart/pull/110).
9
+ * Improve type hints on `File` [#111](https://github.com/Kludex/python-multipart/pull/111).
10
+ * Add type hint to helper functions [#112](https://github.com/Kludex/python-multipart/pull/112).
11
+ * Minor fix for Field.__repr__ [#114](https://github.com/Kludex/python-multipart/pull/114).
12
+ * Fix use of chunk_size parameter [#136](https://github.com/Kludex/python-multipart/pull/136).
13
+ * Allow digits and valid token chars in headers [#134](https://github.com/Kludex/python-multipart/pull/134).
14
+ * Fix headers being carried between parts [#135](https://github.com/Kludex/python-multipart/pull/135).
15
+
16
+ ## 0.0.9 (2024-02-10)
17
+
18
+ * Add support for Python 3.12 [#85](https://github.com/Kludex/python-multipart/pull/85).
19
+ * Drop support for Python 3.7 [#95](https://github.com/Kludex/python-multipart/pull/95).
20
+ * Add `MultipartState(IntEnum)` [#96](https://github.com/Kludex/python-multipart/pull/96).
21
+ * Add `QuerystringState` [#97](https://github.com/Kludex/python-multipart/pull/97).
22
+ * Add `TypedDict` callbacks [#98](https://github.com/Kludex/python-multipart/pull/98).
23
+ * Add config `TypedDict`s [#99](https://github.com/Kludex/python-multipart/pull/99).
24
+
25
+ ## 0.0.8 (2024-02-09)
26
+
27
+ * Check if Message.get_params return 3-tuple instead of str on parse_options_header [#79](https://github.com/Kludex/python-multipart/pull/79).
28
+ * Cleanup unused regex patterns [#82](https://github.com/Kludex/python-multipart/pull/82).
29
+
30
+ ## 0.0.7 (2024-02-03)
31
+
32
+ * Refactor header option parser to use the standard library instead of a custom RegEx [#75](https://github.com/andrew-d/python-multipart/pull/75).
33
+
34
+ ## 0.0.6 (2023-02-27)
35
+
36
+ * Migrate package installation to `pyproject.toml` (PEP 621) [#54](https://github.com/andrew-d/python-multipart/pull/54).
37
+ * Use yaml.safe_load instead of yaml.load [#46](https://github.com/andrew-d/python-multipart/pull/46).
38
+ * Add support for Python 3.11, drop EOL 3.6 [#51](https://github.com/andrew-d/python-multipart/pull/51).
39
+ * Add support for Python 3.8-3.10, drop EOL 2.7-3.5 [#42](https://github.com/andrew-d/python-multipart/pull/42).
40
+ * `QuerystringParser`: don't raise an AttributeError in `__repr__` [#30](https://github.com/andrew-d/python-multipart/pull/30).
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.3
2
+ Name: python-multipart
3
+ Version: 0.0.10
4
+ Summary: A streaming multipart parser for Python
5
+ Project-URL: Homepage, https://github.com/Kludex/python-multipart
6
+ Project-URL: Documentation, https://kludex.github.io/python-multipart/
7
+ Project-URL: Changelog, https://github.com/Kludex/python-multipart/blob/master/CHANGELOG.md
8
+ Project-URL: Source, https://github.com/Kludex/python-multipart
9
+ Author-email: Andrew Dunham <andrew@du.nham.ca>, Marcelo Trylesinski <marcelotryle@gmail.com>
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE.txt
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Environment :: Web Environment
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+
28
+ # [Python-Multipart](https://kludex.github.io/python-multipart/)
29
+
30
+ [![Package version](https://badge.fury.io/py/python-multipart.svg)](https://pypi.python.org/pypi/python-multipart)
31
+ [![Supported Python Version](https://img.shields.io/pypi/pyversions/python-multipart.svg?color=%2334D058)](https://pypi.org/project/python-multipart)
32
+
33
+ ---
34
+
35
+ `python-multipart` is an Apache2-licensed streaming multipart parser for Python.
36
+ Test coverage is currently 100%.
37
+
38
+ ## Why?
39
+
40
+ Because streaming uploads are awesome for large files.
41
+
42
+ ## How to Test
43
+
44
+ If you want to test:
45
+
46
+ ```bash
47
+ $ pip install '.[dev]'
48
+ $ inv test
49
+ ```
@@ -0,0 +1,22 @@
1
+ # [Python-Multipart](https://kludex.github.io/python-multipart/)
2
+
3
+ [![Package version](https://badge.fury.io/py/python-multipart.svg)](https://pypi.python.org/pypi/python-multipart)
4
+ [![Supported Python Version](https://img.shields.io/pypi/pyversions/python-multipart.svg?color=%2334D058)](https://pypi.org/project/python-multipart)
5
+
6
+ ---
7
+
8
+ `python-multipart` is an Apache2-licensed streaming multipart parser for Python.
9
+ Test coverage is currently 100%.
10
+
11
+ ## Why?
12
+
13
+ Because streaming uploads are awesome for large files.
14
+
15
+ ## How to Test
16
+
17
+ If you want to test:
18
+
19
+ ```bash
20
+ $ pip install '.[dev]'
21
+ $ inv test
22
+ ```
@@ -2,10 +2,10 @@
2
2
  __author__ = "Andrew Dunham"
3
3
  __license__ = "Apache"
4
4
  __copyright__ = "Copyright (c) 2012-2013, Andrew Dunham"
5
- __version__ = "0.0.8"
6
-
5
+ __version__ = "0.0.10"
7
6
 
8
7
  from .multipart import (
8
+ BaseParser,
9
9
  FormParser,
10
10
  MultipartParser,
11
11
  OctetStreamParser,
@@ -13,3 +13,13 @@ from .multipart import (
13
13
  create_form_parser,
14
14
  parse_form,
15
15
  )
16
+
17
+ __all__ = (
18
+ "BaseParser",
19
+ "FormParser",
20
+ "MultipartParser",
21
+ "OctetStreamParser",
22
+ "QuerystringParser",
23
+ "create_form_parser",
24
+ "parse_form",
25
+ )
@@ -1,5 +1,6 @@
1
1
  import base64
2
2
  import binascii
3
+ from io import BufferedWriter
3
4
 
4
5
  from .exceptions import DecodeError
5
6
 
@@ -33,11 +34,11 @@ class Base64Decoder:
33
34
  :param underlying: the underlying object to pass writes to
34
35
  """
35
36
 
36
- def __init__(self, underlying):
37
+ def __init__(self, underlying: BufferedWriter):
37
38
  self.cache = bytearray()
38
39
  self.underlying = underlying
39
40
 
40
- def write(self, data):
41
+ def write(self, data: bytes) -> int:
41
42
  """Takes any input data provided, decodes it as base64, and passes it
42
43
  on to the underlying object. If the data provided is invalid base64
43
44
  data, then this method will raise
@@ -59,8 +60,7 @@ class Base64Decoder:
59
60
  try:
60
61
  decoded = base64.b64decode(val)
61
62
  except binascii.Error:
62
- raise DecodeError('There was an error raised while decoding '
63
- 'base64-encoded data.')
63
+ raise DecodeError("There was an error raised while decoding base64-encoded data.")
64
64
 
65
65
  self.underlying.write(decoded)
66
66
 
@@ -69,19 +69,19 @@ class Base64Decoder:
69
69
  if remaining_len > 0:
70
70
  self.cache = data[-remaining_len:]
71
71
  else:
72
- self.cache = b''
72
+ self.cache = b""
73
73
 
74
74
  # Return the length of the data to indicate no error.
75
75
  return len(data)
76
76
 
77
- def close(self):
77
+ def close(self) -> None:
78
78
  """Close this decoder. If the underlying object has a `close()`
79
79
  method, this function will call it.
80
80
  """
81
- if hasattr(self.underlying, 'close'):
81
+ if hasattr(self.underlying, "close"):
82
82
  self.underlying.close()
83
83
 
84
- def finalize(self):
84
+ def finalize(self) -> None:
85
85
  """Finalize this object. This should be called when no more data
86
86
  should be written to the stream. This function can raise a
87
87
  :class:`multipart.exceptions.DecodeError` if there is some remaining
@@ -91,14 +91,14 @@ class Base64Decoder:
91
91
  call it.
92
92
  """
93
93
  if len(self.cache) > 0:
94
- raise DecodeError('There are %d bytes remaining in the '
95
- 'Base64Decoder cache when finalize() is called'
96
- % len(self.cache))
94
+ raise DecodeError(
95
+ "There are %d bytes remaining in the Base64Decoder cache when finalize() is called" % len(self.cache)
96
+ )
97
97
 
98
- if hasattr(self.underlying, 'finalize'):
98
+ if hasattr(self.underlying, "finalize"):
99
99
  self.underlying.finalize()
100
100
 
101
- def __repr__(self):
101
+ def __repr__(self) -> str:
102
102
  return f"{self.__class__.__name__}(underlying={self.underlying!r})"
103
103
 
104
104
 
@@ -111,11 +111,12 @@ class QuotedPrintableDecoder:
111
111
 
112
112
  :param underlying: the underlying object to pass writes to
113
113
  """
114
- def __init__(self, underlying):
115
- self.cache = b''
114
+
115
+ def __init__(self, underlying: BufferedWriter) -> None:
116
+ self.cache = b""
116
117
  self.underlying = underlying
117
118
 
118
- def write(self, data):
119
+ def write(self, data: bytes) -> int:
119
120
  """Takes any input data provided, decodes it as quoted-printable, and
120
121
  passes it on to the underlying object.
121
122
 
@@ -128,11 +129,11 @@ class QuotedPrintableDecoder:
128
129
  # If the last 2 characters have an '=' sign in it, then we won't be
129
130
  # able to decode the encoded value and we'll need to save it for the
130
131
  # next decoding step.
131
- if data[-2:].find(b'=') != -1:
132
+ if data[-2:].find(b"=") != -1:
132
133
  enc, rest = data[:-2], data[-2:]
133
134
  else:
134
135
  enc = data
135
- rest = b''
136
+ rest = b""
136
137
 
137
138
  # Encode and write, if we have data.
138
139
  if len(enc) > 0:
@@ -142,14 +143,14 @@ class QuotedPrintableDecoder:
142
143
  self.cache = rest
143
144
  return len(data)
144
145
 
145
- def close(self):
146
+ def close(self) -> None:
146
147
  """Close this decoder. If the underlying object has a `close()`
147
148
  method, this function will call it.
148
149
  """
149
- if hasattr(self.underlying, 'close'):
150
+ if hasattr(self.underlying, "close"):
150
151
  self.underlying.close()
151
152
 
152
- def finalize(self):
153
+ def finalize(self) -> None:
153
154
  """Finalize this object. This should be called when no more data
154
155
  should be written to the stream. This function will not raise any
155
156
  exceptions, but it may write more data to the underlying object if
@@ -161,11 +162,11 @@ class QuotedPrintableDecoder:
161
162
  # If we have a cache, write and then remove it.
162
163
  if len(self.cache) > 0:
163
164
  self.underlying.write(binascii.a2b_qp(self.cache))
164
- self.cache = b''
165
+ self.cache = b""
165
166
 
166
167
  # Finalize our underlying stream.
167
- if hasattr(self.underlying, 'finalize'):
168
+ if hasattr(self.underlying, "finalize"):
168
169
  self.underlying.finalize()
169
170
 
170
- def __repr__(self):
171
+ def __repr__(self) -> str:
171
172
  return f"{self.__class__.__name__}(underlying={self.underlying!r})"
@@ -1,6 +1,5 @@
1
1
  class FormParserError(ValueError):
2
2
  """Base error class for our form parser."""
3
- pass
4
3
 
5
4
 
6
5
  class ParseError(FormParserError):
@@ -17,30 +16,19 @@ class MultipartParseError(ParseError):
17
16
  """This is a specific error that is raised when the MultipartParser detects
18
17
  an error while parsing.
19
18
  """
20
- pass
21
19
 
22
20
 
23
21
  class QuerystringParseError(ParseError):
24
22
  """This is a specific error that is raised when the QuerystringParser
25
23
  detects an error while parsing.
26
24
  """
27
- pass
28
25
 
29
26
 
30
27
  class DecodeError(ParseError):
31
28
  """This exception is raised when there is a decoding error - for example
32
29
  with the Base64Decoder or QuotedPrintableDecoder.
33
30
  """
34
- pass
35
-
36
-
37
- # On Python 3.3, IOError is the same as OSError, so we don't want to inherit
38
- # from both of them. We handle this case below.
39
- if IOError is not OSError: # pragma: no cover
40
- class FileError(FormParserError, IOError, OSError):
41
- """Exception class for problems with the File class."""
42
- pass
43
- else: # pragma: no cover
44
- class FileError(FormParserError, OSError):
45
- """Exception class for problems with the File class."""
46
- pass
31
+
32
+
33
+ class FileError(FormParserError, OSError):
34
+ """Exception class for problems with the File class."""