-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Merge] [#80] Create context manager for timing code blocks
- Loading branch information
Showing
12 changed files
with
204 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
pyxx.dev | ||
======== | ||
|
||
.. automodule:: pyxx.dev | ||
|
||
.. currentmodule:: pyxx.dev | ||
|
||
|
||
Code Profiling | ||
-------------- | ||
|
||
The classes below are intended to assist in analyzing the performance of and | ||
profiling code. | ||
|
||
.. autosummary:: | ||
:toctree: ./api | ||
:template: ../_templates/api_reference_class_template.rst | ||
|
||
TimeIt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
"""**Functions and utilities to assist in debugging or analyzing Python code** | ||
The :py:mod:`pyxx.dev` module is intended to provide commonly used tools | ||
or scripts that can be useful when debugging Python code. | ||
""" | ||
|
||
from .classes import TimeIt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"""This module contains classes that can be used to assist in debugging | ||
Python code. | ||
""" | ||
|
||
from .timer import TimeIt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
"""Classes for timing code execution. | ||
""" | ||
|
||
import time | ||
|
||
from pyxx.units.classes.unitconverter import UnitConverterSI | ||
|
||
|
||
class TimeIt: | ||
"""A context manager for measuring the duration of a code block. | ||
This context manager can be used in a "with" statement to measure the | ||
duration of code within the statement. The resulting duration will be | ||
printed to the console when the "with" statement completes. | ||
Warnings | ||
-------- | ||
Using this timer will add some overhead to the code block being measured. | ||
The measured duration outputted to the terminal will be slightly larger | ||
than the actual execution time of the code block. | ||
Examples | ||
-------- | ||
Time a code block and print the duration to the terminal: | ||
.. code-block:: python | ||
>>> from pyxx.dev import TimeIt | ||
>>> import time | ||
>>> with TimeIt(units='ms', message='Execution time: {time:.2f} {units}'): | ||
... # Code block of which to measure the duration | ||
... time.sleep(1) | ||
Execution time: 1000.10 ms | ||
Time a code block and access the duration in Python code following the | ||
code block: | ||
.. code-block:: python | ||
>>> from pyxx.dev import TimeIt | ||
>>> import time | ||
>>> timer = TimeIt(print_duration=False) | ||
>>> with timer: | ||
... # Code block of which to measure the duration | ||
... time.sleep(1) | ||
>>> print(timer.duration('ms')) | ||
1000.1010101010101 | ||
""" | ||
|
||
def __init__(self, print_duration: bool = True, units: str = 's', | ||
message: str = 'Code duration: {time} {units}') -> None: | ||
"""Creates a new context manager for measuring the duration of a code block | ||
Parameters | ||
---------- | ||
print_duration : bool, optional | ||
Whether to print to the terminal the duration of the code block | ||
units : str, optional | ||
Only applicable if ``print_duration`` is ``True``. Specifies the | ||
units in which the duration will be displayed to the terminal | ||
(default is ``'s'``) | ||
message : str, optional | ||
Only applicable if ``print_duration`` is ``True``. The message | ||
template to display the duration (default is | ||
``'Code duration: {time} {units}'``). The ``{time}`` and | ||
``{units}`` placeholders will be replaced by the duration and | ||
units, respectively | ||
""" | ||
self.__unit_converter = UnitConverterSI() | ||
|
||
if (not (self.__unit_converter.is_defined_unit(units) | ||
and self.__unit_converter.is_convertible('s', units))): | ||
raise ValueError( | ||
f'Invalid units: cannot convert from seconds to "{units}"') | ||
|
||
self.__print = bool(print_duration) | ||
self.__units = units | ||
self.__message = message | ||
|
||
self.__t0_s = 0.0 | ||
self.__dt_s = 0.0 | ||
|
||
def __enter__(self) -> None: | ||
self.__t0_s = time.time() | ||
|
||
def __exit__(self, *args, **kwargs) -> None: | ||
t1_s = time.time() | ||
|
||
# Calculate duration in seconds | ||
self.__dt_s = t1_s - self.__t0_s | ||
|
||
if self.__print: | ||
duration = self.__unit_converter.convert( | ||
quantity=self.__dt_s, from_unit='s', to_unit=self.__units) | ||
|
||
print(self.__message.format(time=duration, units=self.__units)) | ||
|
||
def duration(self, units: str = 's') -> float: | ||
"""Returns the last measured duration from the context manager | ||
Parameters | ||
---------- | ||
units : str | ||
The units in which to return the duration (default is ``'s'``) | ||
""" | ||
return float(self.__unit_converter.convert( | ||
quantity=self.__dt_s, from_unit='s', to_unit=units | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .classes import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .test_timer import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import time | ||
import unittest | ||
|
||
from pyxx.dev import TimeIt | ||
from tests import CapturePrint | ||
|
||
|
||
class Test_TimeIt(unittest.TestCase): | ||
def test_duration(self): | ||
# Verifies that duration is measured within a reasonable range | ||
timer = TimeIt(print_duration=False) | ||
with timer: | ||
time.sleep(1.5) | ||
|
||
self.assertAlmostEqual(timer.duration(), 1.5, delta=0.1) | ||
|
||
def test_default_message(self): | ||
# Verifies that correct default message is printed after code completes | ||
with CapturePrint() as terminal_output: | ||
timer = TimeIt() | ||
with timer: | ||
time.sleep(0.1) | ||
|
||
self.assertEqual( | ||
terminal_output.getvalue(), | ||
f'Code duration: {timer.duration()} s\n', | ||
) | ||
|
||
def test_custom_message(self): | ||
# Verifies that correct custom message is printed after code completes | ||
with CapturePrint() as terminal_output: | ||
timer = TimeIt(message='Execution time: {time} {units}', units='ms') | ||
with timer: | ||
time.sleep(0.1) | ||
|
||
self.assertEqual( | ||
terminal_output.getvalue(), | ||
f'Execution time: {timer.duration() * 1000} ms\n', | ||
) | ||
|
||
def test_convert_duration(self): | ||
# Verifies that unit conversion of returned duration is performed correctly | ||
timer = TimeIt(print_duration=False) | ||
with timer: | ||
time.sleep(0.5) | ||
|
||
self.assertAlmostEqual( | ||
timer.duration(units='s') * 1000, | ||
timer.duration(units='ms'), | ||
) | ||
|
||
def test_invalid_units(self): | ||
# Verify that an error is raised when invalid units are specified | ||
with self.assertRaises(ValueError): | ||
with TimeIt(units='invalid'): | ||
pass |