Skip to main content
tif1 uses Python’s typing system extensively for better IDE support and type safety. All public APIs include type hints.

Type Aliases

BackendType

Specifies which DataFrame lib to use.
BackendType = Literal["pandas", "polars"]
```python

**Usage:**
```python
import tif1
from tif1.types import BackendType

lib: BackendType = "pandas"
session = tif1.get_session(2021, "Belgian Grand Prix", "Race", lib=lib)

SessionType

Valid session names.
SessionType = Literal[
    "Practice 1",
    "Practice 2",
    "Practice 3",
    "Qualifying",
    "Sprint",
    "Sprint Qualifying",
    "Sprint Shootout",
    "Race"
]
Usage:
from tif1.types import SessionType

session_name: SessionType = "Qualifying"
session = tif1.get_session(2021, "Belgian Grand Prix", session_name)

CompoundType

Tire compound names.
CompoundType = Literal[
    "SOFT",
    "MEDIUM",
    "HARD",
    "INTERMEDIATE",
    "WET",
    "UNKNOWN",
    "TEST_UNKNOWN"
]
Usage:
from tif1.types import CompoundType

compound: CompoundType = "SOFT"
soft_laps = laps[laps["Compound"] == compound]

TrackStatusType

Track status codes during a session.
TrackStatusType = Literal[
    "1",  # Green Flag
    "2",  # Yellow Flag
    "4",  # Safety Car
    "5",  # Red Flag
    "6",  # VSC (Virtual Safety Car)
    "7"   # VSC Ending
]
```python

**Usage:**
```python
from tif1.types import TrackStatusType

# Filter laps under green flag
green_flag: TrackStatusType = "1"
clean_laps = laps[laps["TrackStatus"] == green_flag]

TypedDict Schemas

These define the structure of DataFrame row data. All fields except core identity fields (Driver, DriverNumber, Team, Time) are optional and may be None.

LapDataDict

Structure of lap timing data in DataFrames. Uses NotRequired for optional fields.
class LapDataDict(TypedDict):
    # Core identity (required)
    Driver: str
    DriverNumber: str
    Team: str
    Time: datetime.timedelta  # Session time from start

    # Lap timing (all timedelta, optional)
    LapTime: NotRequired[datetime.timedelta | None]
    LapNumber: NotRequired[float | None]
    Stint: NotRequired[float | None]
    PitOutTime: NotRequired[datetime.timedelta | None]
    PitInTime: NotRequired[datetime.timedelta | None]
    Sector1Time: NotRequired[datetime.timedelta | None]
    Sector2Time: NotRequired[datetime.timedelta | None]
    Sector3Time: NotRequired[datetime.timedelta | None]
    Sector1SessionTime: NotRequired[datetime.timedelta | None]
    Sector2SessionTime: NotRequired[datetime.timedelta | None]
    Sector3SessionTime: NotRequired[datetime.timedelta | None]
    LapStartTime: NotRequired[datetime.timedelta | None]
    LapStartDate: NotRequired[datetime.datetime | None]

    # Speed traps (km/h, optional)
    SpeedI1: NotRequired[float | None]
    SpeedI2: NotRequired[float | None]
    SpeedFL: NotRequired[float | None]
    SpeedST: NotRequired[float | None]

    # Tire info (optional)
    Compound: NotRequired[str | None]
    TyreLife: NotRequired[float | None]
    FreshTyre: NotRequired[bool]

    # Session/lap metadata (optional)
    TrackStatus: NotRequired[str | None]
    Position: NotRequired[float | None]
    IsPersonalBest: NotRequired[bool]
    Deleted: NotRequired[bool | None]
    DeletedReason: NotRequired[str | None]
    FastF1Generated: NotRequired[bool]
    IsAccurate: NotRequired[bool]

    # Derived fields (optional)
    LapTimeSeconds: NotRequired[float | None]

    # Per-lap weather data (optional)
    WeatherTime: NotRequired[datetime.timedelta | None]
    AirTemp: NotRequired[float | None]
    Humidity: NotRequired[float | None]
    Pressure: NotRequired[float | None]
    Rainfall: NotRequired[bool]
    TrackTemp: NotRequired[float | None]
    WindDirection: NotRequired[int | None]
    WindSpeed: NotRequired[float | None]
