CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-multidict

Dictionary-like collection where keys can occur multiple times, optimized for HTTP headers and URL query strings

Pending
Overview
Eval results
Files

case-insensitive.mddocs/

Case-Insensitive Multidict

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.

Capabilities

Construction

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')

Case-Insensitive Access

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)     # True

Case-Insensitive Modifications

All 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)  # False

Key Case Preservation

The 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)

HTTP Header Patterns

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'

Integration with Regular MultiDict

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 CIMultiDict

Working with istr Keys

When 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)

Environment Variables Pattern

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

docs

case-insensitive.md

immutable-proxies.md

index.md

mutable-multidict.md

string-types-utilities.md

tile.json