Skip to content

Commit

Permalink
Merge branch 'release/0.10.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
jara001 committed Mar 11, 2024
2 parents d8ef3bf + bfad66d commit b4dcc79
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 4 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/).

## Unreleased
## 0.10.2 - 2024-03-11
### Added
- `time`:
- Keyword-only argument to `@duration` to report summary only every `interval`.
- A warning is generated when the argument is not used.
- `interval` can be set to `None` in order to disable automatic statistics.
- Argument `filename` that stores every measurement into the file.

### Changed
- `time`:
- Timer summary now also contains minimum value.

## 0.10.1 - 2024-03-07
### Fixed
- `duration`:
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,3 +509,9 @@ TM.end()
TM.summary()
return output
```

However, in contrast to TimeMeasurer, a keyword-only argument `interval`
can be passed to the decorator to report the summary periodically, and not
on every function call.

Since `>0.10.1` not passing `interval` generates a warning on start-up.
68 changes: 65 additions & 3 deletions autopsy/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ def function():
TM.end()
TM.summary()
return output
However, in contrast to TimeMeasurer, a keyword-only argument 'interval'
can be passed to the decorator to report the summary periodically, and not
on every function call.
Since >0.10.1 not passing 'interval' generates a warning on start-up.
"""
######################
# Imports & Globals
Expand All @@ -58,6 +64,9 @@ def function():
# Py2: Allow import of module with the same name
from __future__ import absolute_import

# ReportTimer
from threading import _Timer

# Timing
import time

Expand Down Expand Up @@ -87,6 +96,23 @@ def let_pass(f):
return block if DISABLED else let_pass


######################
# ReportTimer class
######################

class ReportTimer(_Timer):
"""A thread for reporting the measurer.
Source:
https://stackoverflow.com/questions/12435211/threading-timer-repeat-function-every-n-seconds
"""

def run(self):
"""Run a thread function every 'self.interval'."""
while not self.finished.wait(self.interval):
self.function(*self.args, **self.kwargs)


######################
# Measurer class
######################
Expand All @@ -96,7 +122,7 @@ class Measurer(object):

UNITS = ["", "s", "ms", "us", "ns"]

def __init__(self, name = "", unit = ""):
def __init__(self, name = "", unit = "", filename = "", **kwargs):
"""Initialize the Measurer class.
Arguments
Expand All @@ -105,11 +131,16 @@ def __init__(self, name = "", unit = ""):
name of the Measurer
unit: str = ""
units used by the measurer
filename: str = ""
name of the file to log measured data
Raises
------
ValueError
when unit is not in UNITS
IOError
raised by 'open()'
"""
if unit not in self.UNITS:
raise ValueError("unknown unit '%s'" % unit)
Expand All @@ -119,8 +150,10 @@ def __init__(self, name = "", unit = ""):
self._start = 0
self._count = 0
self._sum = 0
self._min = [] # min([], X) returns the number everytime
self._max = 0
self._last = 0
self._file = open(filename, "a") if filename != "" else None


def unitExp(self, unit):
Expand Down Expand Up @@ -191,17 +224,22 @@ def updateStatistics(self):
"""Update internal statistics of the Measurer."""
self._count += 1
self._sum += self._last
self._min = min(self._min, self._last)
self._max = max(self._max, self._last)

if self._file is not None:
self._file.write("%f\n" % self._last)


@conddisable()
def summary(self):
"""Print out the summary of the measurer."""
if self._count == 0:
print("%s: EMPTY" % self._name)
else:
print("%s: cur=%.4f%s avg=%.4f%s max=%.4f%s" % (
print("%s: cur=%.4f%s min=%.4f%s avg=%.4f%s max=%.4f%s" % (
self._name, self._last, self._unit,
self._min, self._unit,
self._sum / self._count, self._unit,
self._max, self._unit
))
Expand Down Expand Up @@ -271,6 +309,19 @@ def duration(*args, **kwargs):

TM = TimeMeasurer(*args, **kwargs)

if "interval" in kwargs:
if kwargs.get("interval") is not None:
report = ReportTimer(kwargs.get("interval"), TM.summary)
report.daemon = True
report.start()
elif "filename" in kwargs:
kwargs["interval"] = None
else:
print (
"Warning: @duration is used without kwarg 'interval', "
"therefore it will report summary on every call."
)

def wrapper(function, *args, **kwags):
def duration_measurer(*args, **kwargs):
TM.start()
Expand All @@ -281,7 +332,18 @@ def duration_measurer(*args, **kwargs):
return output
return duration_measurer

def wrapper_with_timer(function, *args, **kwags):
def duration_measurer(*args, **kwargs):
TM.start()
output = function(*args, **kwargs)
TM.end()

return output
return duration_measurer

def let_pass(function, *args, **kwargs):
return lambda *x, **y: function(*x, **y)

return let_pass if DISABLED else wrapper
return let_pass if DISABLED else (
wrapper if "interval" not in kwargs else wrapper_with_timer
)
2 changes: 1 addition & 1 deletion package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>autopsy</name>
<version>0.10.1</version>
<version>0.10.2</version>
<description>A set of Python utils for F1Tenth project.</description>

<maintainer email="[email protected]">Jaroslav Klapálek</maintainer>
Expand Down

0 comments on commit b4dcc79

Please sign in to comment.