Usage:
import tif1

session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
laps = session.laps

# Access lap data
for _, lap in laps.iterrows():
    driver: str = lap["Driver"]
    lap_time = lap.get("LapTime")  # May be None
    if lap_time:
        print(f"{driver}: {lap_time}")

TelemetryDataDict

Structure of high-frequency telemetry data. All fields except Time are optional.
class TelemetryDataDict(TypedDict):
    Time: float                                    # Time from lap start (seconds)
    RPM: NotRequired[int | None]                   # Engine RPM
    Speed: NotRequired[float | None]               # Speed (km/h)
    nGear: NotRequired[int | None]                 # Gear number (1-8)
    Throttle: NotRequired[float | None]            # Throttle position (0-100%)
    Brake: NotRequired[int | None]                 # Brake status (0=off, 1=on)
    DRS: NotRequired[int | None]                   # DRS status (0=off, 10+=on)
    Distance: NotRequired[float | None]            # Distance from lap start (meters)
    RelativeDistance: NotRequired[float | None]    # Relative distance
    DriverAhead: NotRequired[str | None]           # Driver code ahead
    DistanceToDriverAhead: NotRequired[float | None]  # Gap to driver ahead
    X: NotRequired[float | None]                   # X coordinate (meters)
    Y: NotRequired[float | None]                   # Y coordinate (meters)
    Z: NotRequired[float | None]                   # Z coordinate (meters)
    AccelerationX: NotRequired[float | None]       # X acceleration
    AccelerationY: NotRequired[float | None]       # Y acceleration
    AccelerationZ: NotRequired[float | None]       # Z acceleration
    DataKey: NotRequired[str | None]               # Data key identifier
Usage:
import tif1

session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
lap = session.laps.iloc[0]
tel = lap.telemetry

# Access telemetry data
for _, sample in tel.iterrows():
    time: float = sample["Time"]
    speed = sample.get("Speed")  # May be None
    if speed:
        print(f"Time: {time:.2f}s, Speed: {speed:.1f} km/h")

WeatherDataDict

Structure of weather data returned by session.weather.
class WeatherDataDict(TypedDict):
    Time: datetime.timedelta                       # Session time from start
    AirTemp: NotRequired[float | None]             # Air temperature (°C)
    Humidity: NotRequired[float | None]            # Relative humidity (%)
    Pressure: NotRequired[float | None]            # Atmospheric pressure (mbar)
    Rainfall: NotRequired[bool | None]             # Rainfall indicator
    TrackTemp: NotRequired[float | None]           # Track temperature (°C)
    WindDirection: NotRequired[int | None]         # Wind direction (degrees)
    WindSpeed: NotRequired[float | None]           # Wind speed (km/h)
Usage:
import tif1

session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
weather = session.weather

# Access weather data
for _, sample in weather.iterrows():
    time = sample["Time"]
    air_temp = sample.get("AirTemp")
    rainfall = sample.get("Rainfall", False)
    print(f"Time: {time}, Temp: {air_temp}°C, Rain: {rainfall}")

RaceControlDataDict

Structure of race control messages.
class RaceControlDataDict(TypedDict):
    Time: float                                    # Session time (seconds)
    Category: NotRequired[str | None]              # Message category
    Message: NotRequired[str | None]               # Message text
    Status: NotRequired[str | None]                # Track status
    Flag: NotRequired[str | None]                  # Flag type
    Scope: NotRequired[str | None]                 # Message scope
    Sector: NotRequired[int | str | None]          # Affected sector
    RacingNumber: NotRequired[str | None]          # Affected driver number
    Lap: NotRequired[int | None]                   # Lap number
Usage:
import tif1

session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
messages = session.race_control_messages

# Access race control messages
for _, msg in messages.iterrows():
    time = msg["Time"]
    category = msg.get("Category", "")
    message = msg.get("Message", "")
    print(f"{time}: [{category}] {message}")

DriverInfoDict

Structure of driver information.
class DriverInfoDict(TypedDict):
    driver: str                    # 3-letter code (e.g., "VER")
    team: str                      # Team name
    dn: str                        # Driver number (car number)
    fn: str                        # First name
    ln: str                        # Last name
    tc: str                        # Team color (hex code)
    url: str                       # Headshot URL
Usage:
import tif1

session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
drivers_df = session.drivers_df

# Access driver information
for _, driver in drivers_df.iterrows():
    code = driver["Driver"]
    name = f"{driver['FirstName']} {driver['LastName']}"
    team = driver["Team"]
    print(f"{code}: {name} ({team})")

DataFrame type hints

For better IDE support, use type hints with DataFrames:

Pandas DataFrames

import pandas as pd
import tif1

session = tif1.get_session(2021, "Belgian Grand Prix", "Race")

# Type hint for laps DataFrame
laps: pd.DataFrame = session.laps

# Type hint for telemetry
tel: pd.DataFrame = session.get_fastest_lap_tel()

# Type hint for weather
weather: pd.DataFrame = session.weather

Polars DataFrames

import polars as pl
import tif1

config = tif1.get_config()
config.set("lib", "polars")

session = tif1.get_session(2021, "Belgian Grand Prix", "Race")

# Type hint for laps DataFrame
laps: pl.DataFrame = session.laps

# Type hint for telemetry
tel: pl.DataFrame = session.get_fastest_lap_tel()

Union types for lib-agnostic code

from typing import Union
import pandas as pd
import polars as pl
import tif1

DataFrame = Union[pd.DataFrame, pl.DataFrame]

def analyze_laps(laps: DataFrame) -> dict:
    """Works with both pandas and polars."""
    if isinstance(laps, pd.DataFrame):
        return {"count": len(laps), "fastest": laps["LapTime"].min()}
    else:
        return {"count": len(laps), "fastest": laps["LapTime"].min()}

Object type hints

Core Objects

from tif1 import Session, Driver
from tif1.core import Lap, Laps, Telemetry

# Session
session: Session = tif1.get_session(2021, "Belgian Grand Prix", "Race")

# Driver
driver: Driver = session.get_driver("VER")

# Lap
lap: Lap = driver.get_lap(19)

# Laps collection
laps: Laps = session.laps

# Telemetry
telemetry: Telemetry = lap.telemetry

Model Objects

from tif1.core import SessionResults, DriverResult, CircuitInfo

# Session results
results: SessionResults = session.results

# Circuit info
circuit: CircuitInfo = session.get_circuit_info()

Generic type hints

Async Functions

from typing import Awaitable
import pandas as pd
import tif1

async def load_session_async() -> pd.DataFrame:
    session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
    laps: pd.DataFrame = await session.laps_async()
    return laps

# Type hint for async function
load_func: Awaitable[pd.DataFrame] = load_session_async()

Dictionary Returns

from typing import Dict
import pandas as pd
import tif1

session = tif1.get_session(2021, "Belgian Grand Prix", "Race")

# Type hint for telemetry dictionary
tels: Dict[str, pd.DataFrame] = session.get_fastest_laps_tels(by_driver=True)

for driver, tel in tels.items():
    driver_code: str = driver
    telemetry: pd.DataFrame = tel
    print(f"{driver_code}: {len(telemetry)} samples")

Complete type-annotated example

from typing import Dict, List, Tuple, Optional
import pandas as pd
import tif1
from tif1 import Session, Driver
from tif1.core import Lap
from tif1.types import BackendType, SessionType, CompoundType

