scope-timer
Lightweight scope-based Python profiler with colorful, hierarchical execution-time reports.
scope-timer makes it easy to find performance bottlenecks in your Python code by timing specific sections and presenting the results in a beautiful, hierarchical tree. It's designed to be simple to use and have minimal overhead.
Key Features
- Simple and Intuitive API: Use decorators (
@ScopeTimer.profile_func(...)) or context managers (with ScopeTimer.profile_block(...)) to profile code blocks effortlessly. - Hierarchical Reports: Understand performance bottlenecks in nested function calls and scopes.
- Multiple Outputs: View reports directly in the console, or save them as plain text or interactive HTML files.
- Rich & Colorful: Powered by the
richlibrary for beautiful and readable terminal output. - Lightweight & Predictable: Low memory overhead and linear performance scaling, suitable for complex applications.
- Thread-Safe: Profile multi-threaded applications without interference between threads.
Installation
pip install scope-timer
Usage
You can easily profile functions and code blocks. Here is a simple example of a multi-stage pipeline:
from scope_timer import ScopeTimer
import time
@ScopeTimer.profile_func()
def preprocess():
with ScopeTimer.profile_block("load_data"):
time.sleep(0.01)
with ScopeTimer.profile_block("clean_data"):
time.sleep(0.015)
@ScopeTimer.profile_func()
def compute():
for _ in range(10):
with ScopeTimer.profile_block("matmul"):
time.sleep(0.001)
with ScopeTimer.profile_block("activation"):
time.sleep(0.0005)
@ScopeTimer.profile_func()
def postprocess():
with ScopeTimer.profile_block("save_results"):
time.sleep(0.005)
# Profile the entire pipeline
with ScopeTimer.profile_block("pipeline"):
preprocess()
compute()
postprocess()
# Print the summary to the console
ScopeTimer.summarize()
# You can also save the report to a file
ScopeTimer.save_html("timer_report.html")
Example Output
The following console output shows a structured report with elapsed time, number of calls, and percentage of parent scope.
Enabling / Disabling the Timer
By default, scope-timer is enabled.
If you want to completely disable timing (e.g., in production or benchmarking mode), you can set the environment variable SCOPE_TIMER_ENABLE=0.
export SCOPE_TIMER_ENABLE=0 # disables all timing
This will skip all profiling calls with near-zero overhead, making scope-timer safe to leave in production code.
Note:
The environment variable must be set before
scope_timeris imported.This is because the internal switch is initialized at import time.
To disabling in code:
import os
os.environ["SCOPE_TIMER_ENABLE"] = "0"
from scope_timer import ScopeTimer
Limitations
Currently, scope-timer does not support asyncio.
This is because timer state is tracked per thread, but asyncio runs multiple tasks within a single thread, frequently switching execution contexts using await. As a result:
- Profiling an
async deffunction with@ScopeTimer.profilemay produce incorrect timing or even raiseValueError. - Using
ScopeTimer.profile(...)across anawaitboundary is not safe.
For now,
scope-timeris intended for synchronous or multi-threaded code only.
Performance
scope-timer is designed to be lightweight with predictable performance.
The following graphs show the memory usage and report generation time when profiling a realistic, multi-stage pipeline.
This benchmark simulates a typical pipeline structure consisting of the following:
preprocess: stage with 2 sub-taskscompute: stage with 1 sub-task and 20 repeated prediction scopespostprocess: stage with a result-saving scope
The full pipeline is executed multiple times (10–1000 iterations), and ScopeTimer.summarize() is called once at the end.
As shown:
- Memory usage increases linearly and remains well under 3MB for 1000 pipeline runs.
- Execution time for summarize() stays under 10ms for large workloads.
This ensures stable and low overhead behavior even in performance-critical applications.
API Overview
All methods are static and can be called directly from the ScopeTimer class.
Core Profiling Methods
-
ScopeTimer.profile_block(name: str)Profiles a block of code. It can be used as a context manager (
with). It automatically handles starting and stopping the timer.Parameters:
name (str): The identifier for the scope.
-
ScopeTimer.profile_func(name: str = None)Profiles a function. It can be used as a decorator (
@). It automatically handles starting and stopping the timer.Parameters:
name (str): The identifier for the scope. If not provided, the decorated function’s name will be used automatically.
Reporting Methods
-
ScopeTimer.summarize(time_unit="auto", precision="auto", divider="rule", verbose=False)Prints a formatted summary of timing results to the console.
Parameters:
time_unit (str): The display unit for time. Can be'auto','s','ms', or'us'. Defaults to'auto'.precision (int | str): The number of decimal places for time values. Defaults to'auto'.divider (str): The style of the separator between root scopes. Can be'rule'or'blank'. Defaults to'rule'.verbose (bool): If True, displays detailed statistics (min, max, avg, var). Defaults toFalse.
-
ScopeTimer.save_txt(file_path, **kwargs)Saves a summary of timing results as a plain text file.
Parameters:
file_path (str | Path): The path to the output file.**kwargs: Accepts the same arguments assummarize()(time_unit,precision,verbose).
-
ScopeTimer.save_html(file_path, **kwargs)Saves a summary of timing results as a themed HTML file.
Parameters:
file_path (str | Path): The path to the output file.**kwargs: Accepts the same arguments assummarize()(time_unit,precision,verbose).
Utility Methods
-
ScopeTimer.reset()Resets all recorded timer data, clearing all scopes and measurements. Use this to start a fresh set of measurements within the same process.
Release History
For a detailed history of changes, please see the CHANGELOG.md file.
License
This project is licensed under the MIT License.
Metadata
Release files for scope-timer 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| scope_timer-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for scope_timer-0.3.0-py3-none-any.whl |
Release files / scope_timer-0.3.0-py3-none-any.whl
| Download URL | scope_timer-0.3.0-py3-none-any.whl |
|---|---|
| Size | 11.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
70132ac35279c92325aeae6c149ac678e4601857a2e742bbc110a9b20cd6e150
|
|
BLAKE2b-256 checksum How to use checksums |
f1fc5dc5e3288667e85b3d0d2b7f492e264085fdc3d4824d98fb503e692c792e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.11
|
Security for scope-timer 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.