0
# High-Level Form Parsing
1
2
Complete form parsing solution that automatically detects content types and creates appropriate parsers. Provides the simplest interface for handling form data, file uploads, and various content encodings with automatic Field and File object creation.
3
4
## Capabilities
5
6
### Convenience Functions
7
8
#### parse_form
9
10
Convenience function that handles the complete form parsing workflow with minimal setup. Automatically detects content type, creates appropriate parser, and processes the entire input stream.
11
12
```python { .api }
13
def parse_form(
14
headers: dict[str, bytes],
15
input_stream: SupportsRead,
16
on_field: OnFieldCallback | None,
17
on_file: OnFileCallback | None,
18
chunk_size: int = 1048576
19
) -> None:
20
"""
21
Parse a request body with minimal setup.
22
23
Parameters:
24
- headers: HTTP headers dict containing Content-Type
25
- input_stream: Input stream to read form data from
26
- on_field: Callback for each parsed field
27
- on_file: Callback for each parsed file
28
- chunk_size: Size of chunks to read from input stream
29
"""
30
```
31
32
**Usage Example:**
33
34
```python
35
def handle_form_submission(environ):
36
fields = []
37
files = []
38
39
def on_field(field):
40
fields.append({
41
'name': field.field_name.decode('utf-8'),
42
'value': field.value.decode('utf-8') if field.value else None
43
})
44
45
def on_file(file):
46
files.append({
47
'field_name': file.field_name.decode('utf-8'),
48
'file_name': file.file_name.decode('utf-8') if file.file_name else None,
49
'size': file.size,
50
'in_memory': file.in_memory
51
})
52
file.close() # Important: close file when done
53
54
headers = {'Content-Type': environ['CONTENT_TYPE'].encode()}
55
parse_form(headers, environ['wsgi.input'], on_field, on_file)
56
57
return {'fields': fields, 'files': files}
58
```
59
60
#### create_form_parser
61
62
Factory function that creates FormParser instances from HTTP headers with automatic content type detection and parser configuration.
63
64
```python { .api }
65
def create_form_parser(
66
headers: dict[str, bytes],
67
on_field: OnFieldCallback | None,
68
on_file: OnFileCallback | None,
69
trust_x_headers: bool = False,
70
config: dict = {}
71
) -> FormParser:
72
"""
73
Create FormParser instance from HTTP headers.
74
75
Parameters:
76
- headers: HTTP headers dict
77
- on_field: Callback for each parsed field
78
- on_file: Callback for each parsed file
79
- trust_x_headers: Whether to trust X-File-Name header
80
- config: Configuration options for parser
81
82
Returns:
83
FormParser instance ready for data processing
84
"""
85
```
86
87
### FormParser Class
88
89
High-level parser that instantiates appropriate underlying parser based on content type and manages Field/File object lifecycle.
90
91
```python { .api }
92
class FormParser:
93
"""
94
All-in-one form parser that handles multiple content types.
95
"""
96
97
DEFAULT_CONFIG: FormParserConfig = {
98
"MAX_BODY_SIZE": float("inf"),
99
"MAX_MEMORY_FILE_SIZE": 1 * 1024 * 1024,
100
"UPLOAD_DIR": None,
101
"UPLOAD_KEEP_FILENAME": False,
102
"UPLOAD_KEEP_EXTENSIONS": False,
103
"UPLOAD_ERROR_ON_BAD_CTE": False,
104
}
105
106
def __init__(
107
self,
108
content_type: str,
109
on_field: OnFieldCallback | None,
110
on_file: OnFileCallback | None,
111
on_end: Callable[[], None] | None = None,
112
boundary: bytes | str | None = None,
113
file_name: bytes | None = None,
114
FileClass: type[FileProtocol] = File,
115
FieldClass: type[FieldProtocol] = Field,
116
config: dict[Any, Any] = {}
117
):
118
"""
119
Initialize FormParser.
120
121
Parameters:
122
- content_type: MIME type of request body
123
- on_field: Callback for each parsed field
124
- on_file: Callback for each parsed file
125
- on_end: Callback when parsing completes
126
- boundary: Multipart boundary for multipart/form-data
127
- file_name: File name for octet-stream content
128
- FileClass: Class to use for file objects
129
- FieldClass: Class to use for field objects
130
- config: Configuration options
131
"""
132
133
def write(self, data: bytes) -> int:
134
"""
135
Write data to parser for processing.
136
137
Parameters:
138
- data: Bytes to process
139
140
Returns:
141
Number of bytes processed
142
"""
143
144
def finalize(self) -> None:
145
"""
146
Finalize parsing and flush any remaining data.
147
Call when no more data will be written.
148
"""
149
150
def close(self) -> None:
151
"""
152
Close parser and clean up resources.
153
"""
154
```
155
156
**Configuration Options:**
157
158
- `UPLOAD_DIR`: Directory for temporary file storage (None = system default)
159
- `UPLOAD_KEEP_FILENAME`: Preserve original filenames when saving to disk
160
- `UPLOAD_KEEP_EXTENSIONS`: Preserve file extensions
161
- `UPLOAD_ERROR_ON_BAD_CTE`: Raise error on bad Content-Transfer-Encoding
162
- `MAX_MEMORY_FILE_SIZE`: Maximum file size to keep in memory (bytes)
163
- `MAX_BODY_SIZE`: Maximum total request body size
164
165
**Usage Example:**
166
167
```python
168
import python_multipart
169
from python_multipart.multipart import parse_options_header
170
171
def handle_multipart_upload(request_headers, input_stream):
172
# Parse Content-Type header
173
content_type, params = parse_options_header(request_headers['Content-Type'])
174
boundary = params.get(b'boundary')
175
176
uploaded_files = []
177
form_fields = []
178
179
def on_field(field):
180
form_fields.append({
181
'name': field.field_name.decode('utf-8'),
182
'value': field.value.decode('utf-8') if field.value else ''
183
})
184
185
def on_file(file):
186
# Save file info and move to permanent location if needed
187
file_info = {
188
'field_name': file.field_name.decode('utf-8'),
189
'file_name': file.file_name.decode('utf-8') if file.file_name else 'unnamed',
190
'size': file.size,
191
'content_type': getattr(file, 'content_type', 'application/octet-stream')
192
}
193
194
if file.in_memory:
195
# File is in memory, can read directly
196
file_info['data'] = file.file_object.getvalue()
197
else:
198
# File is on disk, need to move or copy
199
file_info['temp_path'] = file.actual_file_name
200
201
uploaded_files.append(file_info)
202
file.close()
203
204
# Configure parser
205
config = {
206
'MAX_MEMORY_FILE_SIZE': 5 * 1024 * 1024, # 5MB
207
'UPLOAD_DIR': '/tmp/uploads'
208
}
209
210
# Create and use parser
211
parser = python_multipart.FormParser(
212
content_type.decode('utf-8'),
213
on_field,
214
on_file,
215
config=config
216
)
217
218
# Process data in chunks
219
while True:
220
chunk = input_stream.read(8192)
221
if not chunk:
222
break
223
parser.write(chunk)
224
225
parser.finalize()
226
parser.close()
227
228
return {
229
'fields': form_fields,
230
'files': uploaded_files
231
}
232
```
233
234
### Content Type Support
235
236
FormParser automatically detects and handles multiple content types:
237
238
- **multipart/form-data**: Uses MultipartParser for file uploads and form fields
239
- **application/x-www-form-urlencoded**: Uses QuerystringParser for URL-encoded forms
240
- **application/octet-stream**: Uses OctetStreamParser for binary data uploads
241
242
Each content type is processed with appropriate parsing logic while providing a unified interface through Field and File objects.