8000 Support decoration of async functions by Pliner · Pull Request #767 · prometheus/client_python · GitHub
[go: up one dir, main page]

Skip to content

Support decoration of async functions #767

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 89 additions & 22 deletions tests/test_core.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import asyncio
from concurrent.futures import ThreadPoolExecutor
import time
import unittest

import pytest
import aiounittest # type: ignore
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No typestub is available


from prometheus_client.core import (
CollectorRegistry, Counter, CounterMetricFamily, Enum, Gauge,
Expand All @@ -28,7 +29,7 @@ def assert_not_observable(fn, *args, **kwargs):
assert False, "Did not raise a 'missing label values' exception"


class TestCounter(unittest.TestCase):
class TestCounter(aiounittest.AsyncTestCase):
def setUp(self):
self.registry = CollectorRegistry()
self.counter = Counter('c_total', 'help', registry=self.registry)
Expand Down Expand Up @@ -56,30 +57,42 @@ def f(r):

self.assertEqual((["r"], None, None, None), getargspec(f))

try:
with self.assertRaises(TypeError):
f(False)
except TypeError:
pass
self.assertEqual(0, self.registry.get_sample_value('c_total'))

try:
with self.assertRaises(ValueError):
f(True)
except ValueError:
pass
self.assertEqual(1, self.registry.get_sample_value('c_total'))

async def test_async_function_decorator(self):
@self.counter.count_exceptions(ValueError)
async def f(r):
if r:
raise ValueError
else:
raise TypeError

self.assertEqual((["r"], None, None, None), getargspec(f))

with self.assertRaises(TypeError):
await f(False)

self.assertEqual(0, self.registry.get_sample_value('c_total'))

with self.assertRaises(ValueError):
await f(True)

self.assertEqual(1, self.registry.get_sample_value('c_total'))

def test_block_decorator(self):
with self.counter.count_exceptions():
pass
self.assertEqual(0, self.registry.get_sample_value('c_total'))

raised = False
try:
with self.assertRaises(ValueError):
with self.counter.count_exceptions():
raise ValueError
except:
raised = True
self.assertTrue(raised)
self.assertEqual(1, self.registry.get_sample_value('c_total'))

def test_count_exceptions_not_observable(self):
Expand Down Expand Up @@ -115,7 +128,7 @@ def test_exemplar_too_long(self):
})


class TestGauge(unittest.TestCase):
class TestGauge(aiounittest.AsyncTestCase):
def setUp(self):
self.registry = CollectorRegistry()
self.gauge = Gauge('g', 'help', registry=self.registry)
Expand Down Expand Up @@ -160,6 +173,18 @@ def f():
f()
self.assertEqual(0, self.registry.get_sample_value('g'))

async def test_inprogress_async_function_decorator(self):
self.assertEqual(0, self.registry.get_sample_value('g'))

@self.gauge.track_inprogress()
async def f():
self.assertEqual(1, self.registry.get_sample_value('g'))

self.assertEqual(([], None, None, None), getargspec(f))

await f()
self.assertEqual(0, self.registry.get_sample_value('g'))

def test_inprogress_block_decorator(self):
self.assertEqual(0, self.registry.get_sample_value('g'))
with self.gauge.track_inprogress():
Expand All @@ -185,12 +210,24 @@ def test_time_function_decorator(self):

@self.gauge.time()
def f():
time.sleep(.001)
time.sleep(.05)

self.assertEqual(([], None, None, None), getargspec(f))

f()
self.assertNotEqual(0, self.registry.get_sample_value('g'))
self.assertTrue(0.05 <= self.registry.get_sample_value('g') <= 0.1)
Copy link
Contributor Author
@Pliner Pliner Feb 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to check the expected duration. It makes more sense in a case of async functions to be sure that the decorator works fine.


async def test_time_async_function_decorator(self):
self.assertEqual(0, self.registry.get_sample_value('g'))

@self.gauge.time()
async def f():
await asyncio.sleep(.05)

self.assertEqual(([], None, None, None), getargspec(f))

await f()
self.assertTrue(0.05 <= self.registry.get_sample_value('g') <= 0.1)

def test_function_decorator_multithread(self):
self.assertEqual(0, self.registry.get_sample_value('g'))
Expand Down Expand Up @@ -239,7 +276,7 @@ def manager():
assert_not_observable(manager)


class TestSummary(unittest.TestCase):
class TestSummary(aiounittest.AsyncTestCase):
def setUp(self):
self.registry = CollectorRegistry()
self.summary = Summary('s', 'help', registry=self.registry)
Expand All @@ -264,12 +301,26 @@ def test_function_decorator(self):

@self.summary.time()
def f():
pass
time.sleep(.05)

self.assertEqual(([], None, None, None), getargspec(f))

f()
self.assertEqual(1, self.registry.get_sample_value('s_count'))
self.assertTrue(.05 < self.registry.get_sample_value('s_sum') < 0.1)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


async def test_async_function_decorator(self):
self.assertEqual(0, self.registry.get_sample_value('s_count'))

@self.summary.time()
async def f():
await asyncio.sleep(.05)

self.assertEqual(([], None, None, None), getargspec(f))

await f()
self.assertEqual(1, self.registry.get_sample_value('s_count'))
self.assertTrue(.05 < self.registry.get_sample_value('s_sum') < 0.1)

def test_function_decorator_multithread(self):
self.assertEqual(0, self.registry.get_sample_value('s_count'))
Expand Down Expand Up @@ -343,7 +394,7 @@ def manager():
assert_not_observable(manager)


class TestHistogram(unittest.TestCase):
class TestHistogram(aiounittest.AsyncTestCase):
def setUp(self):
self.registry = CollectorRegistry()
self.histogram = Histogram('h', 'help', registry=self.registry)
Expand Down Expand Up @@ -417,13 +468,29 @@ def test_function_decorator(self):

@self.histogram.time()
def f():
pass
time.sleep(.05)

self.assertEqual(([], None, None, None), getargspec(f))

f()
self.assertEqual(1, self.registry.get_sample_value('h_count'))
self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '+Inf'}))
self.assertTrue(.05 < self.registry.get_sample_value('h_sum') < 0.1)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


async def test_async_function_decorator(self):
self.assertEqual(0, self.registry.get_sample_value('h_count'))
self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '+Inf'}))

@self.histogram.time()
async def f():
await asyncio.sleep(.05)

self.assertEqual(([], None, None, None), getargspec(f))

await f()
self.assertEqual(1, self.registry.get_sample_value('h_count'))
self.assertEqual(1, self.registry.get_sample_value('h_bucket', {'le': '+Inf'}))
self.assertTrue(.05 < self.registry.get_sample_value('h_sum') < 0.1)

def test_function_decorator_multithread(self):
self.assertEqual(0, self.registry.get_sample_value('h_count'))
Expand Down Expand Up @@ -527,7 +594,7 @@ def test_labels(self):
self.assertRaises(ValueError, self.labels.state, 'a')

def test_overlapping_labels(self):
with pytest.raises(ValueError):
with self.assertRaises(ValueError):
Enum('e', 'help', registry=None, labelnames=['e'])


Expand Down Expand Up @@ -568,7 +635,7 @@ def test_incorrect_label_count_raises(self):
self.assertRaises(ValueError, self.counter.remove, 'a', 'b')

def test_labels_on_labels(self):
with pytest.raises(ValueError):
with self.assertRaises(ValueError):
self.counter.labels('a').labels('b')

def test_labels_coerced_to_string(self):
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ envlist = coverage-clean,py3.6,py3.7,py3.8,py3.9,py3.10,pypy3.7,py3.9-nooptional
deps =
coverage
pytest
aiounittest
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unittest.IsolatedAsyncioTestCase is available for python 3.8+, so this module is needed to support async version of unittest.TestCase.

attrs

[testenv]
Expand Down
0