0
# Data Structures
1
2
Data structure classes that provide enhanced dictionary interfaces with special behaviors for HTTP-related operations. These structures are used throughout the requests library for headers, status codes, and general data handling.
3
4
## Capabilities
5
6
### CaseInsensitiveDict Class
7
8
Dictionary with case-insensitive key lookup, primarily used for HTTP headers.
9
10
```python { .api }
11
class CaseInsensitiveDict(dict):
12
"""
13
A case-insensitive dictionary implementation.
14
15
Used for HTTP headers where keys should be treated case-insensitively
16
according to HTTP specification.
17
"""
18
19
def __init__(self, data=None, **kwargs):
20
"""
21
Initialize CaseInsensitiveDict.
22
23
Parameters:
24
- data: Initial data (dict, list of tuples, or another CaseInsensitiveDict)
25
- **kwargs: Additional key-value pairs
26
"""
27
28
def __setitem__(self, key, value):
29
"""Set value with case-insensitive key."""
30
31
def __getitem__(self, key):
32
"""Get value with case-insensitive key lookup."""
33
34
def __delitem__(self, key):
35
"""Delete item with case-insensitive key lookup."""
36
37
def __iter__(self):
38
"""Iterate over original-case keys."""
39
40
def __len__(self) -> int:
41
"""Get number of items."""
42
43
def __eq__(self, other) -> bool:
44
"""Compare with another mapping (case-insensitive)."""
45
46
def __repr__(self) -> str:
47
"""String representation."""
48
49
def lower_items(self):
50
"""
51
Iterate over (lowercase_key, value) pairs.
52
53
Yields:
54
Tuples of (lowercase_key, value)
55
"""
56
57
def copy(self) -> 'CaseInsensitiveDict':
58
"""Create a copy of the dictionary."""
59
```
60
61
### LookupDict Class
62
63
Dictionary subclass with attribute-style access, used for status codes.
64
65
```python { .api }
66
class LookupDict(dict):
67
"""
68
A dictionary that supports both item and attribute access.
69
70
Used for status codes to enable both codes['ok'] and codes.ok syntax.
71
"""
72
73
def __init__(self, name=None):
74
"""
75
Initialize LookupDict.
76
77
Parameters:
78
- name: Optional name for the lookup dict
79
"""
80
81
def __getitem__(self, key):
82
"""Get item with fallback to __dict__ lookup."""
83
84
def get(self, key, default=None):
85
"""
86
Get value with default.
87
88
Parameters:
89
- key: Key to look up
90
- default: Default value if key not found
91
92
Returns:
93
Value or default
94
"""
95
96
def __repr__(self) -> str:
97
"""String representation."""
98
```
99
100
## Usage Examples
101
102
### CaseInsensitiveDict Usage
103
104
```python
105
import requests
106
from requests.structures import CaseInsensitiveDict
107
108
# CaseInsensitiveDict is used for response headers
109
response = requests.get('https://httpbin.org/get')
110
headers = response.headers
111
112
# All these access the same header (case-insensitive)
113
print(headers['Content-Type']) # Works
114
print(headers['content-type']) # Works
115
print(headers['CONTENT-TYPE']) # Works
116
print(headers['Content-type']) # Works
117
118
# Check header existence (case-insensitive)
119
print('content-type' in headers) # True
120
print('Content-Type' in headers) # True
121
122
# Iterate over headers (preserves original case)
123
for name, value in headers.items():
124
print(f"{name}: {value}")
125
```
126
127
### Manual CaseInsensitiveDict Creation
128
129
```python
130
from requests.structures import CaseInsensitiveDict
131
132
# Create from dict
133
headers = CaseInsensitiveDict({
134
'Content-Type': 'application/json',
135
'Authorization': 'Bearer token123',
136
'User-Agent': 'MyApp/1.0'
137
})
138
139
# Access with any case
140
print(headers['content-type']) # 'application/json'
141
print(headers['AUTHORIZATION']) # 'Bearer token123'
142
print(headers['user-agent']) # 'MyApp/1.0'
143
144
# Set with any case
145
headers['accept'] = 'application/json'
146
headers['CACHE-CONTROL'] = 'no-cache'
147
148
# Check existence (case-insensitive)
149
print('Accept' in headers) # True
150
print('cache-control' in headers) # True
151
152
# Delete with any case
153
del headers['USER-AGENT']
154
print('user-agent' in headers) # False
155
```
156
157
### CaseInsensitiveDict Operations
158
159
```python
160
from requests.structures import CaseInsensitiveDict
161
162
# Create from various sources
163
headers1 = CaseInsensitiveDict([
164
('Content-Type', 'application/json'),
165
('Accept', 'application/json')
166
])
167
168
headers2 = CaseInsensitiveDict(
169
authorization='Bearer token',
170
user_agent='MyApp/1.0'
171
)
172
173
# Copy dictionary
174
headers_copy = headers1.copy()
175
headers_copy['X-Custom'] = 'custom-value'
176
177
# Compare dictionaries (case-insensitive)
178
h1 = CaseInsensitiveDict({'Content-Type': 'application/json'})
179
h2 = CaseInsensitiveDict({'content-type': 'application/json'})
180
print(h1 == h2) # True (case-insensitive comparison)
181
182
# Get lowercase items for processing
183
headers = CaseInsensitiveDict({
184
'Content-Type': 'application/json',
185
'Accept': 'application/json',
186
'Authorization': 'Bearer token'
187
})
188
189
for lower_key, value in headers.lower_items():
190
print(f"{lower_key}: {value}")
191
# content-type: application/json
192
# accept: application/json
193
# authorization: Bearer token
194
```
195
196
### LookupDict Usage
197
198
```python
199
import requests
200
201
# LookupDict is used for status codes
202
codes = requests.codes
203
204
# Attribute access
205
print(codes.ok) # 200
206
print(codes.not_found) # 404
207
print(codes.internal_server_error) # 500
208
209
# Dictionary access
210
print(codes['ok']) # 200
211
print(codes['not_found']) # 404
212
print(codes['server_error']) # 500
213
214
# Both work the same way
215
status_code = 404
216
if status_code == codes.not_found:
217
print("Resource not found")
218
219
if status_code == codes['not_found']:
220
print("Resource not found")
221
```
222
223
### Custom LookupDict
224
225
```python
226
from requests.structures import LookupDict
227
228
# Create custom lookup dict
229
http_methods = LookupDict('HTTP Methods')
230
http_methods.update({
231
'get': 'GET',
232
'post': 'POST',
233
'put': 'PUT',
234
'delete': 'DELETE',
235
'head': 'HEAD',
236
'options': 'OPTIONS',
237
'patch': 'PATCH'
238
})
239
240
# Use both access methods
241
print(http_methods.get) # 'GET'
242
print(http_methods['post']) # 'POST'
243
print(http_methods.delete) # 'DELETE'
244
245
# Get with default
246
print(http_methods.get('trace', 'TRACE')) # 'TRACE'
247
print(http_methods.get('connect')) # None
248
```
249
250
### Practical Header Management
251
252
```python
253
import requests
254
from requests.structures import CaseInsensitiveDict
255
256
def build_headers(content_type=None, auth_token=None, custom_headers=None):
257
"""Build headers with case-insensitive handling."""
258
headers = CaseInsensitiveDict()
259
260
# Set standard headers
261
headers['User-Agent'] = 'MyApp/2.0'
262
263
if content_type:
264
headers['Content-Type'] = content_type
265
266
if auth_token:
267
headers['Authorization'] = f'Bearer {auth_token}'
268
269
# Merge custom headers (case-insensitive)
270
if custom_headers:
271
for key, value in custom_headers.items():
272
headers[key] = value
273
274
return headers
275
276
# Build headers
277
headers = build_headers(
278
content_type='application/json',
279
auth_token='abc123',
280
custom_headers={
281
'x-api-version': '2.0',
282
'X-CUSTOM-HEADER': 'custom-value'
283
}
284
)
285
286
# Use with request
287
response = requests.post('https://httpbin.org/post',
288
headers=headers,
289
json={'key': 'value'})
290
291
# Examine sent headers (case preserved)
292
sent_headers = response.request.headers
293
for name, value in sent_headers.items():
294
print(f"{name}: {value}")
295
```
296
297
### Header Manipulation
298
299
```python
300
import requests
301
from requests.structures import CaseInsensitiveDict
302
303
# Start with response headers
304
response = requests.get('https://httpbin.org/response-headers?foo=bar')
305
headers = response.headers
306
307
print(f"Original headers: {len(headers)} items")
308
309
# Modify headers (case-insensitive)
310
modified_headers = headers.copy()
311
modified_headers['cache-control'] = 'no-cache' # Might overwrite Cache-Control
312
modified_headers['X-Custom'] = 'custom-value' # Add new header
313
del modified_headers['date'] # Remove Date header
314
315
# Use modified headers in new request
316
new_response = requests.get('https://httpbin.org/headers',
317
headers=modified_headers)
318
319
# Check what was sent
320
request_headers = new_response.json()['headers']
321
for name, value in request_headers.items():
322
print(f"{name}: {value}")
323
```
324
325
### Session Header Management
326
327
```python
328
import requests
329
from requests.structures import CaseInsensitiveDict
330
331
# Session headers are CaseInsensitiveDict
332
session = requests.Session()
333
print(type(session.headers)) # <class 'requests.structures.CaseInsensitiveDict'>
334
335
# Set session headers (case-insensitive)
336
session.headers.update({
337
'User-Agent': 'MyApp/1.0',
338
'accept': 'application/json',
339
'AUTHORIZATION': 'Bearer token123'
340
})
341
342
# Check headers with different cases
343
print('user-agent' in session.headers) # True
344
print(session.headers['Accept']) # 'application/json'
345
print(session.headers['authorization']) # 'Bearer token123'
346
347
# Headers persist across requests
348
response1 = session.get('https://httpbin.org/headers')
349
response2 = session.post('https://httpbin.org/post', json={'data': 'test'})
350
351
# Both requests include the session headers
352
print("Request 1 headers:", response1.json()['headers'])
353
print("Request 2 headers:", response2.json()['headers'])
354
```
355
356
### Status Code Management
357
358
```python
359
import requests
360
from requests.structures import LookupDict
361
362
# The codes object is a LookupDict
363
print(type(requests.codes)) # <class 'requests.structures.LookupDict'>
364
365
# Create custom status mappings
366
api_codes = LookupDict('API Status Codes')
367
api_codes.update({
368
'success': 200,
369
'created': 201,
370
'accepted': 202,
371
'bad_request': 400,
372
'unauthorized': 401,
373
'forbidden': 403,
374
'not_found': 404,
375
'server_error': 500
376
})
377
378
# Use in response handling
379
def handle_api_response(response):
380
status = response.status_code
381
382
if status == api_codes.success:
383
return response.json()
384
elif status == api_codes.created:
385
print("Resource created")
386
return response.json()
387
elif status == api_codes.not_found:
388
print("Resource not found")
389
return None
390
elif status == api_codes.server_error:
391
print("Server error occurred")
392
return None
393
else:
394
print(f"Unexpected status: {status}")
395
return None
396
397
# Test with different responses
398
responses = [
399
requests.get('https://httpbin.org/status/200'),
400
requests.get('https://httpbin.org/status/404'),
401
requests.get('https://httpbin.org/status/500')
402
]
403
404
for resp in responses:
405
result = handle_api_response(resp)
406
print(f"Status {resp.status_code}: {result}")
407
```
408
409
## Structure Comparison
410
411
### Standard Dict vs CaseInsensitiveDict
412
413
```python
414
from requests.structures import CaseInsensitiveDict
415
416
# Standard dict - case-sensitive
417
standard = {'Content-Type': 'application/json'}
418
print('content-type' in standard) # False
419
print('Content-Type' in standard) # True
420
421
# CaseInsensitiveDict - case-insensitive
422
case_insensitive = CaseInsensitiveDict({'Content-Type': 'application/json'})
423
print('content-type' in case_insensitive) # True
424
print('Content-Type' in case_insensitive) # True
425
print('CONTENT-TYPE' in case_insensitive) # True
426
427
# Key preservation
428
print(list(standard.keys())) # ['Content-Type']
429
print(list(case_insensitive.keys())) # ['Content-Type'] (original case preserved)
430
431
# Lowercase iteration
432
for key, value in case_insensitive.lower_items():
433
print(f"Lowercase: {key} -> {value}") # content-type -> application/json
434
```
435
436
### Standard Dict vs LookupDict
437
438
```python
439
from requests.structures import LookupDict
440
441
# Standard dict - item access only
442
standard = {'ok': 200, 'not_found': 404}
443
print(standard['ok']) # 200
444
# print(standard.ok) # AttributeError
445
446
# LookupDict - both item and attribute access
447
lookup = LookupDict()
448
lookup.update({'ok': 200, 'not_found': 404})
449
print(lookup['ok']) # 200
450
print(lookup.ok) # 200 (attribute access)
451
print(lookup.not_found) # 404
452
453
# Graceful handling of missing keys
454
print(lookup.get('missing')) # None
455
print(lookup.get('missing', 'default')) # 'default'
456
```