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"
]
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"
]
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]
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
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)
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
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
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
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
- Always use type hints in function signatures: Helps catch errors early.
- Use Union types for lib-agnostic code: Supports both pandas and polars.
- Import types from tif1.types: Centralized type definitions.
- Use TypedDict for JSON data: Better than plain dicts.
- Enable strict type checking: Use mypy or pyright in CI/CD.
- Document complex types: Add docstrings explaining type parameters.
- 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