0
# Case-Insensitive Multidict
1
2
The `CIMultiDict` class provides case-insensitive multidict functionality, ideal for HTTP headers, environment variables, and other scenarios where key casing should be ignored. It inherits all functionality from `MultiDict` while treating keys case-insensitively.
3
4
## Capabilities
5
6
### Construction
7
8
Create case-insensitive multidict instances with the same initialization patterns as `MultiDict`.
9
10
```python { .api }
11
class CIMultiDict(MultiDict[_V]):
12
def __init__(self, arg: MDArg[_V] = None, /, **kwargs: _V):
13
"""
14
Create a case-insensitive mutable multidict.
15
16
Parameters:
17
- arg: Mapping, iterable of key-value pairs, or None
18
- **kwargs: Additional key-value pairs
19
20
All keys are treated case-insensitively for lookups and operations.
21
"""
22
```
23
24
Usage examples:
25
26
```python
27
# HTTP headers (case-insensitive by specification)
28
headers = CIMultiDict([
29
('Content-Type', 'text/html'),
30
('content-length', '1234'),
31
('CACHE-CONTROL', 'no-cache')
32
])
33
34
# Environment-style variables
35
env_vars = CIMultiDict({
36
'PATH': '/usr/bin',
37
'home': '/home/user'
38
})
39
40
# Mixed case initialization
41
config = CIMultiDict([('Debug', 'true')], timeout='30', MAX_SIZE='1024')
42
```
43
44
### Case-Insensitive Access
45
46
Access values using keys of any case - the original case of the first occurrence is preserved for storage.
47
48
```python { .api }
49
# All MultiDict methods work case-insensitively
50
def __getitem__(self, key: str) -> _V: ...
51
def get(self, key: str, default=None): ...
52
def getone(self, key: str, default=None): ...
53
def getall(self, key: str, default=None): ...
54
def __contains__(self, key: object) -> bool: ...
55
```
56
57
Usage examples:
58
59
```python
60
headers = CIMultiDict([
61
('Content-Type', 'text/html'),
62
('Accept-Encoding', 'gzip'),
63
('accept-encoding', 'deflate') # Second value for same key
64
])
65
66
# All these access the same key
67
print(headers['content-type']) # 'text/html'
68
print(headers['CONTENT-TYPE']) # 'text/html'
69
print(headers['Content-Type']) # 'text/html'
70
71
# Get all values case-insensitively
72
print(headers.getall('ACCEPT-ENCODING')) # ['gzip', 'deflate']
73
74
# Check existence case-insensitively
75
print('content-type' in headers) # True
76
print('CONTENT-TYPE' in headers) # True
77
```
78
79
### Case-Insensitive Modifications
80
81
All modification operations work case-insensitively while preserving the original case of the first key occurrence.
82
83
```python { .api }
84
def add(self, key: str, value: _V) -> None: ...
85
def __setitem__(self, key: str, value: _V) -> None: ...
86
def __delitem__(self, key: str) -> None: ...
87
def update(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None: ...
88
def extend(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None: ...
89
def merge(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None: ...
90
```
91
92
Usage examples:
93
94
```python
95
headers = CIMultiDict([('Content-Type', 'text/html')])
96
97
# Add to existing key (case-insensitive match)
98
headers.add('content-type', 'application/json')
99
print(headers.getall('Content-Type')) # ['text/html', 'application/json']
100
101
# Set value (replaces all, case-insensitive)
102
headers['CONTENT-TYPE'] = 'text/plain'
103
print(headers['content-type']) # 'text/plain'
104
105
# Delete case-insensitively
106
del headers['content-type']
107
print('Content-Type' in headers) # False
108
```
109
110
### Key Case Preservation
111
112
The multidict preserves the case of keys as first encountered, while treating all operations case-insensitively.
113
114
```python
115
headers = CIMultiDict()
116
117
# First occurrence sets the case
118
headers.add('Content-Type', 'text/html')
119
headers.add('content-type', 'application/json') # Same key, different case
120
121
# Original case is preserved in iteration
122
print(list(headers.keys())) # ['Content-Type', 'Content-Type']
123
124
# But access works case-insensitively
125
print(headers['CONTENT-TYPE']) # 'text/html' (first value)
126
```
127
128
### HTTP Header Patterns
129
130
Common patterns for working with HTTP headers using case-insensitive multidicts.
131
132
```python
133
# Parse HTTP headers
134
def parse_headers(header_lines):
135
headers = CIMultiDict()
136
for line in header_lines:
137
if ':' in line:
138
key, value = line.split(':', 1)
139
headers.add(key.strip(), value.strip())
140
return headers
141
142
# Common header operations
143
headers = CIMultiDict([
144
('Accept', 'text/html'),
145
('Accept', 'application/xml'),
146
('User-Agent', 'MyApp/1.0'),
147
('accept-encoding', 'gzip')
148
])
149
150
# Check for specific encodings
151
if 'accept-encoding' in headers:
152
encodings = headers.getall('Accept-Encoding') # Case-insensitive
153
if 'gzip' in encodings:
154
print("Client accepts gzip compression")
155
156
# Set security headers
157
headers['X-Frame-Options'] = 'DENY'
158
headers['x-content-type-options'] = 'nosniff' # Different case
159
160
# Both access the same data
161
print(headers['X-CONTENT-TYPE-OPTIONS']) # 'nosniff'
162
```
163
164
### Integration with Regular MultiDict
165
166
Case-insensitive multidicts can work alongside regular multidicts and convert between them.
167
168
```python { .api }
169
def copy(self) -> 'CIMultiDict[_V]':
170
"""Return a shallow copy as a CIMultiDict."""
171
```
172
173
Usage examples:
174
175
```python
176
# Convert regular multidict to case-insensitive
177
regular_md = MultiDict([('Key', 'value1'), ('key', 'value2')])
178
ci_md = CIMultiDict(regular_md) # Now case-insensitive
179
180
# Convert back to regular (preserves current key cases)
181
back_to_regular = MultiDict(ci_md)
182
183
# Copy maintains case-insensitive behavior
184
ci_copy = ci_md.copy() # Returns CIMultiDict
185
```
186
187
### Working with istr Keys
188
189
When working with case-insensitive strings directly, you can use `istr` objects.
190
191
```python
192
from multidict import CIMultiDict, istr
193
194
headers = CIMultiDict()
195
196
# Both regular strings and istr work the same way
197
headers['Content-Type'] = 'text/html'
198
headers[istr('content-type')] = 'application/json' # Replaces previous
199
200
# istr comparison is case-insensitive
201
key1 = istr('Content-Type')
202
key2 = istr('content-type')
203
print(key1 == key2) # True
204
205
# But regular string behavior is preserved in storage
206
print(str(key1)) # 'Content-Type' (original case)
207
```
208
209
### Environment Variables Pattern
210
211
Common pattern for case-insensitive environment-like configuration.
212
213
```python
214
# Environment variables (case-insensitive on Windows)
215
env = CIMultiDict([
216
('PATH', '/usr/bin:/bin'),
217
('Home', '/home/user'),
218
('PYTHONPATH', '/opt/python')
219
])
220
221
# Access regardless of case
222
print(env.get('path')) # '/usr/bin:/bin'
223
print(env.get('HOME')) # '/home/user'
224
print(env.get('pythonpath')) # '/opt/python'
225
226
# Set new variables
227
env['temp'] = '/tmp'
228
env['TEMP'] = '/var/tmp' # Replaces previous (case-insensitive)
229
230
print(env['temp']) # '/var/tmp'
231
```