or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

auth.mdchannel.mddiscovery.mderrors.mdhttp.mdindex.mdmedia.mdmimeparse.mdmodel.mdschema.mdtesting.md

mimeparse.mddocs/

0

# MIME Type Parsing and Content Negotiation

1

2

The mimeparse module provides utilities for parsing MIME types, media ranges, and performing content negotiation. It helps applications determine the best content type to serve based on client preferences and server capabilities.

3

4

## Capabilities

5

6

### MIME Type Parsing

7

8

Parse MIME type strings into structured components for analysis and comparison.

9

10

```python { .api }

11

def parse_mime_type(mime_type):

12

"""

13

Parse a MIME type string into its components.

14

15

Args:

16

mime_type (str): MIME type string (e.g., 'text/html; charset=utf-8')

17

18

Returns:

19

tuple: (type, subtype, params) where:

20

- type (str): Main type (e.g., 'text')

21

- subtype (str): Subtype (e.g., 'html')

22

- params (dict): Parameters (e.g., {'charset': 'utf-8'})

23

"""

24

25

def parse_media_range(range):

26

"""

27

Parse a media range string into components with quality factor.

28

29

Args:

30

range (str): Media range string (e.g., 'text/html;q=0.9')

31

32

Returns:

33

tuple: (type, subtype, params, quality) where:

34

- type (str): Main type

35

- subtype (str): Subtype

36

- params (dict): Parameters excluding quality

37

- quality (float): Quality factor (0.0 to 1.0)

38

"""

39

```

40

41

### Content Negotiation

42

43

Determine the best content type match based on client preferences and server capabilities.

44

45

```python { .api }

46

def quality_parsed(mime_type_list, ranges):

47

"""

48

Calculate quality for parsed MIME types against client ranges.

49

50

Args:

51

mime_type_list (list): List of (type, subtype, params) tuples

52

ranges (list): List of parsed media ranges with quality factors

53

54

Returns:

55

float: Quality factor (0.0 to 1.0) for the best match

56

"""

57

58

def quality(mime_type, ranges):

59

"""

60

Calculate quality of a MIME type against client Accept header ranges.

61

62

Args:

63

mime_type (str): MIME type to evaluate (e.g., 'application/json')

64

ranges (str): Accept header value with media ranges and quality factors

65

66

Returns:

67

float: Quality factor (0.0 to 1.0) indicating preference level

68

"""

69

70

def best_match(supported, header):

71

"""

72

Find the best matching MIME type from supported types.

73

74

Args:

75

supported (list): List of MIME types supported by the server

76

header (str): Client Accept header value

77

78

Returns:

79

str: Best matching MIME type from supported list, or None if no match

80

"""

81

```

82

83

## Usage Examples

84

85

### Basic MIME Type Parsing

86

87

```python

88

from googleapiclient.mimeparse import parse_mime_type, parse_media_range

89

90

# Parse a simple MIME type

91

mime_type = "text/html"

92

type_, subtype, params = parse_mime_type(mime_type)

93

print(f"Type: {type_}, Subtype: {subtype}, Params: {params}")

94

# Output: Type: text, Subtype: html, Params: {}

95

96

# Parse MIME type with parameters

97

mime_type_with_params = "text/html; charset=utf-8; boundary=something"

98

type_, subtype, params = parse_mime_type(mime_type_with_params)

99

print(f"Type: {type_}, Subtype: {subtype}, Params: {params}")

100

# Output: Type: text, Subtype: html, Params: {'charset': 'utf-8', 'boundary': 'something'}

101

102

# Parse media range with quality factor

103

media_range = "application/json;q=0.8"

104

type_, subtype, params, quality = parse_media_range(media_range)

105

print(f"Quality: {quality}")

106

# Output: Quality: 0.8

107

```

108

109

### Content Negotiation

110

111

```python

112

from googleapiclient.mimeparse import best_match, quality

113

114

# Server-supported MIME types

115

supported_types = [

116

'application/json',

117

'application/xml',

118

'text/html',

119

'text/plain'

120

]

121

122

# Client Accept headers (examples)

123

accept_headers = [

124

'application/json, application/xml;q=0.9, */*;q=0.1',

125

'text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8',

126

'application/xml, application/json;q=0.8, text/plain;q=0.5',

127

'*/*'

128

]

129

130

for accept_header in accept_headers:

131

best = best_match(supported_types, accept_header)

132

print(f"Accept: {accept_header}")

133

print(f"Best match: {best}")

134

print()

135

136

# Check quality of specific MIME type

137

mime_type = 'application/json'

138

accept_header = 'application/json;q=0.9, application/xml;q=0.8, */*;q=0.1'

139

quality_score = quality(mime_type, accept_header)

140

print(f"Quality of {mime_type}: {quality_score}")

141

```

