From 0745de647b34ef2e275502647b8a820082ab7c9b Mon Sep 17 00:00:00 2001
From: Florian Bruhin <git@the-compiler.org>
Date: Wed, 16 Sep 2015 22:50:19 +0200
Subject: [PATCH] Allow debug.log_time to be used as decorator.

---
 qutebrowser/utils/debug.py     | 49 ++++++++++++++++++++++++----------
 tests/unit/utils/test_debug.py | 15 +++++++++++
 2 files changed, 50 insertions(+), 14 deletions(-)

diff --git a/qutebrowser/utils/debug.py b/qutebrowser/utils/debug.py
index ba79a0341..abdeac947 100644
--- a/qutebrowser/utils/debug.py
+++ b/qutebrowser/utils/debug.py
@@ -225,23 +225,44 @@ def format_call(func, args=None, kwargs=None, full=True):
     return '{}({})'.format(name, format_args(args, kwargs))
 
 
-@contextlib.contextmanager
-def log_time(logger, action='operation'):
-    """Log the time the operation in the with-block takes.
+class log_time:  # pylint: disable=invalid-name
 
-    Args:
-        logger: The logging.Logger to use for logging, or a logger name.
-        action: A description of what's being done.
+    """Log the time an operation takes.
+
+    Usable as context manager or as decorator.
     """
-    if isinstance(logger, str):
-        logger = logging.getLogger(logger)
-    started = datetime.datetime.now()
-    try:
-        yield
-    finally:
+
+    def __init__(self, logger, action='operation'):
+        """Constructor.
+
+        Args:
+            logger: The logging.Logger to use for logging, or a logger name.
+            action: A description of what's being done.
+        """
+        if isinstance(logger, str):
+            self._logger = logging.getLogger(logger)
+        else:
+            self._logger = logger
+        self._started = None
+        self._action = action
+
+    def __enter__(self):
+        self._started = datetime.datetime.now()
+
+    def __exit__(self, _exc_type, _exc_val, _exc_tb):
+        assert self._started is not None
         finished = datetime.datetime.now()
-        delta = (finished - started).total_seconds()
-        logger.debug("{} took {} seconds.".format(action.capitalize(), delta))
+        delta = (finished - self._started).total_seconds()
+        self._logger.debug(
+            "{} took {} seconds.".format(self._action.capitalize(), delta))
+
+    def __call__(self, func):
+        @functools.wraps(func)
+        def wrapped(*args, **kwargs):
+            with self:
+                func(*args, **kwargs)
+
+        return wrapped
 
 
 def _get_widgets():
diff --git a/tests/unit/utils/test_debug.py b/tests/unit/utils/test_debug.py
index 2b853140a..f1cf7e1e4 100644
--- a/tests/unit/utils/test_debug.py
+++ b/tests/unit/utils/test_debug.py
@@ -110,6 +110,21 @@ class TestLogTime:
 
         assert len(caplog.records()) == 1
 
+    def test_decorator(self, caplog):
+        logger_name = 'qt-tests'
+
+        @debug.log_time(logger_name, action='foo')
+        def func(arg, *, kwarg):
+            assert arg == 1
+            assert kwarg == 2
+
+        with caplog.atLevel(logging.DEBUG, logger_name):
+            func(1, kwarg=2)
+
+        records = caplog.records()
+        assert len(records) == 1
+        assert records[0].msg.startswith('Foo took')
+
 
 class TestQEnumKey: