This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Dependency injection failure for unhashable types #1985
Comments
Depends by default cache the dependency. Can you check if from fastapi import FastAPI
app = FastAPI()
class MySettings(BaseSettings):
password: SecretStr
base_url: AnyHttpUrl = "https://www.google.com"
localdownload_dir: str = "~/mydir-import"
class Config:
env_prefix = "my_prefix"
def get_settings() -> MySettings:
return MySettings()
@app.get("/")
def read_root(settings:MySettings = Depends(get_settings,use_cache=False)):
return {"Hello": "World"} works? |
No, it keeps failing unfortunately |
Can you please explain what you're trying to achieve? i just tested it myself, and i don't get any error. what version of FastAPI and pydantic are you using? |
Damn, I had an @lru_cache on top of a method, that was causing the caching |
Thanks for the help here @syniex ! 👏 🙇 Thanks for reporting back and closing the issue @edmondo1984 👍 |
This documentation section shows that we can use a dependency with Should the docs be updated to remove the usage of |
Just ran into the same issue. Looks like I guess, in general, it makes sense to document the use of Anyway, we can work around that issue for now by either let def get_settings() -> config.Settings:
cache = getattr(get_settings, "__cached", None)
if not cache:
settings = config.Settings()
get_settings.__cache = settings
return get_settings.__cache |
Nevermind, I found the issue for my case. ...
@lru_cache()
def get_settings() -> config.Settings:
return config.Settings()
@lru_cache() # this does not work, due to the unhashable argument
def get_foo(
settings: config.Settings = Depends(get_settings), # noqa: B008
) -> Foo:
return Foo(settings.bar)
@app.get("/foo")
def read_foo(foo: Foo = Depends(get_foo), # noqa: B008) :
...
... So, the error is raised as soon as |
@escaped The argument to |
To clarify for anyone asking the @cbenz question.
No, |
In case anybody else runs into the same problem, I transformed @escaped 's solution into a decorator, with some additional input from various google searches. So far, it seems to fix the problem. FN = TypeVar('FN', bound=Callable)
def do_once(fn: FN) -> FN:
lock = Lock()
sentinel = object() # in case the wrapped method returns None
cache = sentinel
@wraps(fn)
def inner(*args, **kwargs):
nonlocal cache
with lock:
if cache is sentinel:
cache = fn(*args, **kwargs)
return cache
return cast(FN, inner)
@do_once
def get_settings() -> config.Settings:
config.Settings() |
A simple way to fix the issue, at least in some cases: class Settings(BaseSettings):
class Config:
frozen = True When defining your Settings class, define it as frozen. This way, it will be hashable, so other deps functions requiring a Settings objects can themselves have a lru_cache. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Example
Suppose you run the following script
export MY_PREFIX_PASSWORD=hello
and then
Description
When you invoke via curl the rest endpoint, you get the following failure:
File "/venv/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 390, in run_asgi result = await app(self.scope, self.receive, self.send) File "/venv/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__ return await self.app(scope, receive, send) File "/venv/lib/python3.7/site-packages/fastapi/applications.py", line 179, in __call__ await super().__call__(scope, receive, send) File "/venv/lib/python3.7/site-packages/starlette/applications.py", line 111, in __call__ await self.middleware_stack(scope, receive, send) File "/venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 181, in __call__ raise exc from None File "/venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 159, in __call__ await self.app(scope, receive, _send) File "/venv/lib/python3.7/site-packages/starlette/exceptions.py", line 82, in __call__ raise exc from None File "/venv/lib/python3.7/site-packages/starlette/exceptions.py", line 71, in __call__ await self.app(scope, receive, sender) File "/venv/lib/python3.7/site-packages/starlette/routing.py", line 566, in __call__ await route.handle(scope, receive, send) File "/venv/lib/python3.7/site-packages/starlette/routing.py", line 227, in handle await self.app(scope, receive, send) File "/venv/lib/python3.7/site-packages/starlette/routing.py", line 41, in app response = await func(request) File "/venv/lib/python3.7/site-packages/fastapi/routing.py", line 176, in app dependency_overrides_provider=dependency_overrides_provider, File "/venv/lib/python3.7/site-packages/fastapi/dependencies/utils.py", line 552, in solve_dependencies solved = await run_in_threadpool(call, **sub_values) File "/venv/lib/python3.7/site-packages/starlette/concurrency.py", line 34, in run_in_threadpool return await loop.run_in_executor(None, func, *args) File "/usr/local/lib/python3.7/concurrent/futures/thread.py", line 57, in run result = self.fn(*self.args, **self.kwargs) TypeError: unhashable type: 'MySettings'
Environment
The text was updated successfully, but these errors were encountered: