[](https://opensource.org/licenses/MIT)

# 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 `rich` library 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
```bash
pip install scope-timer
```
## Usage
You can easily profile functions and code blocks. Here is a simple example of a multi-stage pipeline:
```python
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.
<p align="center">
<img src="https://raw.githubusercontent.com/skst328/scope-timer/main/images/screenshot.png" alt="ScopeTimer Console Output" width="400">
</p>
## 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`.
```bash
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_timer` is imported.
>
> This is because the internal switch is initialized at import time.
To disabling in code:
```python
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 def` function with `@ScopeTimer.profile` may produce **incorrect timing** or even raise `ValueError`.
- Using `ScopeTimer.profile(...)` across an `await` boundary is **not safe**.
> For now, `scope-timer` is 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.
<p align="center"> <img src="https://raw.githubusercontent.com/skst328/scope-timer/main/images/scope_timer_performance.png" alt="ScopeTimer Performance Graph" width="90%"> </p>
This benchmark simulates a typical pipeline structure consisting of the following:
* `preprocess`: stage with 2 sub-tasks
* `compute`: stage with 1 sub-task and 20 repeated prediction scopes
* `postprocess`: 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 to `False`.
* `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 as `summarize()` (`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 as `summarize()` (`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.
Raw data
{
"_id": null,
"home_page": null,
"name": "scope-timer",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "performance, profiler, rich, scope, timer, timing",
"author": "skst328",
"author_email": null,
"download_url": null,
"platform": null,
"description": "[](https://opensource.org/licenses/MIT)\n\n\n\n# scope-timer\n\nLightweight scope-based Python profiler with colorful, hierarchical execution-time reports.\n\n`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.\n\n## Key Features\n\n* **Simple and Intuitive API**: Use decorators (`@ScopeTimer.profile_func(...)`) or context managers (`with ScopeTimer.profile_block(...)`) to profile code blocks effortlessly.\n* **Hierarchical Reports**: Understand performance bottlenecks in nested function calls and scopes.\n* **Multiple Outputs**: View reports directly in the console, or save them as plain text or interactive HTML files.\n* **Rich & Colorful**: Powered by the `rich` library for beautiful and readable terminal output.\n* **Lightweight & Predictable**: Low memory overhead and linear performance scaling, suitable for complex applications.\n* **Thread-Safe**: Profile multi-threaded applications without interference between threads.\n\n## Installation\n\n```bash\npip install scope-timer\n```\n\n## Usage\n\nYou can easily profile functions and code blocks. Here is a simple example of a multi-stage pipeline:\n\n```python\nfrom scope_timer import ScopeTimer\nimport time\n\n@ScopeTimer.profile_func()\ndef preprocess():\n with ScopeTimer.profile_block(\"load_data\"):\n time.sleep(0.01)\n with ScopeTimer.profile_block(\"clean_data\"):\n time.sleep(0.015)\n\n@ScopeTimer.profile_func()\ndef compute():\n for _ in range(10):\n with ScopeTimer.profile_block(\"matmul\"):\n time.sleep(0.001)\n with ScopeTimer.profile_block(\"activation\"):\n time.sleep(0.0005)\n\n@ScopeTimer.profile_func()\ndef postprocess():\n with ScopeTimer.profile_block(\"save_results\"):\n time.sleep(0.005)\n\n# Profile the entire pipeline\nwith ScopeTimer.profile_block(\"pipeline\"):\n preprocess()\n compute()\n postprocess()\n\n# Print the summary to the console\nScopeTimer.summarize()\n\n# You can also save the report to a file\nScopeTimer.save_html(\"timer_report.html\")\n```\n\n## Example Output\n\nThe following console output shows a structured report with elapsed time, number of calls, and percentage of parent scope.\n\n<p align=\"center\">\n <img src=\"https://raw.githubusercontent.com/skst328/scope-timer/main/images/screenshot.png\" alt=\"ScopeTimer Console Output\" width=\"400\">\n</p>\n\n## Enabling / Disabling the Timer\n\nBy default, `scope-timer` is enabled.\nIf you want to completely disable timing (e.g., in production or benchmarking mode), you can set the environment variable `SCOPE_TIMER_ENABLE=0`.\n\n```bash\nexport SCOPE_TIMER_ENABLE=0 # disables all timing\n```\n\nThis will skip all profiling calls with **near-zero overhead**, making `scope-timer` safe to leave in production code.\n\n> Note:\n>\n> The environment variable must be set before `scope_timer` is imported.\n>\n> This is because the internal switch is initialized at import time.\n\nTo disabling in code:\n\n```python\nimport os\nos.environ[\"SCOPE_TIMER_ENABLE\"] = \"0\"\nfrom scope_timer import ScopeTimer\n```\n\n## Limitations\n\nCurrently, `scope-timer` does **not support** `asyncio`.\n\nThis 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:\n\n- Profiling an `async def` function with `@ScopeTimer.profile` may produce **incorrect timing** or even raise `ValueError`.\n- Using `ScopeTimer.profile(...)` across an `await` boundary is **not safe**.\n\n> For now, `scope-timer` is intended for synchronous or multi-threaded code only.\n\n\n## Performance\n\n`scope-timer` is designed to be lightweight with predictable performance.\nThe following graphs show the memory usage and report generation time when profiling a realistic, multi-stage pipeline.\n\n<p align=\"center\"> <img src=\"https://raw.githubusercontent.com/skst328/scope-timer/main/images/scope_timer_performance.png\" alt=\"ScopeTimer Performance Graph\" width=\"90%\"> </p>\n\nThis benchmark simulates a typical pipeline structure consisting of the following:\n\n* `preprocess`: stage with 2 sub-tasks\n* `compute`: stage with 1 sub-task and 20 repeated prediction scopes\n* `postprocess`: stage with a result-saving scope\n\nThe full pipeline is executed multiple times (10\u20131000 iterations), and ScopeTimer.summarize() is called once at the end.\n\nAs shown:\n\n* Memory usage increases linearly and remains well under 3MB for 1000 pipeline runs.\n* Execution time for summarize() stays under 10ms for large workloads.\n\nThis ensures stable and low overhead behavior even in performance-critical applications.\n\n\n\n## API Overview\n\nAll methods are static and can be called directly from the `ScopeTimer` class.\n\n### Core Profiling Methods\n\n\n* `ScopeTimer.profile_block(name: str)`\n\n Profiles a block of code. It can be used as a context manager (`with`). It automatically handles starting and stopping the timer.\n\n Parameters:\n - `name (str)`: The identifier for the scope.\n\n* `ScopeTimer.profile_func(name: str = None)`\n\n Profiles a function. It can be used as a decorator (`@`). It automatically handles starting and stopping the timer.\n\n Parameters:\n - `name (str)`: The identifier for the scope. If not provided, the decorated function\u2019s name will be used automatically.\n\n### Reporting Methods\n\n* `ScopeTimer.summarize(time_unit=\"auto\", precision=\"auto\", divider=\"rule\", verbose=False)`\n\n Prints a formatted summary of timing results to the console.\n\n Parameters:\n - `time_unit (str)`: The display unit for time. Can be `'auto'`, `'s'`, `'ms'`, or `'us'`. Defaults to `'auto'`.\n - `precision (int | str)`: The number of decimal places for time values. Defaults to `'auto'`.\n - `divider (str)`: The style of the separator between root scopes. Can be `'rule'` or `'blank'`. Defaults to `'rule'`.\n - `verbose (bool)`: If True, displays detailed statistics (min, max, avg, var). Defaults to `False`.\n\n* `ScopeTimer.save_txt(file_path, **kwargs)`\n\n Saves a summary of timing results as a plain text file.\n\n Parameters:\n - `file_path (str | Path)`: The path to the output file.\n - `**kwargs`: Accepts the same arguments as `summarize()` (`time_unit`, `precision`, `verbose`).\n\n* `ScopeTimer.save_html(file_path, **kwargs)`\n\n Saves a summary of timing results as a themed HTML file.\n\n Parameters:\n - `file_path (str | Path)`: The path to the output file.\n - `**kwargs`: Accepts the same arguments as `summarize()` (`time_unit`, `precision`, `verbose`).\n\n### Utility Methods\n\n* `ScopeTimer.reset()`\n\n Resets all recorded timer data, clearing all scopes and measurements. Use this to start a fresh set of measurements within the same process.\n\n## Release History\n\nFor a detailed history of changes, please see the `CHANGELOG.md` file.\n\n## License\n\nThis project is licensed under the MIT License.\n",
"bugtrack_url": null,
"license": "MIT License",
"summary": "Lightweight scope-based Python profiler with colorful, hierarchical execution time reports.",
"version": "0.3.0",
"project_urls": {
"Bug Tracker": "https://github.com/skst328/scope-timer/issues",
"Homepage": "https://github.com/skst328/scope-timer"
},
"split_keywords": [
"performance",
" profiler",
" rich",
" scope",
" timer",
" timing"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f1fc5dc5e3288667e85b3d0d2b7f492e264085fdc3d4824d98fb503e692c792e",
"md5": "71482bc4e7de0ff53751e22c729a2a90",
"sha256": "70132ac35279c92325aeae6c149ac678e4601857a2e742bbc110a9b20cd6e150"
},
"downloads": -1,
"filename": "scope_timer-0.3.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "71482bc4e7de0ff53751e22c729a2a90",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 11314,
"upload_time": "2025-08-09T14:11:58",
"upload_time_iso_8601": "2025-08-09T14:11:58.507610Z",
"url": "https://files.pythonhosted.org/packages/f1/fc/5dc5e3288667e85b3d0d2b7f492e264085fdc3d4824d98fb503e692c792e/scope_timer-0.3.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-09 14:11:58",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "skst328",
"github_project": "scope-timer",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "scope-timer"
}