142

143

### API Response Content Negotiation

144

145

```python

146

from googleapiclient.mimeparse import best_match

147

from googleapiclient import discovery

148

from flask import Flask, request

149

150

app = Flask(__name__)

151

152

class ContentNegotiator:

153

"""Handle content negotiation for API responses."""

154

155

def __init__(self):

156

self.supported_formats = {

157

'application/json': self._format_json,

158

'application/xml': self._format_xml,

159

'text/csv': self._format_csv,

160

'text/plain': self._format_plain

161

}

162

163

def negotiate_response_format(self, data, accept_header):

164

"""

165

Negotiate the best response format based on Accept header.

166

167

Args:

168

data: Response data to format

169

accept_header (str): Client Accept header

170

171

Returns:

172

tuple: (formatted_data, content_type)

173

"""

174

supported_types = list(self.supported_formats.keys())

175

best_type = best_match(supported_types, accept_header)

176

177

if not best_type:

178

# Default to JSON if no match

179

best_type = 'application/json'

180

181

formatter = self.supported_formats[best_type]

182

formatted_data = formatter(data)

183

184

return formatted_data, best_type

185

186

def _format_json(self, data):

187

"""Format data as JSON."""

188

import json

189

return json.dumps(data, indent=2)

190

191

def _format_xml(self, data):

192

"""Format data as XML."""

193

# Simplified XML formatting

194

def dict_to_xml(d, root_name='root'):

195

xml = f'<{root_name}>'

196

for key, value in d.items():

197

if isinstance(value, dict):

198

xml += dict_to_xml(value, key)

199

elif isinstance(value, list):

200

for item in value:

201

xml += dict_to_xml(item, key[:-1] if key.endswith('s') else key)

202

else:

203

xml += f'<{key}>{value}</{key}>'

204

xml += f'</{root_name}>'

205

return xml

206

207

return dict_to_xml(data, 'response')

208

209

def _format_csv(self, data):

210

"""Format data as CSV."""

211

import csv

212

import io

213

214

output = io.StringIO()

215

if isinstance(data, list) and data:

216

# Assume list of dictionaries

217

fieldnames = data[0].keys()

218

writer = csv.DictWriter(output, fieldnames=fieldnames)

219

writer.writeheader()

220

writer.writerows(data)

221

elif isinstance(data, dict):

222

# Single dictionary

223

writer = csv.DictWriter(output, fieldnames=data.keys())

224

writer.writeheader()

225

writer.writerow(data)

226

227

return output.getvalue()

228

229

def _format_plain(self, data):

230

"""Format data as plain text."""

231

return str(data)

232

233

# Flask route with content negotiation

234

negotiator = ContentNegotiator()

235

236

@app.route('/api/messages')

237

def get_messages():

238

# Get messages from Gmail API

239

service = discovery.build('gmail', 'v1', credentials=credentials)

240

try:

241

messages_result = service.users().messages().list(

242

userId='me',

243

maxResults=10

244

).execute()

245

246

messages = messages_result.get('messages', [])

247

248

# Negotiate response format

249

accept_header = request.headers.get('Accept', 'application/json')

250

formatted_data, content_type = negotiator.negotiate_response_format(

251

messages,

252

accept_header

253

)

254

255

return formatted_data, 200, {'Content-Type': content_type}

256

257

except Exception as e:

258

error_data = {'error': str(e)}

259

accept_header = request.headers.get('Accept', 'application/json')

260

formatted_error, content_type = negotiator.negotiate_response_format(

261

error_data,

262

accept_header

263

)

264

return formatted_error, 500, {'Content-Type': content_type}

265

266

if __name__ == '__main__':

267

app.run(debug=True)

268

```

269

270

### MIME Type Validation

271

272