def analyze_qualifying(
    year: int,
    gp: str,
    lib: BackendType = "pandas"
) -> Dict[str, float]:
    """
    Analyze qualifying session and return fastest lap times.

    Args:
        year: Season year
        gp: Grand Prix name
        lib: DataFrame library to use

    Returns:
        Dictionary mapping driver codes to fastest lap times
    """
    session_name: SessionType = "Qualifying"
    session: Session = tif1.get_session(year, gp, session_name, lib=lib)

    fastest_laps: pd.DataFrame = session.get_fastest_laps(by_driver=True)

    results: Dict[str, float] = {}
    for _, row in fastest_laps.iterrows():
        driver: str = row["Driver"]
        lap_time: float = row["LapTime"]
        results[driver] = lap_time

    return results

def get_compound_usage(
    session: Session,
    driver_code: str
) -> Dict[CompoundType, int]:
    """
    Count laps per compound for a driver.

    Args:
        session: Session object
        driver_code: 3-letter driver code

    Returns:
        Dictionary mapping compounds to lap counts
    """
    driver: Driver = session.get_driver(driver_code)
    laps: pd.DataFrame = driver.laps

    compound_counts: Dict[CompoundType, int] = {}
    for compound in ["SOFT", "MEDIUM", "HARD"]:
        count: int = len(laps[laps["Compound"] == compound])
        if count > 0:
            compound_counts[compound] = count  # type: ignore

    return compound_counts

def find_fastest_sector(
    lap: Lap
) -> Tuple[int, float]:
    """
    Find the fastest sector in a lap.

    Args:
        lap: Lap object

    Returns:
        Tuple of (sector_number, sector_time)
    """
    # Access lap data (Lap is a Series subclass)
    s1: float = lap["Sector1Time"]
    s2: float = lap["Sector2Time"]
    s3: float = lap["Sector3Time"]

    sectors: List[Tuple[int, float]] = [
        (1, s1),
        (2, s2),
        (3, s3)
    ]

    fastest: Tuple[int, float] = min(sectors, key=lambda x: x[1])
    return fastest

# Usage with type checking
if __name__ == "__main__":
    results: Dict[str, float] = analyze_qualifying(2021, "Belgian Grand Prix")

    for driver, time in results.items():
        print(f"{driver}: {time:.3f}s")

Type Checking

Using mypy

# Install mypy
pip install mypy

# Check types
mypy your_script.py

Using pyright

# Install pyright
npm install -g pyright

# Check types
pyright your_script.py

IDE Support

Modern IDEs like VS Code, PyCharm, and others provide:
  • Autocomplete based on type hints
  • Inline type checking
  • Parameter hints
  • Return type hints
Example in VS Code:
import tif1

session = tif1.get_session(2021, "Belgian Grand Prix", "Race")
# Hovering over 'session' shows: session: Session

laps = session.laps
# Hovering over 'laps' shows: laps: DataFrame

driver = session.get_driver("VER")
# Hovering over 'driver' shows: driver: Driver

Best Practices

  1. Always use type hints in function signatures: Helps catch errors early.
  2. Use Union types for lib-agnostic code: Supports both pandas and polars.
  3. Import types from tif1.types: Centralized type definitions.
  4. Use TypedDict for JSON data: Better than plain dicts.
  5. Enable strict type checking: Use mypy or pyright in CI/CD.
  6. Document complex types: Add docstrings explaining type parameters.
  7. Use Optional for nullable values: Makes None handling explicit.
from typing import Optional
import tif1

def get_driver_safely(
    session: tif1.Session,
    driver_code: str
) -> Optional[tif1.Driver]:
    """Get driver or None if not found."""
    try:
        return session.get_driver(driver_code)
    except tif1.DriverNotFoundError:
        return None
Last modified on March 5, 2026