CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-vcrpy

Automatically mock your HTTP interactions to simplify and speed up testing

Overview
Eval results
Files

request-response.mddocs/

Request and Response Processing

HTTP request and response representation with case-insensitive headers, body processing, and URI parsing capabilities. VCR.py provides internal models for capturing and manipulating HTTP interactions.

Capabilities

Request Class

VCR's representation of an HTTP request with automatic body and header processing.

class Request:
    """
    VCR's representation of an HTTP request.
    
    Args:
        method (str): HTTP method (GET, POST, etc.)
        uri (str): Full request URI
        body: Request body (str, bytes, file-like object, or iterable)
        headers: Request headers (dict or HeadersDict)
    """
    def __init__(self, method: str, uri: str, body, headers): ...

Request Properties

Properties providing parsed access to request components.

@property
def headers(self) -> HeadersDict:
    """Case-insensitive dictionary of request headers."""

@property
def body(self):
    """
    Request body, returned in appropriate format:
    - File-like objects: Returns BytesIO
    - Iterables: Returns iterator  
    - Other types: Returns as-is
    """

@property 
def method(self) -> str:
    """HTTP method (GET, POST, PUT, DELETE, etc.)"""

@property
def uri(self) -> str:
    """Complete request URI"""

@property
def scheme(self) -> str:
    """URI scheme (http, https)"""

@property
def host(self) -> str:
    """Request host/domain"""

@property
def port(self) -> int:
    """Request port number"""

@property
def path(self) -> str:
    """URI path component"""

@property
def query(self) -> str:
    """Query string component"""

HeadersDict Class

Case-insensitive dictionary implementation for HTTP headers.

class HeadersDict(dict):
    """
    Case-insensitive dictionary for HTTP headers.
    
    Allows access to headers regardless of case:
    headers['Content-Type'] == headers['content-type']
    """
    def __init__(self, data=None): ...

Usage Examples

Creating Requests

from vcr.request import Request, HeadersDict

# Basic request creation
request = Request(
    method='GET',
    uri='https://api.example.com/users?page=1',
    body=None,
    headers={'User-Agent': 'MyApp/1.0', 'Accept': 'application/json'}
)

# POST request with body
post_request = Request(
    method='POST',
    uri='https://api.example.com/users',
    body='{"name": "John", "email": "john@example.com"}',
    headers={
        'Content-Type': 'application/json',
        'Authorization': 'Bearer token123'
    }
)

Accessing Request Properties

# URI components
print(request.scheme)    # 'https'
print(request.host)      # 'api.example.com'  
print(request.port)      # 443 (default for https)
print(request.path)      # '/users'
print(request.query)     # 'page=1'

# Headers (case-insensitive access)
print(request.headers['User-Agent'])     # 'MyApp/1.0'
print(request.headers['user-agent'])     # 'MyApp/1.0' (same value)
print(request.headers.get('Accept'))     # 'application/json'

Body Processing

import io

# String body
request = Request(
    method='POST',
    uri='https://api.example.com/data',
    body='{"key": "value"}',
    headers={}
)
print(request.body)  # '{"key": "value"}'

# File-like body
file_obj = io.BytesIO(b'file content')
request = Request(
    method='PUT',
    uri='https://api.example.com/upload',
    body=file_obj,
    headers={}
)
# Request automatically reads and stores file content
# request.body returns BytesIO with the content

# Iterable body  
request = Request(
    method='POST',
    uri='https://api.example.com/stream',
    body=[b'chunk1', b'chunk2', b'chunk3'],
    headers={}
)
# Request converts iterable to list
# request.body returns iterator over the chunks

HeadersDict Usage

from vcr.request import HeadersDict

# Create headers dict
headers = HeadersDict({
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token123',
    'X-Custom-Header': 'custom-value'
})

# Case-insensitive access
print(headers['content-type'])       # 'application/json'
print(headers['AUTHORIZATION'])      # 'Bearer token123'
print(headers['x-custom-header'])    # 'custom-value'

# Standard dict operations work
headers['New-Header'] = 'new-value'
del headers['X-Custom-Header']

# Iteration preserves original case
for key, value in headers.items():
    print(f"{key}: {value}")

Request Modification

# Modifying request components
request = Request('GET', 'https://api.example.com/data', None, {})

# Update headers (creates new HeadersDict if needed)
request.headers['Authorization'] = 'Bearer new-token'
request.headers['Accept'] = 'application/xml'

# Body modification
request.body = '{"updated": "data"}'

# Note: URI components are read-only properties
# To change URI, create a new Request object

Integration with VCR Filters

def sanitize_request(request):
    """Example filter function that modifies requests before recording"""
    # Remove sensitive headers
    if 'authorization' in request.headers:
        request.headers['authorization'] = 'REDACTED'
    
    # Modify query parameters by reconstructing URI
    if 'api_key' in request.uri:
        # Custom logic to remove api_key from URI
        pass
    
    return request

# Use with VCR configuration
my_vcr = vcr.VCR(before_record_request=sanitize_request)

Advanced Usage

Custom Header Processing

from vcr.request import HeadersDict

def normalize_headers(headers_dict):
    """Normalize header values for consistent recording"""
    normalized = HeadersDict()
    
    for key, value in headers_dict.items():
        if key.lower() == 'user-agent':
            # Normalize user agent strings
            normalized[key] = 'Normalized-User-Agent/1.0'
        elif key.lower() == 'date':
            # Remove timestamp headers for deterministic recording
            continue
        else:
            normalized[key] = value
    
    return normalized

# Apply to requests during processing
request.headers = normalize_headers(request.headers)

Body Content Analysis

import json
from urllib.parse import parse_qs

def analyze_request_body(request):
    """Analyze and potentially modify request body"""
    if request.headers.get('content-type', '').startswith('application/json'):
        try:
            # Parse and potentially modify JSON body
            data = json.loads(request.body)
            # Remove sensitive fields
            data.pop('password', None)
            data.pop('api_secret', None)
            request.body = json.dumps(data)
        except (json.JSONDecodeError, TypeError):
            pass
    
    elif request.headers.get('content-type') == 'application/x-www-form-urlencoded':
        # Handle form data
        try:
            data = parse_qs(request.body)
            # Process form fields
            if 'password' in data:
                data['password'] = ['REDACTED']
            request.body = '&'.join(f"{k}={v[0]}" for k, v in data.items())
        except (ValueError, TypeError):
            pass
    
    return request

Install with Tessl CLI

npx tessl i tessl/pypi-vcrpy

docs

core-configuration.md

data-filtering.md

error-handling.md

index.md

record-modes.md

request-matching.md

request-response.md

serialization.md

test-integration.md

tile.json