```python

273

from googleapiclient.mimeparse import parse_mime_type

274

275

class MimeTypeValidator:

276

"""Validate and analyze MIME types."""

277

278

def __init__(self):

279

self.allowed_types = {

280

'text': ['plain', 'html', 'csv', 'css', 'javascript'],

281

'application': ['json', 'xml', 'pdf', 'zip', 'octet-stream'],

282

'image': ['jpeg', 'png', 'gif', 'svg+xml'],

283

'audio': ['mpeg', 'wav', 'ogg'],

284

'video': ['mp4', 'mpeg', 'quicktime']

285

}

286

287

def is_valid_mime_type(self, mime_type):

288

"""

289

Check if a MIME type is valid and allowed.

290

291

Args:

292

mime_type (str): MIME type to validate

293

294

Returns:

295

tuple: (is_valid, reason)

296

"""

297

try:

298

type_, subtype, params = parse_mime_type(mime_type)

299

except Exception as e:

300

return False, f"Invalid MIME type format: {e}"

301

302

if type_ not in self.allowed_types:

303

return False, f"Type '{type_}' not allowed"

304

305

if subtype not in self.allowed_types[type_]:

306

return False, f"Subtype '{subtype}' not allowed for type '{type_}'"

307

308

return True, "Valid MIME type"

309

310

def get_file_categories(self, mime_types):

311

"""Categorize MIME types by file type."""

312

categories = {

313

'documents': [],

314

'images': [],

315

'media': [],

316

'data': [],

317

'other': []

318

}

319

320

for mime_type in mime_types:

321

try:

322

type_, subtype, _ = parse_mime_type(mime_type)

323

324

if type_ == 'text' or (type_ == 'application' and subtype in ['pdf', 'msword']):

325

categories['documents'].append(mime_type)

326

elif type_ == 'image':

327

categories['images'].append(mime_type)

328

elif type_ in ['audio', 'video']:

329

categories['media'].append(mime_type)

330

elif type_ == 'application' and subtype in ['json', 'xml', 'csv']:

331

categories['data'].append(mime_type)

332

else:

333

categories['other'].append(mime_type)

334

335

except Exception:

336

categories['other'].append(mime_type)

337

338

return categories

339

340

# Usage

341

validator = MimeTypeValidator()

342

343

test_mime_types = [

344

'text/plain',

345

'application/json',

346

'image/jpeg',

347

'invalid/mime/type',

348

'application/unknown'

349

]

350

351

for mime_type in test_mime_types:

352

is_valid, reason = validator.is_valid_mime_type(mime_type)

353

print(f"{mime_type}: {'✓' if is_valid else '✗'} - {reason}")

354

355

# Categorize file types

356

file_mime_types = [

357

'text/plain', 'application/pdf', 'image/jpeg',

358

'audio/mpeg', 'application/json', 'text/html'

359

]

360

361

categories = validator.get_file_categories(file_mime_types)

362

for category, types in categories.items():

363

if types:

364

print(f"{category.title()}: {', '.join(types)}")

365

```

366

367

### Advanced Content Negotiation

368

369

```python

370

from googleapiclient.mimeparse import quality_parsed, parse_media_range

371

372

class AdvancedContentNegotiator:

373

"""Advanced content negotiation with custom scoring."""

374

375

def __init__(self):

376

self.type_preferences = {

377

'application/json': 1.0,

378

'application/xml': 0.8,

379

'text/html': 0.6,

380

'text/plain': 0.4

381

}

382

383

def negotiate_with_scoring(self, supported_types, accept_header):

384

"""

385

Negotiate content type with custom server preferences.

386

387

Args:

388

supported_types (list): MIME types supported by server

389

accept_header (str): Client Accept header

390

391

Returns:

392

tuple: (best_type, combined_score)

393

"""

394

# Parse client preferences

395

client_ranges = []

396

for range_str in accept_header.split(','):

397

range_str = range_str.strip()

398

try:

399

type_, subtype, params, quality = parse_media_range(range_str)

400

client_ranges.append((type_, subtype, params, quality))

401

except Exception:

402

continue

403

404

best_match = None

405

best_score = 0.0

406

407

for mime_type in supported_types:

408

# Parse server MIME type

409

try:

410

from googleapiclient.mimeparse import parse_mime_type

411

type_, subtype, params = parse_mime_type(mime_type)

412

server_parsed = [(type_, subtype, params)]

413

414

# Calculate client quality

415

client_quality = quality_parsed(server_parsed, client_ranges)

416

417

# Apply server preference

418

server_preference = self.type_preferences.get(mime_type, 0.5)

419

420

# Combined score

421

combined_score = client_quality * server_preference

422

423

if combined_score > best_score:

424

best_score = combined_score

425

best_match = mime_type

426

427

except Exception:

428

continue

429

430

return best_match, best_score

431

432

def get_negotiation_details(self, supported_types, accept_header):

433

"""Get detailed negotiation information."""

434

details = []

435

436

for mime_type in supported_types:

437

try:

438

from googleapiclient.mimeparse import quality

439

client_quality = quality(mime_type, accept_header)

440

server_preference = self.type_preferences.get(mime_type, 0.5)

441

combined_score = client_quality * server_preference

442

443

details.append({

444

'mime_type': mime_type,

445

'client_quality': client_quality,

446

'server_preference': server_preference,

447

'combined_score': combined_score

448

})

449

except Exception:

450

continue

451

452

# Sort by combined score

453

details.sort(key=lambda x: x['combined_score'], reverse=True)

454

return details

455

456

# Usage

457

negotiator = AdvancedContentNegotiator()

458

459

supported = ['application/json', 'application/xml', 'text/html', 'text/plain']

460

accept_header = 'text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8'

461

462

best_type, score = negotiator.negotiate_with_scoring(supported, accept_header)

463

print(f"Best match: {best_type} (score: {score:.3f})")

464

465

# Get detailed breakdown

466

details = negotiator.get_negotiation_details(supported, accept_header)

467

print("\nNegotiation details:")

468

for detail in details:

469

print(f" {detail['mime_type']}: "

470

f"client={detail['client_quality']:.2f}, "

471

f"server={detail['server_preference']:.2f}, "

472

f"combined={detail['combined_score']:.3f}")

473

```

474

475

### MIME Type Utilities

476

477

```python

478

from googleapiclient.mimeparse import parse_mime_type, best_match

479

480

class MimeTypeUtils:

481

"""Utility functions for working with MIME types."""

482

483

@staticmethod

484

def is_text_type(mime_type):

485

"""Check if MIME type is a text type."""

486

try:

487

type_, _, _ = parse_mime_type(mime_type)

488

return type_ == 'text'

489

except Exception:

490

return False

491

492

@staticmethod

493

def is_binary_type(mime_type):

494

"""Check if MIME type is a binary type."""

495

try:

496

type_, subtype, _ = parse_mime_type(mime_type)

497

# Common binary types

498

binary_types = {

499

'image': True,

500

'audio': True,

501

'video': True,

502

'application': subtype not in ['json', 'xml', 'javascript', 'css']

503

}

504

return binary_types.get(type_, False)

505

except Exception:

506

return False

507

508

@staticmethod

509

def get_preferred_extension(mime_type):

510

"""Get preferred file extension for MIME type."""

511

extensions = {

512

'text/plain': '.txt',

513

'text/html': '.html',

514

'text/css': '.css',

515

'application/json': '.json',

516

'application/xml': '.xml',

517

'application/pdf': '.pdf',

518

'image/jpeg': '.jpg',

519

'image/png': '.png',

520

'image/gif': '.gif',

521

'audio/mpeg': '.mp3',

522

'video/mp4': '.mp4'

523

}

524

return extensions.get(mime_type, '')

525

526

@staticmethod

527

def select_encoding(mime_type):

528

"""Select appropriate encoding for MIME type."""

529

if MimeTypeUtils.is_text_type(mime_type):

530

return 'utf-8'

531

else:

532

return 'binary'

533

534

# Usage examples

535

utils = MimeTypeUtils()

536

537

test_types = [

538

'text/plain',

539

'application/json',

540

'image/jpeg',

541

'application/pdf',

542

'audio/mpeg'

543

]

544

545

for mime_type in test_types:

546

is_text = utils.is_text_type(mime_type)

547

is_binary = utils.is_binary_type(mime_type)

548

extension = utils.get_preferred_extension(mime_type)

549

encoding = utils.select_encoding(mime_type)

550

551

print(f"{mime_type}:")

552

print(f" Text: {is_text}, Binary: {is_binary}")

553

print(f" Extension: {extension}, Encoding: {encoding}")

554

print()

555

```