Profiling applications with decorators - how to improve decorator names

I have been using some of the typical profiling tools to profile my web application - cprofile, yappi, etc.
The application has a fair amount of decorator reuse across the application, but one side effect of this reuse is that profiles can often yield confusing output.

A common pattern is to use wrapped_fn, decorator, or similar name for the wrapping function. Functools can help at least pass through the docstring and the decorator name in certain use cases, but it does not seem to resolve issues with profiling.

@wraps(fn)
def wrapped_fn(*args, **kwargs):

### Profile will lump together all calls to wrapped_fn, callees will show a large amount of functions 
### This distorts the profile and if we visualize it, the profile can be quite messy with all arrows pointing back to the decorator


^ snippet of the profile with gprof2dot showing the decorator problem in profiling

The only solution I have found is to dive into the name and code property to append the wrapped function name to the decorator name:

traceable_name  = f"wrapped_fn_{fn.__name__}"
wrapped_fn.__name__ = traceable_name ### PATCH - better profiling resolution
wrapped_fn.__code__ = wrapped_fn.__code__.replace(co_name=traceable_name)

This declutters the visualization, and I think makes analysis a bit simpler.
We can still analyze the impact of the decorators by examining total time of the decorator prefixed functions. It also removes the callee/caller loops that clutter visualizations and make pstat usage much more difficult.

Could not embed this one due to my new user status :frowning:
upload://AnWwKAXVLrrZCilC5F5yvoTb9M5.jpeg

Is there a more standard solution for handling profiles with lots of decorator reuse? Reaching into the code property seems like it could be risky or fragile.