Skip to content

Support async functions in metric decorators - #1242

Open
siddham-jain wants to merge 1 commit into
prometheus:masterfrom
siddham-jain:support-async-decorators
Open

siddham-jain wants to merge 1 commit into
prometheus:masterfrom
siddham-jain:support-async-decorators

Conversation

@siddham-jain

Copy link
Copy Markdown

@csmarchbanks

Fixes #535.

Decorating an async def function with Histogram.time(), Summary.time(), Gauge.time(), Counter.count_exceptions() or Gauge.track_inprogress() currently wraps only the creation of the coroutine, not its execution. So the decorators silently record wrong data, e.g. a FastAPI endpoint that awaits for 100ms is observed as ~1µs, raised exceptions are not counted, and the in-progress gauge is back to 0 before the body runs. The wrapper is also no longer a coroutine function, so inspect.iscoroutinefunction() returns False for it.

Changes:

  • Backport coroutine support from upstream decorator 4.1 into the bundled decorator.py: FunctionMaker.create generates an async def wrapper with return await when the caller is a coroutine function (and make uses DEF.search so that template parses). Signatures stay preserved as before.
  • context_managers.py uses an async caller for coroutine functions, so the with block spans the awaited call. Sync functions take the same path as before.
  • Tests for all five decorators on async def functions, including overlapping calls via asyncio.gather, plus a note in the instrumenting docs.

This picks up the direction discussed in #535 now that Python 2 is gone, and supersedes the stalled #767, whose tests cover the same cases.

Signed-off-by: Siddham Jain <siddhamjain224@gmail.com>
@squat

squat commented Oct 28, 2026

Copy link
Copy Markdown

yay, this is a great start for implementing Async support here

@chrikrah chrikrah left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@siddham-jain async tests fail on base, signatures survive. Mergeable after a rebase.

# git archive 40b8532, Python 3.12.3, prometheus_client imported from the export
$ python -m pytest tests -q -p no:cacheprovider --ignore=tests/test_asgi.py --ignore=tests/test_twisted.py --ignore=tests/test_aiohttp.py
421 passed, 2 skipped, 28 subtests passed
$ python -m pytest tests/test_core.py -q -p no:cacheprovider   # both files from base ff9a2d3
5 failed, 120 passed   # the five *async* decorator tests
$ python -I probe.py   # @h.time() on: async def f(a, b=2, *, c)
(a, b=2, *, c) True f   # inspect.signature, iscoroutinefunction, __name__
$ git merge-tree --write-tree --name-only origin/master 40b8532   # master e2f2bf9
CONFLICT (content): Merge conflict in tests/test_core.py
# not run: Python 3.9 to 3.11, the three ignored suites, the suite on a merge with master

non-blocking: keep import math (#1225) and import inspect.

Async series jump from ~1µs to real latency. Worth an upgrade note?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async context manager

3 participants