Skip to main content
The core_utils package provides the foundational utilities that power tif1’s high-performance data processing pipeline. While these are primarily internal APIs, they expose powerful capabilities for advanced users who need fine-grained control over data transformations, backend conversions, and performance optimization.This module is the performance engine of tif1, implementing zero-copy operations, optimized JSON parsing, and intelligent DataFrame transformations that enable the library to process millions of rows of telemetry data with minimal overhead.

Overview

The core utilities package (tif1.core_utils) is organized into five specialized modules, each addressing a critical aspect of the library’s performance and functionality:
  1. Backend Conversion (backend_conversion.py) - Zero-copy DataFrame conversion between pandas and polars using Apache Arrow
  2. JSON Utilities (json_utils.py) - High-performance JSON parsing with orjson, providing 2-3x faster deserialization
  3. Helper Functions (helpers.py) - DataFrame manipulation, validation, and data transformation utilities
  4. Constants (constants.py) - Column name mappings, rename dictionaries, and configuration constants
  5. Resource Manager (resource_manager.py) - Context manager for guaranteed resource cleanup with LIFO ordering
These utilities enable tif1 to achieve its performance goals while maintaining compatibility with both pandas and polars backends. The design philosophy emphasizes:
  • Zero-copy operations wherever possible to minimize memory overhead
  • Lazy evaluation and deferred imports to reduce startup time
  • Type safety with comprehensive validation and error handling
  • Performance-first design with optimized hot paths
  • Backend agnostic APIs that work seamlessly with pandas or polars

Module Import Structure

Performance Philosophy

The core_utils module is designed with performance as the primary concern. Every function is optimized for:
  • Minimal memory allocations: Reuse buffers, avoid unnecessary copies
  • Cache-friendly access patterns: Sequential reads, predictable branches
  • SIMD-friendly operations: Leverage NumPy/Arrow vectorization
  • Lazy computation: Defer work until absolutely necessary
  • Zero-copy semantics: Share memory between backends via Arrow
Real-world impact: These optimizations enable tif1 to load and process a full race weekend (3 practice sessions, qualifying, race) with telemetry for all 20 drivers in under 5 seconds on modern hardware.

Backend Conversion

The backend conversion module provides zero-copy DataFrame transformations between pandas and polars using Apache Arrow as the interchange format. This enables seamless switching between backends without the performance penalty of traditional serialization/deserialization.

Architecture

The conversion system leverages:
  • Apache Arrow as the zero-copy interchange format
  • PyArrow extension arrays for pandas to maintain Arrow memory layout
  • Lazy polars imports to avoid dependency requirements when not needed
  • Automatic fallback to standard conversion if zero-copy fails

pandas_to_polars

Convert a pandas DataFrame to polars using zero-copy Arrow conversion. This function uses pl.from_pandas() with Arrow as the interchange format, avoiding memory copies when possible. Parameters:
  • df (pd.DataFrame): pandas DataFrame to convert. Can contain any pandas-supported dtype including nullable types, categoricals, and datetime types.
  • rechunk (bool, optional): Whether to rechunk the resulting polars DataFrame for optimal memory layout. Default is False to preserve zero-copy semantics. Set to True if you plan to perform many operations on the polars DataFrame and want contiguous memory.
Returns:
  • pl.DataFrame: polars DataFrame with equivalent data and schema
Raises:
  • ImportError: If polars is not installed in the environment
  • ValueError: If the conversion fails due to incompatible data types or corrupted data
Performance Characteristics:
  • Time complexity: O(1) for zero-copy conversion (when rechunk=False), O(n) when rechunking
  • Memory overhead: Minimal (shares memory with source DataFrame via Arrow)
  • Typical performance: ~50ms for 1M rows with zero-copy
Example - Basic Conversion:
Example - With Rechunking:
Example - Handling Large DataFrames:
Use rechunk=False (default) when you need to convert data quickly and will only perform a few operations. Use rechunk=True when you plan to perform many polars operations and want optimal performance.

polars_to_pandas

