From 59e08fb16f12628a59e29da1b3bd932a5bfc00dd Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Thu, 1 Feb 2018 08:38:26 -0800 Subject: [PATCH] Use six.wraps() for Metrics so decorated methods can be inspected Previously it was using functools.wraps() to create the decorators. The problem is that we can't use the oslo_utils.reflection.get_signature() function to get the correct signature. Change it to use six.wraps() which will add the '__wrapped__' attribute which will be used when calling get_signature() and return the signature of the decorated function. Added unit tests to show we are able to see the signature of a wrapped function. Closes-Bug: #1746730 Change-Id: I75428e948b64b3b6758d31678a80d158a11c6beb (cherry picked from commit cdea1086d64d54b07884caa63103258fbf3047ed) --- ironic_lib/metrics.py | 7 +++-- ironic_lib/tests/test_metrics.py | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/ironic_lib/metrics.py b/ironic_lib/metrics.py index 889ca33d..fa79f9e3 100644 --- a/ironic_lib/metrics.py +++ b/ironic_lib/metrics.py @@ -14,7 +14,6 @@ # under the License. import abc -import functools import random import time @@ -55,7 +54,7 @@ class Timer(object): self._start = None def __call__(self, f): - @functools.wraps(f) + @six.wraps(f) def wrapped(*args, **kwargs): start = _time() result = f(*args, **kwargs) @@ -117,7 +116,7 @@ class Counter(object): self.sample_rate = sample_rate def __call__(self, f): - @functools.wraps(f) + @six.wraps(f) def wrapped(*args, **kwargs): self.metrics.send_counter( self.metrics.get_metric_name(self.name), @@ -164,7 +163,7 @@ class Gauge(object): self.name = name def __call__(self, f): - @functools.wraps(f) + @six.wraps(f) def wrapped(*args, **kwargs): result = f(*args, **kwargs) self.metrics.send_gauge(self.metrics.get_metric_name(self.name), diff --git a/ironic_lib/tests/test_metrics.py b/ironic_lib/tests/test_metrics.py index f941868b..dc75bc41 100644 --- a/ironic_lib/tests/test_metrics.py +++ b/ironic_lib/tests/test_metrics.py @@ -16,17 +16,63 @@ import types import mock +from oslo_utils import reflection from ironic_lib import metrics as metricslib +from ironic_lib import metrics_utils from ironic_lib.tests import base +METRICS = metrics_utils.get_metrics_logger(prefix='foo', backend='noop') + + +@METRICS.timer('testing1') +def timer_check(run, timer=None): + pass + + +@METRICS.counter('testing2') +def counter_check(run, counter=None): + pass + + +@METRICS.gauge('testing2') +def gauge_check(run, gauge=None): + pass + + class MockedMetricLogger(metricslib.MetricLogger): _gauge = mock.Mock(spec_set=types.FunctionType) _counter = mock.Mock(spec_set=types.FunctionType) _timer = mock.Mock(spec_set=types.FunctionType) +class TestMetricReflection(base.IronicLibTestCase): + def test_timer_reflection(self): + # Ensure our decorator is done correctly (six.wraps) and we can get the + # arguments of our decorated function. + expected = ['run', 'timer'] + signature = reflection.get_signature(timer_check) + parameters = list(signature.parameters) + self.assertEqual(expected, parameters) + + def test_counter_reflection(self): + # Ensure our decorator is done correctly (six.wraps) and we can get the + # arguments of our decorated function. + expected = ['run', 'counter'] + signature = reflection.get_signature(counter_check) + parameters = list(signature.parameters) + self.assertEqual(expected, parameters) + + def test_gauge_reflection(self): + # Ensure our decorator is done correctly (six.wraps) and we can get the + # arguments of our decorated function. + expected = ['run', 'gauge'] + signature = reflection.get_signature(gauge_check) + parameters = list(signature.parameters) + self.assertEqual(expected, parameters) + + class TestMetricLogger(base.IronicLibTestCase): def setUp(self): super(TestMetricLogger, self).setUp()