-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Add type annotations to trace
decorator
#13328
Changes from all commits
c6914ee
893cb52
52318fb
fd0d962
245eb11
725b44a
5e99e74
244c719
99f35bb
65a7218
cde9700
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add type hints to `trace` decorator. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,14 +84,13 @@ def interesting_function(*args, **kwargs): | |
return something_usual_and_useful | ||
|
||
|
||
Operation names can be explicitly set for a function by passing the | ||
operation name to ``trace`` | ||
Operation names can be explicitly set for a function by using ``trace_with_opname``: | ||
|
||
.. code-block:: python | ||
|
||
from synapse.logging.opentracing import trace | ||
from synapse.logging.opentracing import trace_with_opname | ||
|
||
@trace(opname="a_better_operation_name") | ||
@trace_with_opname("a_better_operation_name") | ||
def interesting_badly_named_function(*args, **kwargs): | ||
# Does all kinds of cool and expected things | ||
return something_usual_and_useful | ||
|
@@ -798,33 +797,31 @@ def extract_text_map(carrier: Dict[str, str]) -> Optional["opentracing.SpanConte | |
# Tracing decorators | ||
|
||
|
||
def trace(func=None, opname: Optional[str] = None): | ||
def trace_with_opname(opname: str) -> Callable[[Callable[P, R]], Callable[P, R]]: | ||
""" | ||
Decorator to trace a function. | ||
Sets the operation name to that of the function's or that given | ||
as operation_name. See the module's doc string for usage | ||
examples. | ||
Decorator to trace a function with a custom opname. | ||
|
||
See the module's doc string for usage examples. | ||
|
||
""" | ||
|
||
def decorator(func): | ||
def decorator(func: Callable[P, R]) -> Callable[P, R]: | ||
if opentracing is None: | ||
return func # type: ignore[unreachable] | ||
|
||
_opname = opname if opname else func.__name__ | ||
|
||
if inspect.iscoroutinefunction(func): | ||
|
||
@wraps(func) | ||
async def _trace_inner(*args, **kwargs): | ||
with start_active_span(_opname): | ||
return await func(*args, **kwargs) | ||
async def _trace_inner(*args: P.args, **kwargs: P.kwargs) -> R: | ||
with start_active_span(opname): | ||
return await func(*args, **kwargs) # type: ignore[misc] | ||
|
||
else: | ||
# The other case here handles both sync functions and those | ||
# decorated with inlineDeferred. | ||
@wraps(func) | ||
def _trace_inner(*args, **kwargs): | ||
scope = start_active_span(_opname) | ||
def _trace_inner(*args: P.args, **kwargs: P.kwargs) -> R: | ||
scope = start_active_span(opname) | ||
scope.__enter__() | ||
|
||
try: | ||
|
@@ -858,12 +855,21 @@ def err_back(result: R) -> R: | |
scope.__exit__(type(e), None, e.__traceback__) | ||
raise | ||
|
||
return _trace_inner | ||
return _trace_inner # type: ignore[return-value] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the error here, out of interest? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Actually now that I split this up I might be able to do simple overloads with returning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure that's possible actually. Any idea if we should do something about this one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As sad as I am to leave a typing problem lying around like this, I think I'd probably leave the ignore as is. The important thing is that we accurately annotate the decorator itself; its internals don't have an effect on the rest of the function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This was kind of my conclusion after messing with it for a bit. |
||
|
||
if func: | ||
return decorator(func) | ||
else: | ||
return decorator | ||
return decorator | ||
|
||
|
||
def trace(func: Callable[P, R]) -> Callable[P, R]: | ||
""" | ||
Decorator to trace a function. | ||
|
||
Sets the operation name to that of the function's name. | ||
|
||
See the module's doc string for usage examples. | ||
""" | ||
|
||
return trace_with_opname(func.__name__)(func) | ||
|
||
|
||
def tag_args(func: Callable[P, R]) -> Callable[P, R]: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For completeness this is ignoring:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I think this is fine: within this branch,
func
is a coroutine so should return an awaitable. It's a shame mypy can't deduce this for itself, given the use ofTypeGuard
in typeshed.Oh, but that bit of typeshed I quoted only just landed this morning! So we might find that this ignore is unnecessary in a future mypy release.