Skip to content

✨ Make Depends and Security hashable again#14320

Closed
zmievsa wants to merge 1 commit into
fastapi:masterfrom
zmievsa:master
Closed

✨ Make Depends and Security hashable again#14320
zmievsa wants to merge 1 commit into
fastapi:masterfrom
zmievsa:master

Conversation

@zmievsa

@zmievsa zmievsa commented Nov 9, 2025

Copy link
Copy Markdown

Cadwyn is currently broken on new FastAPI versions because Depends and Security have previously been hashable and we relied on this in our logic.

Now they are unhashable dataclasses and I am a little stuck with how could fix it in Cadwyn. So I propose to make them hashable again. We have three options:

  1. frozen=True, eq=True which will work but can be a breaking change for some users of FastAPI (though I don't expect it to be a problem for most) because it will become frozen
  2. unsafe_hash=True which will work and shouldn't be a breaking change but having an object with a mutable hash is probably not a great idea
  3. def __hash__(self): return super().__hash__() which should be roughly equivalent to prior behavior where there was a hash but it was the default object hash instead of anything meaningful

Any of these options would fix Cadwyn. Are you open to any of them?

@YuriiMotov

Copy link
Copy Markdown
Member

I think frozen=True would be best here.

frozen=True, eq=True which will work but can be a breaking change for some users of FastAPI

@zmievsa, what use cases do you have in mind? I mean for which it might be breaking change

@zmievsa-semasoftware

Copy link
Copy Markdown

I am quite comfortable with frozen=true for my use cases.

I only worry about frameworks based on fastapi that modify depends/security for some reason. But I don't know any frameworks like this

@YuriiMotov YuriiMotov changed the title Make Depends and Security hashable again ✨ Make Depends and Security hashable again Nov 13, 2025
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has a merge conflict that needs to be resolved.

@github-actions github-actions Bot added the conflicts Automatically generated when a PR has a merge conflict label Nov 19, 2025
@tiangolo

Copy link
Copy Markdown
Member

Thank you! I went with the frozen=True with the required refactor. I also added tests to ensure they keep hashable. I did that here: #14372

It's now released in 0.121.3 🎉


Just as a note, FastAPI doesn't really have official support for extending its internals in ways that are not documented in the docs.

So this is not a long-term commitment to keep some specific behavior, but a quick workaround to make things easier for current tools that interact with FastAPI internals in some way.


At some point in the future, I'll refactor the internals of FastAPI to make them more explicitly private, and then also figure out the right ways to expose any points of connection with external components that currently need to interact with the internals in some way, and formalize that better.


Before that, I need to handle a few issues, bugs, features, refactors, deprecations, etc. And then I'll be able to come back to this idea of figuring out how and what to expose. 🤓


Given that, I'll now close this one. Thank you! ☕

@tiangolo tiangolo closed this Nov 19, 2025
@zmievsa

zmievsa commented Nov 19, 2025

Copy link
Copy Markdown
Author

Thanks, @tiangolo !

I remember discussing it with you so it's all clear. However, I will compile a list of "here's what I use from fastapi internals" so that it's easier for you to make judgements when you decide to make the refactor and to make some of the internal parts a part of the public interface ❤️

@tiangolo

Copy link
Copy Markdown
Member

I remember discussing it with you so it's all clear. However, I will compile a list of "here's what I use from fastapi internals" so that it's easier for you to make judgements when you decide to make the refactor and to make some of the internal parts a part of the public interface ❤️

This would be great, thank you @zmievsa! 🙌

If you could put your notes on a discussion, that would be super helpful. And @YuriiMotov will surely help me keep track of it so I don't forget. 😅

@zmievsa

zmievsa commented Nov 29, 2025

Copy link
Copy Markdown
Author

@tiangolo @YuriiMotov

  • fastapi._compat.ModelField -- we use it as type hint because we use body_field
  • fastapi._compat._normalize_errors -- we use it because we are mimicking fastapi's request validation but we do validation for each API version separately
  • fastapi._compat.get_compat_model_name_map, fastapi._compat.get_definitions, -- we use it in changelog generation between API versions because it is a super convenient way to use fastapi's internal metadata on endpoints
  • fastapi.routing._prepare_response_content -- we do this because we pre-process response before it gets to fastapi but we also want the response body to look exactly the same as if it was already processed by fastapi. This is because we do API migrations on response before it reaches fastapi
  • fastapi.dependencies.utils.solve_dependencies -- we solve dependencies because we want to make sure that everything works well after we applied all API migrations
  • fastapi.dependencies.models.Dependant -- we use dependants from routes directly for solving dependencies so we need this for typehinting
  • fastapi.utils.create_model_field -- after migrating route type hints to a different API version, we use this to recreate the model field
  • fastapi.utils.create_cloned_field -- same as create_model_field
  • We also use route.dependant, route.body_field, route.response_field, route.secure_cloned_response_field but these look like they are public already
  • route._embed_body_fields -- we use it when re-creating routes when migrating them between API versions
  • fastapi.dependencies.models has a bunch of functions that I was forced to copy because they are private in FastAPI. We use them for the same reasons fastapi uses them:
    • is_gen_callable
    • is_async_gen_callable
    • is_coroutine_callable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflicts Automatically generated when a PR has a merge conflict feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants