or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-objects.mddecoders.mdexceptions.mdform-parsing.mdindex.mdstreaming-parsers.md

form-parsing.mddocs/

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.