Convert a polars DataFrame to pandas using zero-copy Arrow conversion. This function uses df.to_pandas() with PyArrow extension arrays to maintain Arrow memory layout in pandas, enabling zero-copy semantics. Parameters:
  • df (pl.DataFrame): polars DataFrame to convert. Supports all polars data types including nested types (List, Struct), categoricals, and temporal types.
  • use_pyarrow (bool, optional): Whether to use PyArrow extension arrays in the resulting pandas DataFrame. Default is True for zero-copy conversion. Set to False to convert to native pandas dtypes (slower but more compatible with legacy pandas code).
Returns:
  • pd.DataFrame: pandas DataFrame with equivalent data and schema
Raises:
  • ImportError: If polars is not installed in the environment
  • ValueError: If the conversion fails due to incompatible data types
Performance Characteristics:
  • Time complexity: O(1) for zero-copy (when use_pyarrow=True), O(n) for native pandas dtypes
  • Memory overhead: Minimal with PyArrow arrays, 2x memory usage with native dtypes
  • Typical performance: ~100ms for 1M rows with PyArrow
Example - Basic Conversion:
Example - Native Pandas Dtypes:
Example - Handling Nested Types:
When use_pyarrow=True, the resulting pandas DataFrame uses PyArrow extension arrays. Some legacy pandas operations may not support these arrays. If you encounter compatibility issues, set use_pyarrow=False to use native pandas dtypes.

convert_backend

Intelligently convert a DataFrame to the target backend (pandas or polars) using zero-copy conversion when possible. This is the high-level API that automatically detects the source backend and performs the appropriate conversion. Parameters:
  • df (DataFrame): DataFrame to convert. Can be either pd.DataFrame or pl.DataFrame.
  • target_backend (str): Target backend name. Must be either "pandas" or "polars" (case-sensitive).
Returns:
  • DataFrame: DataFrame in the target backend format. If already in the target backend, returns the input DataFrame unchanged (no-op).
Raises:
  • ValueError: If target_backend is not “pandas” or “polars”, or if the conversion fails
  • ImportError: If polars is not installed and target is “polars”
Performance Characteristics:
  • No-op detection: O(1) type check to avoid unnecessary conversions
  • Conversion time: Same as pandas_to_polars or polars_to_pandas
  • Memory efficient: Uses zero-copy conversion internally
Example - Basic Usage:
Example - Integration with tif1:
Example - Error Handling:
Use convert_backend as the primary API for backend conversion. It handles edge cases, performs no-op detection, and provides consistent error messages.

JSON Utilities

The JSON utilities module provides high-performance JSON parsing and serialization using orjson, a fast, correct JSON library for Python written in Rust. This module is critical for tif1’s performance as it handles parsing of large JSON payloads from the CDN containing lap data, telemetry, and race control messages.

Why orjson?

orjson provides significant advantages over Python’s standard library json module:
  • 2-3x faster parsing: Rust-based implementation with SIMD optimizations
  • Lower memory usage: Efficient memory allocation and reuse
  • Native bytes support: Parse directly from HTTP response bodies without decoding
  • NumPy integration: Automatic handling of NumPy types during serialization
  • Strict correctness: Validates JSON spec compliance
Benchmark comparison (parsing 10MB JSON payload):

Architecture

The JSON utilities module implements a fallback strategy:
  1. Primary: Use orjson for maximum performance
  2. Fallback: Use stdlib json if orjson fails (rare edge cases)
  3. Automatic: No user configuration required
This ensures reliability while maintaining performance for the common case.

json_loads

Deserialize JSON payload to Python object using accelerated orjson codec with automatic fallback to stdlib json. Parameters:
  • payload (str | bytes | bytearray | memoryview): JSON data to parse. Accepts multiple input types for flexibility:
    • str: Standard JSON string (UTF-8 encoded)
    • bytes: Raw bytes from HTTP responses (most efficient)
    • bytearray: Mutable byte array
    • memoryview: Zero-copy view of bytes (converted to bytes internally)
Returns:
  • Any: Parsed Python object. Common return types:
    • dict: JSON objects {}
    • list: JSON arrays []
    • str, int, float, bool, None: JSON primitives
Raises:
  • json.JSONDecodeError: If the payload is not valid JSON (from fallback parser)
  • ValueError: If the payload is malformed (from orjson)
Performance Characteristics:
  • Time complexity: O(n) where n is the payload size
  • Memory overhead: Minimal (orjson uses efficient allocation)
  • Typical performance:
    • Small payloads (<1KB): ~10μs
    • Medium payloads (100KB): ~2ms
    • Large payloads (10MB): ~180ms
Example - Basic Usage:
Example - Parsing CDN Response:
Example - Handling Complex Nested Data:
Example - Performance Comparison:
When fetching JSON from HTTP endpoints, pass response.content (bytes) directly to json_loads instead of response.text (str). This avoids an unnecessary UTF-8 decode step and is faster.

json_dumps

Serialize Python object to JSON string using accelerated orjson codec with automatic fallback to stdlib json. Parameters:
  • data (Any): Python object to serialize. Supported types:
    • dict, list: Collections
    • str, int, float, bool, None: Primitives
    • datetime, date, time: Temporal types (ISO 8601 format)
    • UUID: Converted to string
    • numpy types: Automatically converted to Python equivalents
    • dataclasses, pydantic models: Serialized to dict
Returns:
  • str: JSON string representation of the data
Raises:
  • TypeError: If the data contains non-serializable types (e.g., custom classes without __dict__)
Performance Characteristics:
  • Time complexity: O(n) where n is the data size
  • Memory overhead: Minimal (efficient string building)
  • Typical performance:
    • Small objects (<1KB): ~5μs
    • Medium objects (100KB): ~1ms
    • Large objects (10MB): ~100ms
Example - Basic Usage:
Example - Serializing NumPy Types:
Example - Serializing Datetime Objects:
Example - Round-trip Serialization:
json_dumps uses orjson for 2-3x faster serialization than stdlib json. It automatically handles NumPy types, datetime objects, and other common Python types without requiring custom encoders.

parse_response_json

Decode an HTTP response body to Python object, preferring raw-byte parsing when available for maximum performance. This function is optimized for parsing JSON from HTTP responses (niquests, requests, httpx, etc.). Parameters:
  • response (Any): HTTP response object. Should have either:
    • .content attribute (bytes): Preferred for performance
    • .json() method: Fallback for compatibility
Returns:
  • Any: Parsed Python object from the response JSON body
Raises:
  • json.JSONDecodeError: If the response body is not valid JSON
  • AttributeError: If the response object has neither .content nor .json()
Performance Characteristics:
  • Optimized path: Parse from .content bytes using orjson (~180ms for 10MB)
  • Fallback path: Use .json() method (~450ms for 10MB)
  • Speedup: 2-3x faster than calling .json() directly
Example - Parsing niquests Response:
Example - Comparing Performance:
Example - Integration with tif1 HTTP Session:
Always use parse_response_json() instead of response.json() when working with HTTP responses in performance-critical code. The 2-3x speedup adds up quickly when fetching data for multiple sessions or drivers.

JSON Utilities Best Practices

  1. Use bytes when possible: Pass response.content to json_loads instead of response.text
  2. Prefer parse_response_json: Use parse_response_json() for HTTP responses
  3. Trust the fallback: The automatic fallback to stdlib json ensures reliability
  4. Benchmark your use case: Profile your specific JSON payloads to measure impact
  5. Handle errors gracefully: Catch json.JSONDecodeError for malformed JSON
Common Pitfalls:
  • json.loads(response.text) - Slow (decode + parse)
  • response.json() - Slower (uses stdlib json)
  • json_loads(response.content) - Fast (direct bytes parse)
  • parse_response_json(response) - Fastest (optimized path)

Helper Functions

The helpers module (helpers.py) provides a comprehensive suite of utility functions for DataFrame manipulation, validation, and data transformation. These functions are the workhorses of tif1’s data processing pipeline, handling everything from input validation to complex DataFrame operations across both pandas and polars backends.

Design Philosophy

The helper functions follow these core principles:
  1. Backend Agnostic: All functions work seamlessly with both pandas and polars DataFrames
  2. Zero-Copy Optimization: Minimize memory allocations and avoid unnecessary data copies
  3. Type Safety: Comprehensive validation with clear error messages
  4. Performance First: Optimized hot paths for common operations
  5. Defensive Programming: Handle edge cases gracefully with fallback strategies

