Thanks to visit codestin.com
Credit goes to Github.com

Skip to content

PeterVL02/timekid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Timekid

A high-precision timing and profiling library for Python with support for context managers, decorators, lap timing, and benchmarking.

Tests

Features

  • Multiple Timer Interfaces: Choose between StopWatch, TimerContext, or the registry-based Timer class
  • Context Manager Support: Time code blocks with clean with statements
  • Decorator Support: Automatically time function executions with @timer.timed and @timer.timed_async
  • Lap Timing: Record intermediate times within a timing context
  • Historical Tracking: All timing invocations are stored in lists for statistical analysis
  • Benchmarking: Run functions multiple times with warmup support
  • Async Support: Full support for async functions with @timer.timed_async
  • Type Safe: Comprehensive type hints using modern Python typing features
  • Zero Dependencies: Uses only Python standard library
  • High Precision: Uses time.perf_counter() for accurate timing

Installation

Requirements: Python 3.12+

Install with uv (recommended):

uv pip install -e .

Or with pip:

pip install -e .

Quick Start

Basic Timing with Context Manager

from timekid.timer import Timer

timer = Timer(precision=3)

with timer['database_query']:
    # Your code here
    result = execute_query()

# Access timing (returns list of floats)
print(f"Query took {timer.times['database_query'][0]}s")

Function Timing with Decorators

from timekid.timer import Timer

timer = Timer(precision=2)

@timer.timed
def process_data(data):
    # Your processing logic
    return processed_data

# Call multiple times
for item in items:
    process_data(item)

# Analyze all invocations
times = timer.times['process_data']
print(f"Average: {sum(times) / len(times):.3f}s")
print(f"Min: {min(times):.3f}s, Max: {max(times):.3f}s")

Lap Timing

with timer['data_pipeline'] as t:
    load_data()
    t.lap()  # Record lap 1

    transform_data()
    t.lap()  # Record lap 2

    save_data()
    # Final lap recorded automatically on exit

# Access lap times
contexts = timer.get('data_pipeline')
print(f"Load: {contexts[0].laps[0]}s")
print(f"Transform: {contexts[0].laps[1]}s")
print(f"Save: {contexts[0].laps[2]}s")

Async Function Timing

import asyncio
from timekid.timer import Timer

timer = Timer()

@timer.timed_async
async def fetch_data(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.json()

# Use normally
await fetch_data('https://api.example.com/data')

Benchmarking

from timekid.timer import Timer

timer = Timer()

# Benchmark a function with 1000 iterations
results = timer.benchmark(my_function, num_iter=1000, arg1, arg2)

# Analyze results
times = [r.elapsed_time for r in results]
avg_time = sum(times) / len(times)
print(f"Average: {avg_time:.6f}s")

Simple StopWatch

from timekid.timer import StopWatch

sw = StopWatch(precision=2)
sw.start()

# Your code here
do_something()

elapsed = sw.stop()
print(f"Elapsed: {elapsed}s")

Key Concepts

List-Based Registry

All timer registry values are stored as lists of TimerContext objects. This enables:

  • Historical tracking: Every invocation is preserved
  • Statistical analysis: Calculate min, max, average, standard deviation
  • Performance trends: Track performance over time
  • Consistent API: No mix of single values and lists
# Multiple invocations create list entries
with timer['task']:
    do_work()

with timer['task']:
    do_work()

# Access all timings
all_times = timer.times['task']  # Returns list[float]
first_time = timer.times['task'][0]
latest_time = timer.times['task'][-1]

Decorator Behavior

Decorated functions store all invocations under the function name (no numbered keys):

@timer.timed
def process(item):
    return item * 2

# Call 3 times
process(1)
process(2)
process(3)

# All stored under 'process' key
print(len(timer.times['process']))  # Output: 3

Precision Control

All timer types accept an optional precision parameter for rounding:

timer = Timer(precision=3)  # Round to 3 decimal places

with timer['task']:
    time.sleep(0.123456)

print(timer.times['task'][0])  # Output: 0.123

Verbose Mode

Enable verbose logging to see timing events in real-time:

import logging

logger = logging.getLogger(__name__)
timer = Timer(verbose=True, log_func=logger.info)

with timer['task']:
    # Logs start and stop events
    do_work()

API Reference

Timer Class

Main registry-based interface for timing operations.

Timer(precision: Optional[int] = None, verbose: bool = False, log_func: Callable[[str], None] = print)

Properties:

  • times: Dict[str, list[float]] - All elapsed times for succeeded timers
  • contexts: Dict[str, list[TimerContext]] - All timer contexts
  • precision: Optional[int] - Configured precision for rounding

Methods:

  • timer['key'] - Create/access timer context (creates new context each time)
  • timed(func) - Decorator for synchronous functions
  • timed_async(func) - Decorator for async functions
  • get(key: str) - Get all contexts matching a key
  • status(key: str) - Get list of statuses for a key
  • sorted(reverse: bool = False) - Get timers sorted by elapsed time
  • timeit(func, *args, **kwargs) - Time a single function call
  • benchmark(func, num_iter: int, *args, **kwargs) - Benchmark function with multiple iterations
  • anonymous(name, verbose, log_func) - Create anonymous timer context (not stored in registry)

TimerContext Class

Context manager for timing code blocks.

TimerContext(precision: Optional[int], name: Optional[str] = None, verbose: bool = False, log_func: Callable[[str], None] = print)

Properties:

  • elapsed_time: float - Total elapsed time
  • laps: list[float] - List of lap times
  • status: Status - Current status (PENDING/RUNNING/SUCCEEDED/FAILED)
  • name: str - Timer name

Methods:

  • lap() - Record intermediate time
  • reset() - Reset timer (clears laps, starts from now)
  • rename(name: str) - Change timer name

StopWatch Class

Simple imperative timer with manual control.

StopWatch(precision: Optional[int] = None)

Properties:

  • elapsed_time: float - Elapsed time (raises error if not started)
  • status: Status - Current status

Methods:

  • start() - Start timing
  • stop() - Stop timing and return elapsed time
  • reset() - Reset to initial state

Status Enum

Timer lifecycle states:

  • Status.PENDING - Created but not started
  • Status.RUNNING - Currently timing
  • Status.STOPPED - Manually stopped (StopWatch only)
  • Status.SUCCEEDED - Context exited normally
  • Status.FAILED - Context exited with exception

Testing

Run tests with unittest:

python -m unittest tests._basic_test -v

Development

This project uses uv for package management:

# Install in editable mode
uv pip install -e .

# Run tests
.venv/bin/python -m unittest tests._basic_test -v

# Run examples
python -m timekid.timer

CI/CD

The project includes GitHub Actions workflow for automated testing:

  • Runs on Python 3.12 and 3.13
  • Tests on push/PR to main, master, or develop branches
  • Uses uv for dependency management

Migration from Older Versions

If upgrading from a version with mixed single/list registry values:

# Old way (single values)
elapsed = timer.times['my_task']  # Was a float

# New way (list values)
elapsed = timer.times['my_task'][0]  # First timing
elapsed = timer.times['my_task'][-1]  # Latest timing

Contributing

Contributions are welcome! Please ensure:

  • Python 3.12+ compatibility
  • All tests pass
  • Type hints for all functions
  • Update documentation as needed

License

MIT License

Author

Peter Vestereng Larsen ([email protected])

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages