Source code for schrodinger.application.desmond.packages.timer
"""
Facilities for timing a block of code.
Copyright Schrodinger, LLC. All rights reserved.
Example usage:
   from schrodinger.application.desmond.packages.timer import timer
   with timer("Reading the trajectory..."):
       traj.read_trj(trj_fname)
Example output:
   Reading the trajectory...
     100 frames
     Done. (0.5 sec)
In this example, we have three lines in output. Line#1 is the start message, and
line#3 is the timing message. The start-message and the format for the timing
message can be customed by the `message` and `fmt` arguments, respectively.
Arguments for `timer`:
:type    message: `str` or `None`
:type    message: The message to print out before the timing starts. Default is
                  `None` -- no message will be printed.
:type        fmt: `str`
:type        fmt: The format to print out the timing. Default is
                  "Done. (%.2f sec)". A custom format should contain 1 floating
                  number control paramter.
:type  threshold: `float`
:param threshold: Specifies a threshold. If the elapsed time is less than
                  `threshold`, no timing message will not be printed out.
:type     record: `list` or `None`
:param    record: If a list value is given, the current elapsed time will be
                  appended to the list. This is handy if you want to accumulate
                  the elapsed times.
:type     logger: A callable object.
:param    logger: Function called to print the messages.
"""
import time
from collections import namedtuple
from contextlib import contextmanager
def _default_logger(message):
    print(message)
[docs]class Timer(object):
    """
    You can use this class to create your own timer function with custom default
    behavior.
    Example usage:
      from schrodinger.application.desmond.packages.timer import Timer
      mytimer = Timer(threshold=0.2)
      # Creates a timer with 0.2 threshold.
      with mytimer("Reading the trajectory..."):
        traj.read_trj(trj_fname)
    In this example, `mytimer` behaves exactly the same as the standard
    `timer` (see the docstring of this module) except for the default value of
    `threshold` parameter that has been customed to 0.2.
    """
    Args = namedtuple("Args", "message threshold fmt record logger")
[docs]    def __init__(self,
                 message=None,
                 threshold=0.1,
                 fmt="  Done. (%.2f sec)",
                 record=None,
                 logger=_default_logger):
        self._default = Timer.Args(message, threshold, fmt, record, logger) 
    @contextmanager
    def __call__(self, *arg, **kwarg):
        a = list(self._default)
        a = list(arg) + a[len(arg):]  # Updates `a' with `arg'.
        a = Timer.Args(*a)
        a = a._replace(**kwarg)  # Updates `a' with `kwarg'.
        message, threshold, fmt, record, logger = a
        message and logger(message)
        start_time = time.time()
        yield
        elapsed_time = time.time() - start_time
        if elapsed_time > threshold:
            logger(fmt % elapsed_time)
        if isinstance(record, list):
            record.append(elapsed_time) 
timer = Timer()