Validation Functions

Input validation is critical for data integrity and user experience. The validation helpers provide comprehensive checks with informative error messages.

_validate_year

Validate that a year is within the supported range for F1 data. This function is called before any data fetching operations to ensure the requested year has available data. Parameters:
  • year (int): Year to validate. Must be an integer representing a calendar year.
  • min_year (int): Minimum supported year (inclusive). Typically 2018 for tif1.
  • max_year (int): Maximum supported year (inclusive). Typically the current year + 1 for future scheduled races.
Raises:
  • ValueError: If year is outside the range [min_year, max_year]. Error message includes the valid range and the invalid value provided.
Performance:
  • Time complexity: O(1) - simple integer comparison
  • Typical execution: <1μs
Example - Basic Validation:
Example - Integration with Session Loading:
This validation is performed early in the data loading pipeline to fail fast and provide clear feedback before any network requests are made.

_validate_drivers_list

Validate that a drivers list parameter is well-formed and contains valid driver codes. This function ensures that driver filtering operations receive properly formatted input. Parameters:
  • drivers (list[str] | None): List of driver codes to validate. Each code should be a non-empty string (typically 3-letter abbreviations like “VER”, “HAM”, “LEC”). Can be None to indicate no filtering.
Raises:
  • TypeError: If drivers is not a list or None. Error message includes the actual type received.
  • ValueError: If the list is empty, or if any element is not a non-empty string. Error messages are specific to the validation failure.
Validation Rules:
  1. Must be a list type (not tuple, set, or other iterable)
  2. Cannot be an empty list (use None instead to indicate “all drivers”)
  3. All elements must be strings
  4. All strings must be non-empty (no empty strings or whitespace-only strings)
Performance:
  • Time complexity: O(n) where n is the number of drivers
  • Typical execution: <10μs for 20 drivers
Example - Valid Driver Lists:
Example - Invalid Driver Lists:
Example - Integration with Data Filtering:
Use None instead of an empty list when you want to indicate “all drivers”. An empty list is considered an error because it would result in an empty DataFrame, which is likely unintentional.

_validate_lap_number

Validate that a lap number is a positive integer. Lap numbers in F1 start from 1 (not 0), so this function ensures the value is valid for lap-based operations. Parameters:
  • lap_number (int): Lap number to validate. Must be a positive integer (>= 1).
Raises:
  • TypeError: If lap_number is not an integer. Error message includes the actual type received.
  • ValueError: If lap_number is zero or negative. Error message includes the invalid value.
Validation Rules:
  1. Must be an integer type (not float, string, or other numeric type)
  2. Must be positive (>= 1)
Performance:
  • Time complexity: O(1) - simple type check and comparison
  • Typical execution: <1μs
Example - Valid Lap Numbers:
Example - Invalid Lap Numbers:
Example - Integration with Telemetry Loading:
Lap numbers in F1 start from 1, not 0. If you’re iterating over laps, use range(1, num_laps + 1) instead of range(num_laps).

_validate_string_param

Validate that a string parameter is non-empty and properly formatted. This generic validation function is used throughout the library for string inputs like GP names, session types, driver codes, etc. Parameters:
  • param (str): String parameter to validate. Must be a non-empty string with at least one non-whitespace character.
  • param_name (str): Human-readable parameter name for error messages. Used to provide context in error messages (e.g., “gp”, “session_type”, “driver”).
Raises:
  • TypeError: If param is not a string. Error message includes the parameter name and actual type received.
  • ValueError: If param is empty or contains only whitespace. Error message includes the parameter name.
Validation Rules:
  1. Must be a string type (not int, None, or other type)
  2. Cannot be empty string ""
  3. Cannot be whitespace-only (e.g., " ", "\t", "\n")
Performance:
  • Time complexity: O(n) where n is the string length (for whitespace check)
  • Typical execution: <5μs for typical parameter lengths
Example - Valid String Parameters:
Example - Invalid String Parameters:
Example - Integration with API Functions:
This function uses .strip() to check for whitespace-only strings, ensuring that parameters like " " are rejected even though they have non-zero length.

