Skip to content
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

Beeline tracing decorator does preserve function signature #197

Closed
jwelch92 opened this issue Dec 6, 2021 · 3 comments
Closed

Beeline tracing decorator does preserve function signature #197

jwelch92 opened this issue Dec 6, 2021 · 3 comments
Labels
type: bug Something isn't working

Comments

@jwelch92
Copy link

jwelch92 commented Dec 6, 2021

Versions

  • Python: 3.8.9 (observed on other versions including 3.6.x)
  • Beeline: 2.16.x and 2.17.x

Steps to reproduce

Code snippet showing the issue

from typing import Optional, Callable

import beeline

import inspect


@beeline.traced("async_beeline_changes_signature")
async def example_async_traced(a: str, b: str, c: Optional[str] = None) -> None:
    pass


async def example_async_not_traced(a: str, b: str, c: Optional[str] = None) -> None:
    pass


@beeline.traced("beeline_changes_signature")
def example_traced(a: str, b: str, c: Optional[str] = None) -> None:
    pass


def example_not_traced(a: str, b: str, c: Optional[str] = None) -> None:
    pass


def display_argspec(fn: Callable) -> None:
    arg_spec = inspect.getfullargspec(fn)
    sig = inspect.signature(fn)
    print(f"Example function: {fn.__name__}")
    print("Full Arg Spec:", arg_spec)
    print("Signature:", sig)
    print()


if __name__ == "__main__":
    display_argspec(example_async_traced)
    display_argspec(example_not_traced)
    display_argspec(example_traced)
    display_argspec(example_not_traced)

With output

Example function: example_async_traced
Full Arg Spec: FullArgSpec(args=[], varargs='args', varkw='kwargs', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={'return': None})
Signature: (a: str, b: str, c: Union[str, NoneType] = None) -> None

Example function: example_not_traced
Full Arg Spec: FullArgSpec(args=['a', 'b', 'c'], varargs=None, varkw=None, defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={'return': None, 'a': <class 'str'>, 'b': <class 'str'>, 'c': typing.Union[str, NoneType]})
Signature: (a: str, b: str, c: Union[str, NoneType] = None) -> None

Example function: example_traced
Full Arg Spec: FullArgSpec(args=[], varargs='args', varkw='kwargs', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={'return': None})
Signature: (a: str, b: str, c: Union[str, NoneType] = None) -> None

Example function: example_not_traced
Full Arg Spec: FullArgSpec(args=['a', 'b', 'c'], varargs=None, varkw=None, defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={'return': None, 'a': <class 'str'>, 'b': <class 'str'>, 'c': typing.Union[str, NoneType]})
Signature: (a: str, b: str, c: Union[str, NoneType] = None) -> None

Additional context

I am using beeline for tracing a Slack application that is using Slack Bolt which uses kwargs injection for handlers. This uses inspect.getfullargspec in order to get the list of arguments to inject as seen here.

This might not be an issue if I could reorder the decorators but unfortunately due to some constraints imposed by Bolt that's not currently possible so I am looking to fix the decorator behavior in beeline.

Good resource on decorators that I leaned on for narrowing down this issue: https://hynek.me/articles/decorators/.

@MikeGoldsmith
Copy link
Contributor

Hey @jwelch92 - thanks for the bug report.

I've read through the issue and linked blog post and understand the problem - I really appreciated the links 👍🏻

I was going to suggest that you try instrumenting with OpenTelemetry Python - but as you can see they also use functools.wraps.

I think we need to do additional testing, but wrapt feels like the most concise fix and by virtue does support Python 2.7 which is a consideration for us where we try to support older versions of Python util something breaks.

Thanks for your PR to help with that process.

@MikeGoldsmith MikeGoldsmith added version: bump minor A PR that adds behavior, but is backwards-compatible. and removed version: bump minor A PR that adds behavior, but is backwards-compatible. labels Dec 8, 2021
@JamieDanielson JamieDanielson added the status: info needed Further information is requested. label Dec 9, 2021
@vreynolds vreynolds removed the status: info needed Further information is requested. label Dec 22, 2021
@vreynolds vreynolds removed their assignment Dec 22, 2021
@pkanal
Copy link
Contributor

pkanal commented Aug 19, 2022

Hello,
We are trimming our OSS backlog. We will be closing this issue as it is a low priority for us to fix. It is unlikely that we'll ever get to it, and so we'd like to set expectations accordingly.

If this issue is important to you, please feel free to ping here and we can discuss/re-open.

@pkanal pkanal closed this as not planned Won't fix, can't repro, duplicate, stale Aug 19, 2022
@mpcusack-color
Copy link

mpcusack-color commented Oct 26, 2022

I am running into essentially this same issue but specifically found the @beeline.traced silently strips off mypy type annotations, which is a major problem in our codebase. After all the work we've put in adding type annotators, mypy sees any beeline decorated function as returning Any.

I haven't looked at whether the OpenTelemetry client fixes this or not, but we are unable to do that upgrade at the moment. For anyone else finding this in the future we were able to replace all usages of @beeline.traced with:

BeelineTracedParamSpec = ParamSpec('BeelineTracedParamSpec')
BeelineTracedReturn = TypeVar('BeelineTracedReturn')


def beeline_traced(
    name: str,
) -> Callable[
    [Callable[BeelineTracedParamSpec, BeelineTracedReturn]],
    Callable[BeelineTracedParamSpec, BeelineTracedReturn],
]:
    """Applies the @beeline.traced decorator, but maintains typing information.

    This is a known beeline issues which they do not intend to fix.
    https://github.com/honeycombio/beeline-python/issues/197

    Hopefully this sort of hack will not be needed once we switch to the OpenTelemetry
    client. Until then we deprecated usages of beeline.traced in favor of this method
    which casts the decorator to have the have the right types.
    """
    return cast(
        Callable[
            [Callable[BeelineTracedParamSpec, BeelineTracedReturn]],
            Callable[BeelineTracedParamSpec, BeelineTracedReturn],
        ],
        beeline.traced(name=name),
    )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants