Dictionary-like collection where keys can occur multiple times, optimized for HTTP headers and URL query strings
—
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.
Create case-insensitive multidict instances with the same initialization patterns as MultiDict.
class CIMultiDict(MultiDict[_V]):
def __init__(self, arg: MDArg[_V] = None, /, **kwargs: _V):
"""
Create a case-insensitive mutable multidict.
Parameters:
- arg: Mapping, iterable of key-value pairs, or None
- **kwargs: Additional key-value pairs
All keys are treated case-insensitively for lookups and operations.
"""Usage examples:
# HTTP headers (case-insensitive by specification)
headers = CIMultiDict([
('Content-Type', 'text/html'),
('content-length', '1234'),
('CACHE-CONTROL', 'no-cache')
])
# Environment-style variables
env_vars = CIMultiDict({
'PATH': '/usr/bin',
'home': '/home/user'
})
# Mixed case initialization
config = CIMultiDict([('Debug', 'true')], timeout='30', MAX_SIZE='1024')Access values using keys of any case - the original case of the first occurrence is preserved for storage.
# All MultiDict methods work case-insensitively
def __getitem__(self, key: str) -> _V: ...
def get(self, key: str, default=None): ...
def getone(self, key: str, default=None): ...
def getall(self, key: str, default=None): ...
def __contains__(self, key: object) -> bool: ...Usage examples:
headers = CIMultiDict([
('Content-Type', 'text/html'),
('Accept-Encoding', 'gzip'),
('accept-encoding', 'deflate') # Second value for same key
])
# All these access the same key
print(headers['content-type']) # 'text/html'
print(headers['CONTENT-TYPE']) # 'text/html'
print(headers['Content-Type']) # 'text/html'
# Get all values case-insensitively
print(headers.getall('ACCEPT-ENCODING')) # ['gzip', 'deflate']
# Check existence case-insensitively
print('content-type' in headers) # True
print('CONTENT-TYPE' in headers) # TrueAll modification operations work case-insensitively while preserving the original case of the first key occurrence.
def add(self, key: str, value: _V) -> None: ...
def __setitem__(self, key: str, value: _V) -> None: ...
def __delitem__(self, key: str) -> None: ...
def update(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None: ...
def extend(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None: ...
def merge(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None: ...Usage examples:
headers = CIMultiDict([('Content-Type', 'text/html')])
# Add to existing key (case-insensitive match)
headers.add('content-type', 'application/json')
print(headers.getall('Content-Type')) # ['text/html', 'application/json']
# Set value (replaces all, case-insensitive)
headers['CONTENT-TYPE'] = 'text/plain'
print(headers['content-type']) # 'text/plain'
# Delete case-insensitively
del headers['content-type']
print('Content-Type' in headers) # FalseThe multidict preserves the case of keys as first encountered, while treating all operations case-insensitively.
headers = CIMultiDict()
# First occurrence sets the case
headers.add('Content-Type', 'text/html')
headers.add('content-type', 'application/json') # Same key, different case
# Original case is preserved in iteration
print(list(headers.keys())) # ['Content-Type', 'Content-Type']
# But access works case-insensitively
print(headers['CONTENT-TYPE']) # 'text/html' (first value)Common patterns for working with HTTP headers using case-insensitive multidicts.
# Parse HTTP headers
def parse_headers(header_lines):
headers = CIMultiDict()
for line in header_lines:
if ':' in line:
key, value = line.split(':', 1)
headers.add(key.strip(), value.strip())
return headers
# Common header operations
headers = CIMultiDict([
('Accept', 'text/html'),
('Accept', 'application/xml'),
('User-Agent', 'MyApp/1.0'),
('accept-encoding', 'gzip')
])
# Check for specific encodings
if 'accept-encoding' in headers:
encodings = headers.getall('Accept-Encoding') # Case-insensitive
if 'gzip' in encodings:
print("Client accepts gzip compression")
# Set security headers
headers['X-Frame-Options'] = 'DENY'
headers['x-content-type-options'] = 'nosniff' # Different case
# Both access the same data
print(headers['X-CONTENT-TYPE-OPTIONS']) # 'nosniff'Case-insensitive multidicts can work alongside regular multidicts and convert between them.
def copy(self) -> 'CIMultiDict[_V]':
"""Return a shallow copy as a CIMultiDict."""Usage examples:
# Convert regular multidict to case-insensitive
regular_md = MultiDict([('Key', 'value1'), ('key', 'value2')])
ci_md = CIMultiDict(regular_md) # Now case-insensitive
# Convert back to regular (preserves current key cases)
back_to_regular = MultiDict(ci_md)
# Copy maintains case-insensitive behavior
ci_copy = ci_md.copy() # Returns CIMultiDictWhen working with case-insensitive strings directly, you can use istr objects.
from multidict import CIMultiDict, istr
headers = CIMultiDict()
# Both regular strings and istr work the same way
headers['Content-Type'] = 'text/html'
headers[istr('content-type')] = 'application/json' # Replaces previous
# istr comparison is case-insensitive
key1 = istr('Content-Type')
key2 = istr('content-type')
print(key1 == key2) # True
# But regular string behavior is preserved in storage
print(str(key1)) # 'Content-Type' (original case)Common pattern for case-insensitive environment-like configuration.
# Environment variables (case-insensitive on Windows)
env = CIMultiDict([
('PATH', '/usr/bin:/bin'),
('Home', '/home/user'),
('PYTHONPATH', '/opt/python')
])
# Access regardless of case
print(env.get('path')) # '/usr/bin:/bin'
print(env.get('HOME')) # '/home/user'
print(env.get('pythonpath')) # '/opt/python'
# Set new variables
env['temp'] = '/tmp'
env['TEMP'] = '/var/tmp' # Replaces previous (case-insensitive)
print(env['temp']) # '/var/tmp'Install with Tessl CLI
npx tessl i tessl/pypi-multidict