URL Encoding

_encode_url_component

URL-encode a string component for safe use in CDN URLs. This function properly encodes special characters, spaces, and international characters according to RFC 3986, ensuring that URLs are valid and don’t break when passed to HTTP clients. Parameters:
  • component (str): String to encode. Can contain any Unicode characters, spaces, or special characters.
Returns:
  • str: URL-encoded string with all special characters percent-encoded (e.g., space becomes %20, & becomes %26).
Caching:
  • Uses @lru_cache(maxsize=1024) for memoization
  • Repeated calls with the same input return cached results instantly
  • Cache size of 1024 is sufficient for typical usage (GP names, session types, etc.)
Performance:
  • Time complexity: O(n) for first call, O(1) for cached calls
  • Typical execution:
    • First call: ~10μs
    • Cached call: <1μs (cache lookup)
Example - Basic URL Encoding:
Example - Building CDN URLs:
Example - Cache Performance:
Example - Handling International Characters:
The LRU cache makes repeated URL encoding operations essentially free. Since GP names and session types are reused frequently, the cache hit rate is typically >95% in real-world usage.

DataFrame Utility Functions

These functions provide backend-agnostic operations for DataFrame manipulation, enabling seamless work with both pandas and polars.

_is_empty_df

Check if a DataFrame-like object is empty, working across both pandas and polars backends. This function handles the different APIs for checking emptiness and provides a unified interface. Parameters:
  • df: DataFrame-like object to check. Can be pd.DataFrame, pl.DataFrame, or any object with .empty or .is_empty() attributes.
  • lib (str): Backend library name ("pandas" or "polars"). Used as a hint for optimization, but the function also performs runtime type checking.
Returns:
  • bool: True if the DataFrame is empty (zero rows), False otherwise.
Detection Strategy: The function uses a multi-layered approach to handle various DataFrame types:
  1. Type-based detection: Check isinstance(df, pd.DataFrame) or isinstance(df, pl.DataFrame)
  2. Attribute-based detection: Check for .empty (pandas) or .is_empty() (polars)
  3. Fallback: Use len(df) == 0 as last resort
Performance:
  • Time complexity: O(1) - all checks are constant time
  • Typical execution: <1μs
Example - Basic Usage:
Example - Integration with Data Loading:
Example - Conditional Processing:
This function prefers concrete type checking over the lib parameter because some code paths can surface pandas DataFrames even when the configured backend is polars (e.g., during backend conversion).

_create_empty_df

Create an empty DataFrame for the specified backend. This function provides a consistent way to create empty DataFrames across backends, useful for fallback scenarios and initialization. Parameters:
  • lib (str): Backend library name. Must be either "pandas" or "polars".
Returns:
  • pd.DataFrame if lib == "pandas"
  • pl.DataFrame if lib == "polars" and polars is available
  • pd.DataFrame as fallback if polars is requested but not installed
Performance:
  • Time complexity: O(1) - creates empty structure
  • Typical execution: <10μs
Example - Basic Usage:
Example - Fallback Pattern:
Example - Initialization:
Use _create_empty_df() instead of pd.DataFrame() or pl.DataFrame() directly when you need backend-agnostic code. This ensures consistency and handles the polars availability check automatically.

_filter_valid_laptimes

Filter laps DataFrame to include only rows with valid lap times, and add a numeric LapTimeSeconds column for analysis. This function is critical for data quality, removing invalid laps (pit laps, out laps, deleted laps) and providing a consistent numeric representation of lap times. Parameters:
  • laps: Laps DataFrame (pandas or polars). Must contain a LapTime column.
  • lib (str): Backend library name ("pandas" or "polars").
Returns:
  • Filtered DataFrame with:
    • Only rows where LapTime is valid (not null/NaN)
    • New LapTimeSeconds column containing lap time as float (seconds)
    • For pandas: LapTime converted to timedelta64[ns] dtype
    • For polars: LapTime kept as original type, LapTimeSeconds added as Float64
