Depends doesn't unwrap decorators to know if the callable is a function or a generator #9885
Replies: 3 comments 6 replies
-
|
Thanks so much for fielding this on behalf of @beartype, @gabrieldemarmiesse. As always, you amaze and stun in equal measure. To corroborate that excellent synopsis above, it seems likely (but not certain) that FastAPI is failing to unwrap wrappers created by the standard Thankfully, the solution is super-trivial. Well... assuming this is FastAPI's fault, anyway. This might very well be @beartype's fault, but I suspect it's probably not. Anyway, here's a trivial well-tested utility function in the @beartype codebase that we personally use to unwrap wrappers. Catch! from collections.abc import Callable
from typing import Any
def unwrap_func_all(func: Any) -> Callable:
'''
Lowest-level **wrappee** (i.e., callable wrapped by the passed wrapper
callable) of the passed higher-level **wrapper** (i.e., callable wrapping
the wrappee callable to be returned) if the passed callable is a wrapper
*or* that callable as is otherwise (i.e., if that callable is *not* a
wrapper).
Specifically, this getter iteratively undoes the work performed by:
* One or more consecutive uses of the :func:`functools.wrap` decorator on
the wrappee callable to be returned.
* One or more consecutive calls to the :func:`functools.update_wrapper`
function on the wrappee callable to be returned.
Parameters
----------
func : Callable
Wrapper callable to be unwrapped.
Returns
----------
Callable
Either:
* If the passed callable is a wrapper, the lowest-level wrappee
callable wrapped by that wrapper.
* Else, the passed callable as is.
'''
# While this callable still wraps another callable, unwrap one layer of
# wrapping by reducing this wrapper to its next wrappee.
while hasattr(func, '__wrapped__'):
func = func.__wrapped__ # type: ignore[attr-defined]
# Return this wrappee, which is now guaranteed to *NOT* be a wrapper.
return funcAs everyone just noticed, @beartype errs on the side of volubility. It's a character flaw. 🥲 |
Beta Was this translation helpful? Give feedback.
-
|
Can you show me an example which is supposed to work without Also, which are the cases it fails? The title says "function or a generator", which I guess is not every function... |
Beta Was this translation helpful? Give feedback.
-
|
I think this is related the issue i'm having where I want to wrap some of my dependencies in a decorator, which is causing FastAPI to run them in a sync thread, instead of the desired async context. My general use case is wanting to add a quick tracing decorator (from datadog's |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
First Check
Commit to Help
Example Code
Description
Run this script with the latest version of the fastapi and beartype packages. Here are the versions I have locally:
If you remove the beartype decorator, you will notice that everything works fine. It seems fastapi can't find out if the original function is a plain function or a generator.
I raised an issue in the beartype repo and the maintainer @leycec believe it's very likely fastapi that can't inspect the callable given to
Depends()correctly. See the original issue here: beartype/beartype#250Operating System
Linux
Operating System Details
ubuntu 18
FastAPI Version
fastapi==0.100.0
Python Version
Python 3.10.10
Additional Context
No response
Beta Was this translation helpful? Give feedback.
All reactions