Chronograph

Introduction

Chronograph is a feature-packed timer/stopwatch/chronometer that makes it simple to time Python code and functions. Features include:

  • support for named Chronographs and labeled splits
  • reporting
  • multiple verbosity levels
  • built-in logging
  • JSON-compatible output data, including timestamps of start/stop
  • custom exceptions if desired
  • pain-free global management of multiple Chronographs
  • function decorators
  • support of with() syntax
  • cast to float gives total time
  • unit tested

See below for usage examples.

Installation

You can install via pip:

pip install Chronograph

or easy_install:

easy_install Chronograph

or Github:

git clone git@github.com:computron/chronograph.git
cd chronograph
python setup.py install

The Github page also contains the source code.

Ten Examples of How to Use Chronograph

Example 1: Simple usage

Here is a straightforward usage example (note that Example #3 is even simpler):

import time
from chronograph.chronograph import Chronograph

cg = Chronograph(name="Testing Chronograph")

cg.start("first section")
time.sleep(0.5)
cg.stop()

cg.start("second section")
time.sleep(0.25)
cg.stop()

You can then print the output like this:

>>> print(cg.total_elapsed_time)
0.757287
>>> print(float(cg))
0.757287
>>> print(cg.last_split_time)
0.253046
>>> print(cg.report())
Report for Testing Chronograph
Split first section: 0.504241
Split second section: 0.253046
Total elapsed time: 0.757287
>>> print(cg.timing_data)
[{'start': datetime.datetime(2016, 2, 8, 20, 12, 38, 170123), 'stop': datetime.datetime(2016, 2, 8, 20, 12, 38, 674364), 'label': 'first section'}, {'start': datetime.datetime(2016, 2, 8, 20, 12, 38, 674395), 'stop': datetime.datetime(2016, 2, 8, 20, 12, 38, 927441), 'label': 'second section'}]

Example 2: Using splits

The stop/start functionality of the code above can be condensed by using split():

import time
from chronograph.chronograph import Chronograph

cg = Chronograph(name="Testing Chronograph")

cg.start("first section")
time.sleep(0.5)

cg.split("second section")
time.sleep(0.25)
cg.stop()

(see Example 1 for printing the output)

Example 3: Starting timing quickly

It is easy start timing quickly, for example:

import time
from chronograph.chronograph import Chronograph

cg = Chronograph(start_timing=True)
time.sleep(0.5)
cg.stop()

(see Example 1 for printing the output)

Example 4: Controlling verbosity

You can make the Chronograph verbose, e.g.:

import time
from chronograph.chronograph import Chronograph

cg = Chronograph(verbosity=2, start_timing=True)
time.sleep(0.5)
cg.stop()

(see Example 1 for printing the output)

During timing, this option will print out something like:

Chronograph: Split (1) started at: 2016-02-08 20:22:39.246814
Chronograph: stopped at: 2016-02-08 20:22:39.246814
Chronograph: Total elapsed time: 0.50099 s. Last split (1) time: 0.50099 s.

Example 5: Integrating with a logger

It is easy to integrate the Chronograph with a logger:

import logging
import time
from chronograph.chronograph import Chronograph

# set up logger
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)  # anything debug and above passes through to the handler level
fh = logging.FileHandler("chronograph.log", "a")
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)

# start chronograph
cg = Chronograph(verbosity=2, logger=logger, log_lvl="INFO", start_timing=True)
time.sleep(0.5)
cg.stop()

cg.report(printout=True)

Example 6: Multiple chronographs with global scope

Need to manage multiple Chronographs across multiple files and functions? You can use the get_chronograph() function to get a global Chronograph that can be accessed anywhere:

import time
from chronograph.chronograph import get_chronograph


def func1():
    cg = get_chronograph("my first chronograph")
    cg.start()
    time.sleep(0.5)
    cg.stop()


def func2():
    cg = get_chronograph("my second chronograph")
    cg.start()
    time.sleep(0.25)
    cg.stop()

if __name__ == "__main__":
    cg1 = get_chronograph("my first chronograph", start_timing=True)
    time.sleep(0.75)
    cg1.stop()
    func1()
    func2()
    cg1.report(printout=True)

    cg2 = get_chronograph("my second chronograph")
    cg2.report(printout=True)

The first time you call get_chronograph(), it will be created and stored for future use. Note that you can set the initialization parameters for a Chronograph only on the first time that you call get_chronograph().

Example 7: Using the with() clause

You can make the syntax of a Chronograph slightly more compact by using the with() clause:

from chronograph.chronograph import get_chronograph
import time

with get_chronograph("hello") as x:
        time.sleep(1)

print get_chronograph("hello").total_elapsed_time

Example 8: Throwing exceptions

Need to enforce correct usage? You can optionally throw exceptions:

from chronograph.chronograph import Chronograph

cg = Chronograph(name="TestCase", throw_exceptions=True)

cg.start()
cg.start()  # can't start a Chronograph that's already started!

Since the throw_exceptions parameter was set to True, this will throw the exception: chronograph.chronograph.ChronographError: TestCase: Warning: Cannot start Chronograph while in current state! Stop or reset chronograph before starting.

Example 9: Function decorators

If you decorate a function with the add_chronograph decorator, it will automatically time a split every time that function is called. By default, the name of the Chronograph will be the function name, but you can set any Chronograph initialization parameters (such as name) that you desire:

import time
from chronograph.chronograph import add_chronograph, get_chronograph


@add_chronograph()
def func1():
    time.sleep(0.5)

@add_chronograph(name="my func2 timer")
def func2():
    time.sleep(0.25)


if __name__ == "__main__":

    func1()
    func1()
    func1()
    func2()


    cg1 = get_chronograph("func1")
    cg1.report(printout=True)

    print("")

    cg2 = get_chronograph("my func2 timer")
    cg2.report(printout=True)

Example 10: JSON output and accessing split data

The following code demonstrates how to access the split data as well as how to serialize and deserialize to JSON:

import json
import time
from bson import json_util
from chronograph.chronograph import Chronograph, get_split_time

if __name__ == "__main__":

    cg = Chronograph(start_timing=True)
    time.sleep(0.25)
    cg.split()
    time.sleep(0.75)
    cg.stop()

    # JSON compatible data
    print json.dumps(cg.timing_data, default=json_util.default)

    # Iterate through timing data and get all splits
    for t in cg.timing_data:
        print get_split_time(t)