Behavior:
  • Pandas: Converts LapTime to timedelta64[ns] and creates LapTimeSeconds as float
  • Polars: Casts LapTime to Float64 (non-strict) and aliases as LapTimeSeconds
  • Optimization: Minimizes copies by filtering before copying (pandas) or using lazy operations (polars)
Performance:
  • Time complexity: O(n) where n is the number of laps
  • Memory overhead: Minimal (single column addition)
  • Typical execution: ~5ms for 1000 laps
Example - Basic Usage:
Example - Polars Backend:
Example - Handling Mixed Valid/Invalid Data:
Example - Performance Optimization:
This function is called automatically during session loading. The LapTimeSeconds column is essential for numerical analysis, sorting, and filtering operations that require numeric comparison.

_rename_columns

Rename DataFrame columns based on a mapping dictionary, with intelligent handling of duplicates and backend-specific APIs. This function is used to transform CDN column names to user-facing FastF1-compatible names. Parameters:
  • df: DataFrame to rename (pandas or polars)
  • rename_map (dict): Mapping of old column names to new names. Use None as the value to drop a column.
  • lib (str): Backend library name ("pandas" or "polars")
Returns:
  • DataFrame with renamed columns. Columns mapped to None are dropped.
Special Handling:
  • Duplicate prevention: Skips renames that would create duplicate column names
  • Drop columns: Columns mapped to None are removed from the DataFrame
  • No-op renames: Skips renames where source == target (e.g., {"Driver": "Driver"})
  • Existing columns: Avoids conflicts when target name already exists independently
Performance:
  • Time complexity: O(n) where n is the number of columns
  • Memory overhead: Minimal (column metadata only, no data copy)
  • Typical execution: <1ms for typical DataFrames
Example - Basic Column Renaming:
Example - Dropping Columns:
Example - Handling Duplicate Conflicts:
Example - Polars Backend:
Example - Integration with CDN Data Processing:
The function automatically handles edge cases like duplicate column names and missing columns, making it safe to use with varying CDN data formats across different years and sessions.

_apply_categorical

Apply categorical dtype to specified columns for memory optimization and faster operations. Categorical dtypes reduce memory usage by storing repeated string values as integer codes with a lookup table. Parameters:
  • df: DataFrame to modify (pandas or polars)
  • cols (list): List of column names to convert to categorical
  • lib (str): Backend library name ("pandas" or "polars")
Returns:
  • DataFrame with specified columns converted to categorical dtype
Benefits of Categorical Dtype:
  • Memory savings: 50-90% reduction for columns with low cardinality
  • Faster operations: Comparisons and groupby operations are faster
  • Preserved semantics: String operations still work as expected
Performance:
  • Time complexity: O(n × m) where n is rows and m is number of categorical columns
  • Memory savings: Typically 70-80% for Driver, Team, Compound columns
  • Typical execution: ~10ms for 1000 laps with 4 categorical columns
Example - Basic Usage:
Example - Memory Savings Analysis:
Example - Polars Backend:
Example - Performance Impact:
The library automatically applies categorical dtype to Driver, Team, Compound, and TrackStatus columns during session loading. This optimization is transparent to users but provides significant memory and performance benefits.

Constants

Column name mappings and constants used throughout the library.

Column rename maps

The constants module defines mappings for renaming columns from CDN format to user-facing format:

Standard column order


Constants

The constants module (constants.py) defines all column name mappings, configuration values, and standard column orders used throughout the library. These constants ensure consistency across the codebase and provide FastF1 compatibility.

Year Range Constants

These constants define the valid range for F1 season data. The library supports data from 2018 onwards (when the TracingInsights data collection began) through 2100 (allowing for future scheduled races). Example Usage:

Cache Configuration

Defines the maximum number of items to store in the LRU cache for various operations. This prevents unbounded memory growth while maintaining good cache hit rates for typical usage patterns.

Column Rename Maps

The library defines comprehensive column rename mappings to transform CDN data format to user-facing FastF1-compatible format. These mappings handle both verbose and abbreviated column names from different CDN data versions.

Lap Data Rename Map

Example Usage:

Telemetry Rename Map

Example Usage:

Race Control Messages Rename Map


Weather Data Rename Map


Categorical Columns

List of columns that should be converted to categorical dtype for memory optimization. These columns have low cardinality (few unique values) and benefit significantly from categorical encoding. Memory Savings:
  • Driver: ~70% reduction (20 unique values in ~1000 laps)
  • Team: ~75% reduction (10 unique values)
  • Compound: ~80% reduction (3-5 unique values)
  • TrackStatus: ~85% reduction (2-4 unique values)

Standard Column Names

These constants provide a single source of truth for column names, preventing typos and making refactoring easier. Example Usage:

FastF1 Column Order

This list defines the standard column order for laps DataFrames, ensuring FastF1 compatibility. Columns are ordered logically:
  1. Identification columns (index, driver, time)
  2. Core lap data (lap time, number, stint)
  3. Detailed timing (sectors, speed traps)
  4. Metadata (tyres, position, flags)
  5. Weather data
  6. tif1-specific additions
Example Usage:

Resource Manager

The resource manager module (resource_manager.py) provides a robust pattern for managing resources with guaranteed cleanup, even when initialization fails partway through. This is critical for preventing resource leaks in error scenarios.

Architecture

The ResourceManager class implements the context manager protocol and tracks resources in a LIFO (Last-In-First-Out) stack. When cleanup occurs, resources are cleaned up in reverse order of creation, ensuring that dependencies are respected. Key Features:
  • LIFO cleanup order: Resources cleaned up in reverse order of registration
  • Error resilience: Cleanup continues even if individual resources fail to close
  • Comprehensive logging: All cleanup operations are logged for debugging
  • Multiple cleanup methods: Supports both .close() and .shutdown() methods

ResourceManager


Methods

_register_resource

Register a resource for cleanup tracking. Resources are cleaned up in reverse order of registration (LIFO), ensuring dependencies are respected during cleanup. Parameters:
  • name (str): Human-readable name for the resource (used in logging). Should be descriptive for debugging purposes.
  • resource (Any): The resource object to track. Should have a .close() or .shutdown() method for cleanup.
Example:

_cleanup_resources

Cleanup all registered resources in reverse order (LIFO). This method attempts to clean up all resources even if individual cleanup operations fail. Cleanup errors are logged but do not prevent other resources from being cleaned up. Cleanup Strategy:
  1. Iterate through resources in reverse order
  2. For each resource, try .close() method first
  3. If no .close(), try .shutdown(wait=True) method
  4. If neither exists, skip the resource
  5. Log any errors but continue cleanup
  6. Clear the resource list after all cleanup attempts
Example:

Usage Patterns

Basic Context Manager Usage


Error Handling During Initialization


Manual Cleanup


Logging

The ResourceManager provides comprehensive logging for debugging:

Error Resilience

If cleanup fails for one resource, other resources are still cleaned up:
ResourceManager is used internally by the library for managing HTTP sessions, cache connections, and other resources. Most users don’t need to interact with it directly, but it’s available for advanced use cases requiring custom resource management.

Performance Considerations

JSON Parsing

The library uses orjson for JSON parsing, which provides:
  • 2-3x faster parsing than stdlib json
  • Lower memory usage
  • Native support for bytes input
  • Automatic handling of numpy types

Lib Conversion

When converting between backends:
  • pandas → polars: Uses PyArrow for zero-copy when possible
  • polars → pandas: Uses PyArrow by default for efficiency
  • Rechunking: Optional for polars to optimize memory layout
Benchmark results:

Advanced Usage

Custom lib conversion

Custom JSON Processing


Best Practices

  1. Use orjson for JSON: Always use json_loads/json_dumps for performance
  2. Prefer PyArrow conversion: Keep use_pyarrow=True for lib conversion
  3. Validate early: Use validation helpers to catch errors early
  4. Let the library handle resources: ResourceManager is automatic
  5. Use constants for column names: Reference standard column names from constants

Summary

The core_utils package provides:
  • High-performance JSON parsing with orjson
  • Efficient lib conversion (pandas ↔ polars)
  • Data validation utilities
  • Column name standardization
  • Resource management
  • Internal helpers for DataFrame operations
These utilities enable the library’s focus on performance and reliability.
Last modified on May 8, 2026