Skip to content

Commit

Permalink
Merge branch 'tickets/DM-42410'
Browse files Browse the repository at this point in the history
  • Loading branch information
kfindeisen committed Jan 16, 2024
2 parents ad54fd2 + c009dfb commit 46d7d7f
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 1 deletion.
4 changes: 4 additions & 0 deletions doc/lsst.verify/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,8 @@ Python API reference
:no-main-docstr:
:no-inheritance-diagram:

.. automodapi:: lsst.verify.timer
:no-main-docstr:
:no-inheritance-diagram:

.. _SQUASH: https://squash.lsst.codes
1 change: 1 addition & 0 deletions python/lsst/verify/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@
from .jobmetadata import *
from .job import *
from .output import *
from . import timer
from . import yamlpersistance
2 changes: 1 addition & 1 deletion python/lsst/verify/jobmetadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def values(self):
"""Iterate over metadata values.
Returns
------
-------
items : `~collections.abc.ValuesView`
An iterable over all the values.
"""
Expand Down
70 changes: 70 additions & 0 deletions python/lsst/verify/timer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# This file is part of verify.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

__all__ = ["time_this_to_measurement"]

from contextlib import contextmanager
import time

import astropy.units as u

from .datum import Datum
from .measurement import Measurement


def _epoch_to_iso(seconds):
"""Convert a time in seconds since Unix epoch to an ISO 8601 timestamp.
Parameters
----------
seconds : `float`
The number of seconds since the Unix epoch.
Returns
-------
timestamp : `str`
The input time represented as a timestamp.
"""
iso_format = "%Y-%m-%dT%H:%M:%SZ"
return time.strftime(iso_format, time.gmtime(seconds))


@contextmanager
def time_this_to_measurement(measurement: Measurement):
"""Time the enclosed block and record it as an lsst.verify measurement.
Parameters
----------
measurement : `lsst.verify.Measurement`
Measurement object to fill with the timing information. Its metric must
have time dimensions. Any properties other than ``metric`` and
``metric_name`` may be overwritten.
"""
start = time.time()
try:
yield
finally:
end = time.time()
measurement.quantity = (end - start) * u.second
# Same metadata as provided by TimingMetricTask
measurement.notes["estimator"] = "verify.timer.time_this_to_measurement"
measurement.extras["start"] = Datum(_epoch_to_iso(start))
measurement.extras["end"] = Datum(_epoch_to_iso(end))
95 changes: 95 additions & 0 deletions tests/test_timer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# This file is part of verify.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

import time
import unittest

import astropy.units as u

import lsst.utils.tests

from lsst.verify import Measurement, Metric, Name
from lsst.verify.timer import time_this_to_measurement


class TimeThisTestSuite(unittest.TestCase):
def setUp(self):
super().setUp()
self.metric_name = Name("verify.DummyTime")

def test_basic(self):
duration = 0.2
meas = Measurement(self.metric_name)
with time_this_to_measurement(meas):
time.sleep(duration)

self.assertEqual(meas.metric_name, self.metric_name) # Should not have changed
self.assertIsNotNone(meas.quantity)
self.assertGreater(meas.quantity, duration * u.second)
self.assertLess(meas.quantity, 2 * duration * u.second)

def test_unit_checking_ok(self):
duration = 0.2
metric = Metric(self.metric_name, "Unconventional metric", u.nanosecond)
meas = Measurement(metric)
with time_this_to_measurement(meas):
time.sleep(duration)

self.assertEqual(meas.metric_name, self.metric_name) # Should not have changed
self.assertIsNotNone(meas.quantity)
self.assertGreater(meas.quantity, duration * u.second)
self.assertLess(meas.quantity, 2 * duration * u.second)

def test_unit_checking_bad(self):
duration = 0.2
metric = Metric(self.metric_name, "Non-temporal metric", u.meter / u.second)
meas = Measurement(metric)
with self.assertRaises(TypeError):
with time_this_to_measurement(meas):
time.sleep(duration)

def test_exception(self):
duration = 0.2
meas = Measurement(self.metric_name)
try:
with time_this_to_measurement(meas):
time.sleep(duration)
raise RuntimeError("Something went wrong!")
except RuntimeError:
pass

self.assertEqual(meas.metric_name, self.metric_name) # Should not have changed
self.assertIsNotNone(meas.quantity)
self.assertGreater(meas.quantity, duration * u.second)
self.assertLess(meas.quantity, 2 * duration * u.second)


class MemoryTester(lsst.utils.tests.MemoryTestCase):
pass


def setup_module(module):
lsst.utils.tests.init()


if __name__ == "__main__":
lsst.utils.tests.init()
unittest.main()

0 comments on commit 46d7d7f

Please sign in to comment.