mirror of
https://github.com/fastapi/fastapi.git
synced 2026-02-11 23:01:30 -05:00
Compare commits
1 Commits
master
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71287bd213 |
9
.github/workflows/test-redistribute.yml
vendored
9
.github/workflows/test-redistribute.yml
vendored
@@ -12,6 +12,11 @@ on:
|
||||
jobs:
|
||||
test-redistribute:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package:
|
||||
- fastapi
|
||||
- fastapi-slim
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
@@ -25,6 +30,8 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: pip install build
|
||||
- name: Build source distribution
|
||||
env:
|
||||
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
|
||||
run: python -m build --sdist
|
||||
- name: Decompress source distribution
|
||||
run: |
|
||||
@@ -34,6 +41,8 @@ jobs:
|
||||
run: |
|
||||
cd dist/fastapi*/
|
||||
pip install --group tests --editable .[all]
|
||||
env:
|
||||
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
|
||||
- name: Run source distribution tests
|
||||
run: |
|
||||
cd dist/fastapi*/
|
||||
|
||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@@ -56,10 +56,14 @@ jobs:
|
||||
- starlette-pypi
|
||||
- starlette-git
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.9"
|
||||
coverage: coverage
|
||||
uv-resolution: lowest-direct
|
||||
- os: macos-latest
|
||||
python-version: "3.10"
|
||||
coverage: coverage
|
||||
uv-resolution: lowest-direct
|
||||
uv-resolution: highest
|
||||
- os: windows-latest
|
||||
python-version: "3.12"
|
||||
coverage: coverage
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# Advanced Python Types { #advanced-python-types }
|
||||
|
||||
Here are some additional ideas that might be useful when working with Python types.
|
||||
|
||||
## Using `Union` or `Optional` { #using-union-or-optional }
|
||||
|
||||
If your code for some reason can't use `|`, for example if it's not in a type annotation but in something like `response_model=`, instead of using the vertical bar (`|`) you can use `Union` from `typing`.
|
||||
|
||||
For example, you could declare that something could be a `str` or `None`:
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
`typing` also has a shortcut to declare that something could be `None`, with `Optional`.
|
||||
|
||||
Here's a tip from my very **subjective** point of view:
|
||||
|
||||
* 🚨 Avoid using `Optional[SomeType]`
|
||||
* Instead ✨ **use `Union[SomeType, None]`** ✨.
|
||||
|
||||
Both are equivalent and underneath they are the same, but I would recommend `Union` instead of `Optional` because the word "**optional**" would seem to imply that the value is optional, and it actually means "it can be `None`", even if it's not optional and is still required.
|
||||
|
||||
I think `Union[SomeType, None]` is more explicit about what it means.
|
||||
|
||||
It's just about the words and names. But those words can affect how you and your teammates think about the code.
|
||||
|
||||
As an example, let's take this function:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def say_hi(name: Optional[str]):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter:
|
||||
|
||||
```Python
|
||||
say_hi() # Oh, no, this throws an error! 😱
|
||||
```
|
||||
|
||||
The `name` parameter is **still required** (not *optional*) because it doesn't have a default value. Still, `name` accepts `None` as the value:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # This works, None is valid 🎉
|
||||
```
|
||||
|
||||
The good news is, in most cases, you will be able to simply use `|` to define unions of types:
|
||||
|
||||
```python
|
||||
def say_hi(name: str | None):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
So, normally you don't have to worry about names like `Optional` and `Union`. 😎
|
||||
@@ -14,7 +14,7 @@ In a hurry and already know this stuff? Jump to the [`Dockerfile` below 👇](#b
|
||||
<summary>Dockerfile Preview 👀</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.14
|
||||
FROM python:3.9
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
@@ -166,7 +166,7 @@ Now in the same project directory create a file `Dockerfile` with:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)!
|
||||
FROM python:3.14
|
||||
FROM python:3.9
|
||||
|
||||
# (2)!
|
||||
WORKDIR /code
|
||||
@@ -390,7 +390,7 @@ If your FastAPI is a single file, for example, `main.py` without an `./app` dire
|
||||
Then you would just have to change the corresponding paths to copy the file inside the `Dockerfile`:
|
||||
|
||||
```{ .dockerfile .annotate hl_lines="10 13" }
|
||||
FROM python:3.14
|
||||
FROM python:3.9
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
@@ -499,7 +499,7 @@ Of course, there are **special cases** where you could want to have **a containe
|
||||
In those cases, you can use the `--workers` command line option to set the number of workers that you want to run:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
FROM python:3.14
|
||||
FROM python:3.9
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
||||
@@ -135,30 +135,27 @@ You can use, for example:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
|
||||
|
||||
### `typing` module { #typing-module }
|
||||
### Generic types with type parameters { #generic-types-with-type-parameters }
|
||||
|
||||
For some additional use cases, you might need to import some things from the standard library `typing` module, for example when you want to declare that something has "any type", you can use `Any` from `typing`:
|
||||
There are some data structures that can contain other values, like `dict`, `list`, `set` and `tuple`. And the internal values can have their own type too.
|
||||
|
||||
```python
|
||||
from typing import Any
|
||||
These types that have internal types are called "**generic**" types. And it's possible to declare them, even with their internal types.
|
||||
|
||||
To declare those types and the internal types, you can use the standard Python module `typing`. It exists specifically to support these type hints.
|
||||
|
||||
def some_function(data: Any):
|
||||
print(data)
|
||||
```
|
||||
#### Newer versions of Python { #newer-versions-of-python }
|
||||
|
||||
### Generic types { #generic-types }
|
||||
The syntax using `typing` is **compatible** with all versions, from Python 3.6 to the latest ones, including Python 3.9, Python 3.10, etc.
|
||||
|
||||
Some types can take "type parameters" in square brackets, to define their internal types, for example a "list of strings" would be declared `list[str]`.
|
||||
As Python advances, **newer versions** come with improved support for these type annotations and in many cases you won't even need to import and use the `typing` module to declare the type annotations.
|
||||
|
||||
These types that can take type parameters are called **Generic types** or **Generics**.
|
||||
If you can choose a more recent version of Python for your project, you will be able to take advantage of that extra simplicity.
|
||||
|
||||
You can use the same builtin types as generics (with square brackets and types inside):
|
||||
In all the docs there are examples compatible with each version of Python (when there's a difference).
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
For example "**Python 3.6+**" means it's compatible with Python 3.6 or above (including 3.7, 3.8, 3.9, 3.10, etc). And "**Python 3.9+**" means it's compatible with Python 3.9 or above (including 3.10, etc).
|
||||
|
||||
If you can use the **latest versions of Python**, use the examples for the latest version, those will have the **best and simplest syntax**, for example, "**Python 3.10+**".
|
||||
|
||||
#### List { #list }
|
||||
|
||||
@@ -223,20 +220,44 @@ This means:
|
||||
|
||||
You can declare that a variable can be any of **several types**, for example, an `int` or a `str`.
|
||||
|
||||
To define it you use the <dfn title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</dfn> to separate both types.
|
||||
In Python 3.6 and above (including Python 3.10) you can use the `Union` type from `typing` and put inside the square brackets the possible types to accept.
|
||||
|
||||
This is called a "union", because the variable can be anything in the union of those two sets of types.
|
||||
In Python 3.10 there's also a **new syntax** where you can put the possible types separated by a <dfn title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</dfn>.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
This means that `item` could be an `int` or a `str`.
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
In both cases this means that `item` could be an `int` or a `str`.
|
||||
|
||||
#### Possibly `None` { #possibly-none }
|
||||
|
||||
You can declare that a value could have a type, like `str`, but that it could also be `None`.
|
||||
|
||||
In Python 3.6 and above (including Python 3.10) you can declare it by importing and using `Optional` from the `typing` module.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
Using `Optional[str]` instead of just `str` will let the editor help you detect errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
|
||||
|
||||
`Optional[Something]` is actually a shortcut for `Union[Something, None]`, they are equivalent.
|
||||
|
||||
This also means that in Python 3.10, you can use `Something | None`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
@@ -245,7 +266,96 @@ You can declare that a value could have a type, like `str`, but that it could al
|
||||
|
||||
////
|
||||
|
||||
Using `str | None` instead of just `str` will let the editor help you detect errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ alternative
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### Using `Union` or `Optional` { #using-union-or-optional }
|
||||
|
||||
If you are using a Python version below 3.10, here's a tip from my very **subjective** point of view:
|
||||
|
||||
* 🚨 Avoid using `Optional[SomeType]`
|
||||
* Instead ✨ **use `Union[SomeType, None]`** ✨.
|
||||
|
||||
Both are equivalent and underneath they are the same, but I would recommend `Union` instead of `Optional` because the word "**optional**" would seem to imply that the value is optional, and it actually means "it can be `None`", even if it's not optional and is still required.
|
||||
|
||||
I think `Union[SomeType, None]` is more explicit about what it means.
|
||||
|
||||
It's just about the words and names. But those words can affect how you and your teammates think about the code.
|
||||
|
||||
As an example, let's take this function:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
|
||||
|
||||
The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter:
|
||||
|
||||
```Python
|
||||
say_hi() # Oh, no, this throws an error! 😱
|
||||
```
|
||||
|
||||
The `name` parameter is **still required** (not *optional*) because it doesn't have a default value. Still, `name` accepts `None` as the value:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # This works, None is valid 🎉
|
||||
```
|
||||
|
||||
The good news is, once you are on Python 3.10 you won't have to worry about that, as you will be able to simply use `|` to define unions of types:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
|
||||
|
||||
And then you won't have to worry about names like `Optional` and `Union`. 😎
|
||||
|
||||
#### Generic types { #generic-types }
|
||||
|
||||
These types that take type parameters in square brackets are called **Generic types** or **Generics**, for example:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
You can use the same builtin types as generics (with square brackets and types inside):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
And the same as with previous Python versions, from the `typing` module:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
|
||||
In Python 3.10, as an alternative to using the generics `Union` and `Optional`, you can use the <dfn title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</dfn> to declare unions of types, that's a lot better and simpler.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
You can use the same builtin types as generics (with square brackets and types inside):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
And generics from the `typing` module:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
|
||||
////
|
||||
|
||||
### Classes as types { #classes-as-types }
|
||||
|
||||
@@ -293,11 +403,17 @@ To learn more about <a href="https://docs.pydantic.dev/" class="external-link" t
|
||||
|
||||
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
/// tip
|
||||
|
||||
Pydantic has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Required Optional fields</a>.
|
||||
|
||||
///
|
||||
|
||||
## Type Hints with Metadata Annotations { #type-hints-with-metadata-annotations }
|
||||
|
||||
Python also has a feature that allows putting **additional <dfn title="Data about the data, in this case, information about the type, e.g. a description.">metadata</dfn>** in these type hints using `Annotated`.
|
||||
|
||||
You can import `Annotated` from `typing`.
|
||||
Since Python 3.9, `Annotated` is a part of the standard library, so you can import it from `typing`.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
|
||||
|
||||
|
||||
@@ -7,25 +7,10 @@ hide:
|
||||
|
||||
## Latest Changes
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* ➖ Drop support for Python 3.9. PR [#14897](https://github.com/fastapi/fastapi/pull/14897) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Refactors
|
||||
|
||||
* 🎨 Update internal types for Python 3.10. PR [#14898](https://github.com/fastapi/fastapi/pull/14898) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.128.8
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Fix grammar in `docs/en/docs/tutorial/first-steps.md`. PR [#14708](https://github.com/fastapi/fastapi/pull/14708) by [@SanjanaS10](https://github.com/SanjanaS10).
|
||||
|
||||
### Internal
|
||||
|
||||
* 🔨 Tweak PDM hook script. PR [#14895](https://github.com/fastapi/fastapi/pull/14895) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ♻️ Update build setup for `fastapi-slim`, deprecate it, and make it only depend on `fastapi`. PR [#14894](https://github.com/fastapi/fastapi/pull/14894) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.128.7
|
||||
|
||||
### Features
|
||||
|
||||
@@ -106,6 +106,13 @@ As, by default, singular values are interpreted as query parameters, you don't h
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Or in Python 3.9:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
|
||||
For example:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
|
||||
|
||||
@@ -155,7 +155,7 @@ The function parameters will be recognized as follows:
|
||||
|
||||
FastAPI will know that the value of `q` is not required because of the default value `= None`.
|
||||
|
||||
The `str | None` is not used by FastAPI to determine that the value is not required, it will know it's not required because it has a default value of `= None`.
|
||||
The `str | None` (Python 3.10+) or `Union` in `Union[str, None]` (Python 3.9+) is not used by FastAPI to determine that the value is not required, it will know it's not required because it has a default value of `= None`.
|
||||
|
||||
But adding the type annotations will allow your editor to give you better support and detect errors.
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ Now you can declare your dependency using this class.
|
||||
|
||||
Notice how we write `CommonQueryParams` twice in the above code:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -109,7 +109,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -137,7 +137,7 @@ It is from this one that FastAPI will extract the declared parameters and that i
|
||||
|
||||
In this case, the first `CommonQueryParams`, in:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, ...
|
||||
@@ -145,7 +145,7 @@ commons: Annotated[CommonQueryParams, ...
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -163,7 +163,7 @@ commons: CommonQueryParams ...
|
||||
|
||||
You could actually write just:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
@@ -171,7 +171,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -197,7 +197,7 @@ But declaring the type is encouraged as that way your editor will know what will
|
||||
|
||||
But you see that we are having some code repetition here, writing `CommonQueryParams` twice:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -205,7 +205,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -225,7 +225,7 @@ For those specific cases, you can do the following:
|
||||
|
||||
Instead of writing:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -233,7 +233,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -249,7 +249,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
|
||||
...you write:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends()]
|
||||
@@ -257,7 +257,7 @@ commons: Annotated[CommonQueryParams, Depends()]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ And it will save the returned value in a <dfn title="A utility/system to store c
|
||||
|
||||
In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
|
||||
@@ -71,7 +71,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ But if we put that in the assignment `response_model=PlaneItem | CarItem` we wou
|
||||
|
||||
The same way, you can declare responses of lists of objects.
|
||||
|
||||
For that, use the standard Python `list`:
|
||||
For that, use the standard Python `typing.List` (or just `list` in Python 3.9 and above):
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
|
||||
|
||||
@@ -200,7 +200,7 @@ You can also declare a response using a plain arbitrary `dict`, declaring just t
|
||||
|
||||
This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic model) beforehand.
|
||||
|
||||
In this case, you can use `dict`:
|
||||
In this case, you can use `typing.Dict` (or just `dict` in Python 3.9 and above):
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
|
||||
|
||||
|
||||
@@ -47,16 +47,40 @@ Now it's the time to use it with FastAPI. 🚀
|
||||
|
||||
We had this type annotation:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
What we will do is wrap that with `Annotated`, so it becomes:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None]] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Both of those versions mean the same thing, `q` is a parameter that can be a `str` or `None`, and by default, it is `None`.
|
||||
|
||||
Now let's jump to the fun stuff. 🎉
|
||||
|
||||
@@ -191,7 +191,6 @@ nav:
|
||||
- advanced/openapi-webhooks.md
|
||||
- advanced/wsgi.md
|
||||
- advanced/generate-clients.md
|
||||
- advanced/advanced-python-types.md
|
||||
- fastapi-cli.md
|
||||
- Deployment:
|
||||
- deployment/index.md
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<p align="center">
|
||||
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
<img src="https://github.com/fastapi/fastapi/actions/workflows/test.yml/badge.svg?event=push&branch=master" alt="Test">
|
||||
</a>
|
||||
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapi/fastapi" target="_blank">
|
||||
<img src="https://coverage-badge.samuelcolvin.workers.dev/fastapi/fastapi.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/pypi/pyversions/fastapi.svg?color=%2334D058" alt="Supported Python versions">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
|
||||
**Source Code**: <a href="https://github.com/fastapi/fastapi" target="_blank">https://github.com/fastapi/fastapi</a>
|
||||
|
||||
---
|
||||
|
||||
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python based on standard Python type hints.
|
||||
|
||||
## `fastapi-slim`
|
||||
|
||||
⚠️ Do not install this package. ⚠️
|
||||
|
||||
This package, `fastapi-slim`, does nothing other than depend on `fastapi`.
|
||||
|
||||
All the functionality has been integrated into `fastapi`.
|
||||
|
||||
The only reason this package exists is as a migration path for old projects that used to depend on `fastapi-slim`, so that they can get the latest version of `fastapi`.
|
||||
|
||||
You **should not** install this package.
|
||||
|
||||
Install instead:
|
||||
|
||||
```bash
|
||||
pip install fastapi
|
||||
```
|
||||
|
||||
This package is deprecated and will stop receiving any updates and published versions.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the terms of the MIT license.
|
||||
@@ -1,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.128.8"
|
||||
__version__ = "0.128.7"
|
||||
|
||||
from starlette import status as status
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import sys
|
||||
import types
|
||||
import typing
|
||||
import warnings
|
||||
@@ -7,26 +8,27 @@ from dataclasses import is_dataclass
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
TypeGuard,
|
||||
TypeVar,
|
||||
Union,
|
||||
get_args,
|
||||
get_origin,
|
||||
)
|
||||
|
||||
from fastapi.types import UnionType
|
||||
from pydantic import BaseModel
|
||||
from pydantic.version import VERSION as PYDANTIC_VERSION
|
||||
from starlette.datastructures import UploadFile
|
||||
from typing_extensions import TypeGuard, get_args, get_origin
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
# Copy from Pydantic: pydantic/_internal/_typing_extra.py
|
||||
WithArgsTypes: tuple[Any, ...] = (
|
||||
typing._GenericAlias, # type: ignore[attr-defined]
|
||||
types.GenericAlias,
|
||||
types.UnionType,
|
||||
) # pyright: ignore[reportAttributeAccessIssue]
|
||||
if sys.version_info < (3, 10):
|
||||
WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias) # type: ignore[attr-defined]
|
||||
else:
|
||||
WithArgsTypes: tuple[Any, ...] = (
|
||||
typing._GenericAlias, # type: ignore[attr-defined]
|
||||
types.GenericAlias,
|
||||
types.UnionType,
|
||||
) # pyright: ignore[reportAttributeAccessIssue]
|
||||
|
||||
PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])
|
||||
|
||||
@@ -45,7 +47,7 @@ sequence_types: tuple[type[Any], ...] = tuple(sequence_annotation_to_type.keys()
|
||||
|
||||
# Copy of Pydantic: pydantic/_internal/_utils.py with added TypeGuard
|
||||
def lenient_issubclass(
|
||||
cls: Any, class_or_tuple: type[_T] | tuple[type[_T], ...] | None
|
||||
cls: Any, class_or_tuple: Union[type[_T], tuple[type[_T], ...], None]
|
||||
) -> TypeGuard[type[_T]]:
|
||||
try:
|
||||
return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type]
|
||||
@@ -55,13 +57,13 @@ def lenient_issubclass(
|
||||
raise # pragma: no cover
|
||||
|
||||
|
||||
def _annotation_is_sequence(annotation: type[Any] | None) -> bool:
|
||||
def _annotation_is_sequence(annotation: Union[type[Any], None]) -> bool:
|
||||
if lenient_issubclass(annotation, (str, bytes)):
|
||||
return False
|
||||
return lenient_issubclass(annotation, sequence_types)
|
||||
|
||||
|
||||
def field_annotation_is_sequence(annotation: type[Any] | None) -> bool:
|
||||
def field_annotation_is_sequence(annotation: Union[type[Any], None]) -> bool:
|
||||
origin = get_origin(annotation)
|
||||
if origin is Union or origin is UnionType:
|
||||
for arg in get_args(annotation):
|
||||
@@ -77,7 +79,7 @@ def value_is_sequence(value: Any) -> bool:
|
||||
return isinstance(value, sequence_types) and not isinstance(value, (str, bytes))
|
||||
|
||||
|
||||
def _annotation_is_complex(annotation: type[Any] | None) -> bool:
|
||||
def _annotation_is_complex(annotation: Union[type[Any], None]) -> bool:
|
||||
return (
|
||||
lenient_issubclass(annotation, (BaseModel, Mapping, UploadFile))
|
||||
or _annotation_is_sequence(annotation)
|
||||
@@ -85,7 +87,7 @@ def _annotation_is_complex(annotation: type[Any] | None) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def field_annotation_is_complex(annotation: type[Any] | None) -> bool:
|
||||
def field_annotation_is_complex(annotation: Union[type[Any], None]) -> bool:
|
||||
origin = get_origin(annotation)
|
||||
if origin is Union or origin is UnionType:
|
||||
return any(field_annotation_is_complex(arg) for arg in get_args(annotation))
|
||||
@@ -106,7 +108,7 @@ def field_annotation_is_scalar(annotation: Any) -> bool:
|
||||
return annotation is Ellipsis or not field_annotation_is_complex(annotation)
|
||||
|
||||
|
||||
def field_annotation_is_scalar_sequence(annotation: type[Any] | None) -> bool:
|
||||
def field_annotation_is_scalar_sequence(annotation: Union[type[Any], None]) -> bool:
|
||||
origin = get_origin(annotation)
|
||||
if origin is Union or origin is UnionType:
|
||||
at_least_one_scalar_sequence = False
|
||||
|
||||
@@ -8,11 +8,8 @@ from functools import lru_cache
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
Literal,
|
||||
Union,
|
||||
cast,
|
||||
get_args,
|
||||
get_origin,
|
||||
)
|
||||
|
||||
from fastapi._compat import lenient_issubclass, shared
|
||||
@@ -35,6 +32,7 @@ from pydantic_core import Url as Url
|
||||
from pydantic_core.core_schema import (
|
||||
with_info_plain_validator_function as with_info_plain_validator_function,
|
||||
)
|
||||
from typing_extensions import Literal, get_args, get_origin
|
||||
|
||||
RequiredParam = PydanticUndefined
|
||||
Undefined = PydanticUndefined
|
||||
@@ -85,7 +83,7 @@ class ModelField:
|
||||
field_info: FieldInfo
|
||||
name: str
|
||||
mode: Literal["validation", "serialization"] = "validation"
|
||||
config: ConfigDict | None = None
|
||||
config: Union[ConfigDict, None] = None
|
||||
|
||||
@property
|
||||
def alias(self) -> str:
|
||||
@@ -93,14 +91,14 @@ class ModelField:
|
||||
return a if a is not None else self.name
|
||||
|
||||
@property
|
||||
def validation_alias(self) -> str | None:
|
||||
def validation_alias(self) -> Union[str, None]:
|
||||
va = self.field_info.validation_alias
|
||||
if isinstance(va, str) and va:
|
||||
return va
|
||||
return None
|
||||
|
||||
@property
|
||||
def serialization_alias(self) -> str | None:
|
||||
def serialization_alias(self) -> Union[str, None]:
|
||||
sa = self.field_info.serialization_alias
|
||||
return sa or None
|
||||
|
||||
@@ -145,7 +143,7 @@ class ModelField:
|
||||
value: Any,
|
||||
values: dict[str, Any] = {}, # noqa: B006
|
||||
*,
|
||||
loc: tuple[int | str, ...] = (),
|
||||
loc: tuple[Union[int, str], ...] = (),
|
||||
) -> tuple[Any, list[dict[str, Any]]]:
|
||||
try:
|
||||
return (
|
||||
@@ -162,8 +160,8 @@ class ModelField:
|
||||
value: Any,
|
||||
*,
|
||||
mode: Literal["json", "python"] = "json",
|
||||
include: IncEx | None = None,
|
||||
exclude: IncEx | None = None,
|
||||
include: Union[IncEx, None] = None,
|
||||
exclude: Union[IncEx, None] = None,
|
||||
by_alias: bool = True,
|
||||
exclude_unset: bool = False,
|
||||
exclude_defaults: bool = False,
|
||||
@@ -204,7 +202,7 @@ def get_schema_from_model_field(
|
||||
],
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
override_mode: Literal["validation"] | None = (
|
||||
override_mode: Union[Literal["validation"], None] = (
|
||||
None
|
||||
if (separate_input_output_schemas or _has_computed_fields(field))
|
||||
else "validation"
|
||||
@@ -320,7 +318,7 @@ def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
|
||||
return shared.sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return,index]
|
||||
|
||||
|
||||
def get_missing_field_error(loc: tuple[int | str, ...]) -> dict[str, Any]:
|
||||
def get_missing_field_error(loc: tuple[Union[int, str], ...]) -> dict[str, Any]:
|
||||
error = ValidationError.from_exception_data(
|
||||
"Field required", [{"type": "missing", "loc": loc, "input": {}}]
|
||||
).errors(include_url=False)[0]
|
||||
@@ -362,7 +360,7 @@ def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]:
|
||||
# Duplicate of several schema functions from Pydantic v1 to make them compatible with
|
||||
# Pydantic v2 and allow mixing the models
|
||||
|
||||
TypeModelOrEnum = type["BaseModel"] | type[Enum]
|
||||
TypeModelOrEnum = Union[type["BaseModel"], type[Enum]]
|
||||
TypeModelSet = set[TypeModelOrEnum]
|
||||
|
||||
|
||||
@@ -379,7 +377,7 @@ def get_model_name_map(unique_models: TypeModelSet) -> dict[TypeModelOrEnum, str
|
||||
|
||||
|
||||
def get_flat_models_from_model(
|
||||
model: type["BaseModel"], known_models: TypeModelSet | None = None
|
||||
model: type["BaseModel"], known_models: Union[TypeModelSet, None] = None
|
||||
) -> TypeModelSet:
|
||||
known_models = known_models or set()
|
||||
fields = get_model_fields(model)
|
||||
@@ -428,7 +426,7 @@ def get_flat_models_from_fields(
|
||||
|
||||
|
||||
def _regenerate_error_with_loc(
|
||||
*, errors: Sequence[Any], loc_prefix: tuple[str | int, ...]
|
||||
*, errors: Sequence[Any], loc_prefix: tuple[Union[str, int], ...]
|
||||
) -> list[dict[str, Any]]:
|
||||
updated_loc_errors: list[Any] = [
|
||||
{**err, "loc": loc_prefix + err.get("loc", ())} for err in errors
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
from collections.abc import Callable
|
||||
from typing import Annotated, Any
|
||||
from typing import Annotated, Any, Callable
|
||||
|
||||
from annotated_doc import Doc
|
||||
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from collections.abc import Callable, Mapping
|
||||
from collections.abc import Mapping
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
BinaryIO,
|
||||
Callable,
|
||||
Optional,
|
||||
TypeVar,
|
||||
cast,
|
||||
)
|
||||
@@ -56,11 +58,11 @@ class UploadFile(StarletteUploadFile):
|
||||
BinaryIO,
|
||||
Doc("The standard Python file object (non-async)."),
|
||||
]
|
||||
filename: Annotated[str | None, Doc("The original file name.")]
|
||||
size: Annotated[int | None, Doc("The size of the file in bytes.")]
|
||||
filename: Annotated[Optional[str], Doc("The original file name.")]
|
||||
size: Annotated[Optional[int], Doc("The size of the file in bytes.")]
|
||||
headers: Annotated[Headers, Doc("The headers of the request.")]
|
||||
content_type: Annotated[
|
||||
str | None, Doc("The content type of the request, from the headers.")
|
||||
Optional[str], Doc("The content type of the request, from the headers.")
|
||||
]
|
||||
|
||||
async def write(
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import inspect
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from functools import cached_property, partial
|
||||
from typing import Any, Literal
|
||||
from typing import Any, Callable, Optional, Union
|
||||
|
||||
from fastapi._compat import ModelField
|
||||
from fastapi.security.base import SecurityBase
|
||||
from fastapi.types import DependencyCacheKey
|
||||
from typing_extensions import Literal
|
||||
|
||||
if sys.version_info >= (3, 13): # pragma: no cover
|
||||
from inspect import iscoroutinefunction
|
||||
@@ -15,7 +15,7 @@ else: # pragma: no cover
|
||||
from asyncio import iscoroutinefunction
|
||||
|
||||
|
||||
def _unwrapped_call(call: Callable[..., Any] | None) -> Any:
|
||||
def _unwrapped_call(call: Optional[Callable[..., Any]]) -> Any:
|
||||
if call is None:
|
||||
return call # pragma: no cover
|
||||
unwrapped = inspect.unwrap(_impartial(call))
|
||||
@@ -36,19 +36,19 @@ class Dependant:
|
||||
cookie_params: list[ModelField] = field(default_factory=list)
|
||||
body_params: list[ModelField] = field(default_factory=list)
|
||||
dependencies: list["Dependant"] = field(default_factory=list)
|
||||
name: str | None = None
|
||||
call: Callable[..., Any] | None = None
|
||||
request_param_name: str | None = None
|
||||
websocket_param_name: str | None = None
|
||||
http_connection_param_name: str | None = None
|
||||
response_param_name: str | None = None
|
||||
background_tasks_param_name: str | None = None
|
||||
security_scopes_param_name: str | None = None
|
||||
own_oauth_scopes: list[str] | None = None
|
||||
parent_oauth_scopes: list[str] | None = None
|
||||
name: Optional[str] = None
|
||||
call: Optional[Callable[..., Any]] = None
|
||||
request_param_name: Optional[str] = None
|
||||
websocket_param_name: Optional[str] = None
|
||||
http_connection_param_name: Optional[str] = None
|
||||
response_param_name: Optional[str] = None
|
||||
background_tasks_param_name: Optional[str] = None
|
||||
security_scopes_param_name: Optional[str] = None
|
||||
own_oauth_scopes: Optional[list[str]] = None
|
||||
parent_oauth_scopes: Optional[list[str]] = None
|
||||
use_cache: bool = True
|
||||
path: str | None = None
|
||||
scope: Literal["function", "request"] | None = None
|
||||
path: Optional[str] = None
|
||||
scope: Union[Literal["function", "request"], None] = None
|
||||
|
||||
@cached_property
|
||||
def oauth_scopes(self) -> list[str]:
|
||||
@@ -185,7 +185,7 @@ class Dependant:
|
||||
return False
|
||||
|
||||
@cached_property
|
||||
def computed_scope(self) -> str | None:
|
||||
def computed_scope(self) -> Union[str, None]:
|
||||
if self.scope:
|
||||
return self.scope
|
||||
if self.is_gen_callable or self.is_async_gen_callable:
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import dataclasses
|
||||
import inspect
|
||||
import sys
|
||||
from collections.abc import Callable, Mapping, Sequence
|
||||
from collections.abc import Mapping, Sequence
|
||||
from contextlib import AsyncExitStack, contextmanager
|
||||
from copy import copy, deepcopy
|
||||
from dataclasses import dataclass
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
Callable,
|
||||
ForwardRef,
|
||||
Literal,
|
||||
Optional,
|
||||
Union,
|
||||
cast,
|
||||
get_args,
|
||||
get_origin,
|
||||
)
|
||||
|
||||
from fastapi import params
|
||||
@@ -64,6 +63,7 @@ from starlette.datastructures import (
|
||||
from starlette.requests import HTTPConnection, Request
|
||||
from starlette.responses import Response
|
||||
from starlette.websockets import WebSocket
|
||||
from typing_extensions import Literal, get_args, get_origin
|
||||
from typing_inspection.typing_objects import is_typealiastype
|
||||
|
||||
multipart_not_installed_error = (
|
||||
@@ -127,8 +127,8 @@ def get_flat_dependant(
|
||||
dependant: Dependant,
|
||||
*,
|
||||
skip_repeats: bool = False,
|
||||
visited: list[DependencyCacheKey] | None = None,
|
||||
parent_oauth_scopes: list[str] | None = None,
|
||||
visited: Optional[list[DependencyCacheKey]] = None,
|
||||
parent_oauth_scopes: Optional[list[str]] = None,
|
||||
) -> Dependant:
|
||||
if visited is None:
|
||||
visited = []
|
||||
@@ -199,17 +199,20 @@ def get_flat_params(dependant: Dependant) -> list[ModelField]:
|
||||
|
||||
|
||||
def _get_signature(call: Callable[..., Any]) -> inspect.Signature:
|
||||
try:
|
||||
signature = inspect.signature(call, eval_str=True)
|
||||
except NameError:
|
||||
# Handle type annotations with if TYPE_CHECKING, not used by FastAPI
|
||||
# e.g. dependency return types
|
||||
if sys.version_info >= (3, 14):
|
||||
from annotationlib import Format
|
||||
if sys.version_info >= (3, 10):
|
||||
try:
|
||||
signature = inspect.signature(call, eval_str=True)
|
||||
except NameError:
|
||||
# Handle type annotations with if TYPE_CHECKING, not used by FastAPI
|
||||
# e.g. dependency return types
|
||||
if sys.version_info >= (3, 14):
|
||||
from annotationlib import Format
|
||||
|
||||
signature = inspect.signature(call, annotation_format=Format.FORWARDREF)
|
||||
else:
|
||||
signature = inspect.signature(call)
|
||||
signature = inspect.signature(call, annotation_format=Format.FORWARDREF)
|
||||
else:
|
||||
signature = inspect.signature(call)
|
||||
else:
|
||||
signature = inspect.signature(call)
|
||||
return signature
|
||||
|
||||
|
||||
@@ -255,11 +258,11 @@ def get_dependant(
|
||||
*,
|
||||
path: str,
|
||||
call: Callable[..., Any],
|
||||
name: str | None = None,
|
||||
own_oauth_scopes: list[str] | None = None,
|
||||
parent_oauth_scopes: list[str] | None = None,
|
||||
name: Optional[str] = None,
|
||||
own_oauth_scopes: Optional[list[str]] = None,
|
||||
parent_oauth_scopes: Optional[list[str]] = None,
|
||||
use_cache: bool = True,
|
||||
scope: Literal["function", "request"] | None = None,
|
||||
scope: Union[Literal["function", "request"], None] = None,
|
||||
) -> Dependant:
|
||||
dependant = Dependant(
|
||||
call=call,
|
||||
@@ -328,7 +331,7 @@ def get_dependant(
|
||||
|
||||
def add_non_field_param_to_dependency(
|
||||
*, param_name: str, type_annotation: Any, dependant: Dependant
|
||||
) -> bool | None:
|
||||
) -> Optional[bool]:
|
||||
if lenient_issubclass(type_annotation, Request):
|
||||
dependant.request_param_name = param_name
|
||||
return True
|
||||
@@ -353,8 +356,8 @@ def add_non_field_param_to_dependency(
|
||||
@dataclass
|
||||
class ParamDetails:
|
||||
type_annotation: Any
|
||||
depends: params.Depends | None
|
||||
field: ModelField | None
|
||||
depends: Optional[params.Depends]
|
||||
field: Optional[ModelField]
|
||||
|
||||
|
||||
def analyze_param(
|
||||
@@ -396,7 +399,7 @@ def analyze_param(
|
||||
)
|
||||
]
|
||||
if fastapi_specific_annotations:
|
||||
fastapi_annotation: FieldInfo | params.Depends | None = (
|
||||
fastapi_annotation: Union[FieldInfo, params.Depends, None] = (
|
||||
fastapi_specific_annotations[-1]
|
||||
)
|
||||
else:
|
||||
@@ -557,20 +560,20 @@ async def _solve_generator(
|
||||
class SolvedDependency:
|
||||
values: dict[str, Any]
|
||||
errors: list[Any]
|
||||
background_tasks: StarletteBackgroundTasks | None
|
||||
background_tasks: Optional[StarletteBackgroundTasks]
|
||||
response: Response
|
||||
dependency_cache: dict[DependencyCacheKey, Any]
|
||||
|
||||
|
||||
async def solve_dependencies(
|
||||
*,
|
||||
request: Request | WebSocket,
|
||||
request: Union[Request, WebSocket],
|
||||
dependant: Dependant,
|
||||
body: dict[str, Any] | FormData | None = None,
|
||||
background_tasks: StarletteBackgroundTasks | None = None,
|
||||
response: Response | None = None,
|
||||
dependency_overrides_provider: Any | None = None,
|
||||
dependency_cache: dict[DependencyCacheKey, Any] | None = None,
|
||||
body: Optional[Union[dict[str, Any], FormData]] = None,
|
||||
background_tasks: Optional[StarletteBackgroundTasks] = None,
|
||||
response: Optional[Response] = None,
|
||||
dependency_overrides_provider: Optional[Any] = None,
|
||||
dependency_cache: Optional[dict[DependencyCacheKey, Any]] = None,
|
||||
# TODO: remove this parameter later, no longer used, not removing it yet as some
|
||||
# people might be monkey patching this function (although that's not supported)
|
||||
async_exit_stack: AsyncExitStack,
|
||||
@@ -718,7 +721,7 @@ def _is_json_field(field: ModelField) -> bool:
|
||||
|
||||
|
||||
def _get_multidict_value(
|
||||
field: ModelField, values: Mapping[str, Any], alias: str | None = None
|
||||
field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None
|
||||
) -> Any:
|
||||
alias = alias or get_validation_alias(field)
|
||||
if (
|
||||
@@ -750,7 +753,7 @@ def _get_multidict_value(
|
||||
|
||||
def request_params_to_args(
|
||||
fields: Sequence[ModelField],
|
||||
received_params: Mapping[str, Any] | QueryParams | Headers,
|
||||
received_params: Union[Mapping[str, Any], QueryParams, Headers],
|
||||
) -> tuple[dict[str, Any], list[Any]]:
|
||||
values: dict[str, Any] = {}
|
||||
errors: list[dict[str, Any]] = []
|
||||
@@ -898,7 +901,7 @@ async def _extract_form_body(
|
||||
):
|
||||
# For types
|
||||
assert isinstance(value, sequence_types)
|
||||
results: list[bytes | str] = []
|
||||
results: list[Union[bytes, str]] = []
|
||||
for sub_value in value:
|
||||
results.append(await sub_value.read())
|
||||
value = serialize_sequence_value(field=field, value=results)
|
||||
@@ -917,7 +920,7 @@ async def _extract_form_body(
|
||||
|
||||
async def request_body_to_args(
|
||||
body_fields: list[ModelField],
|
||||
received_body: dict[str, Any] | FormData | None,
|
||||
received_body: Optional[Union[dict[str, Any], FormData]],
|
||||
embed_body_fields: bool,
|
||||
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
|
||||
values: dict[str, Any] = {}
|
||||
@@ -947,7 +950,7 @@ async def request_body_to_args(
|
||||
return {first_field.name: v_}, errors_
|
||||
for field in body_fields:
|
||||
loc = ("body", get_validation_alias(field))
|
||||
value: Any | None = None
|
||||
value: Optional[Any] = None
|
||||
if body_to_process is not None:
|
||||
try:
|
||||
value = body_to_process.get(get_validation_alias(field))
|
||||
@@ -967,7 +970,7 @@ async def request_body_to_args(
|
||||
|
||||
def get_body_field(
|
||||
*, flat_dependant: Dependant, name: str, embed_body_fields: bool
|
||||
) -> ModelField | None:
|
||||
) -> Optional[ModelField]:
|
||||
"""
|
||||
Get a ModelField representing the request body for a path operation, combining
|
||||
all body parameters into a single field if necessary.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import dataclasses
|
||||
import datetime
|
||||
from collections import defaultdict, deque
|
||||
from collections.abc import Callable
|
||||
from decimal import Decimal
|
||||
from enum import Enum
|
||||
from ipaddress import (
|
||||
@@ -15,7 +14,7 @@ from ipaddress import (
|
||||
from pathlib import Path, PurePath
|
||||
from re import Pattern
|
||||
from types import GeneratorType
|
||||
from typing import Annotated, Any
|
||||
from typing import Annotated, Any, Callable, Optional, Union
|
||||
from uuid import UUID
|
||||
|
||||
from annotated_doc import Doc
|
||||
@@ -34,13 +33,13 @@ from ._compat import (
|
||||
|
||||
|
||||
# Taken from Pydantic v1 as is
|
||||
def isoformat(o: datetime.date | datetime.time) -> str:
|
||||
def isoformat(o: Union[datetime.date, datetime.time]) -> str:
|
||||
return o.isoformat()
|
||||
|
||||
|
||||
# Adapted from Pydantic v1
|
||||
# TODO: pv2 should this return strings instead?
|
||||
def decimal_encoder(dec_value: Decimal) -> int | float:
|
||||
def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
|
||||
"""
|
||||
Encodes a Decimal as int if there's no exponent, otherwise float
|
||||
|
||||
@@ -119,7 +118,7 @@ def jsonable_encoder(
|
||||
),
|
||||
],
|
||||
include: Annotated[
|
||||
IncEx | None,
|
||||
Optional[IncEx],
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `include` parameter, passed to Pydantic models to set the
|
||||
@@ -128,7 +127,7 @@ def jsonable_encoder(
|
||||
),
|
||||
] = None,
|
||||
exclude: Annotated[
|
||||
IncEx | None,
|
||||
Optional[IncEx],
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `exclude` parameter, passed to Pydantic models to set the
|
||||
@@ -178,7 +177,7 @@ def jsonable_encoder(
|
||||
),
|
||||
] = False,
|
||||
custom_encoder: Annotated[
|
||||
dict[Any, Callable[[Any], Any]] | None,
|
||||
Optional[dict[Any, Callable[[Any], Any]]],
|
||||
Doc(
|
||||
"""
|
||||
Pydantic's `custom_encoder` parameter, passed to Pydantic models to define
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from collections.abc import Mapping, Sequence
|
||||
from typing import Annotated, Any, TypedDict
|
||||
from typing import Annotated, Any, Optional, TypedDict, Union
|
||||
|
||||
from annotated_doc import Doc
|
||||
from pydantic import BaseModel, create_model
|
||||
@@ -68,7 +68,7 @@ class HTTPException(StarletteHTTPException):
|
||||
),
|
||||
] = None,
|
||||
headers: Annotated[
|
||||
Mapping[str, str] | None,
|
||||
Optional[Mapping[str, str]],
|
||||
Doc(
|
||||
"""
|
||||
Any headers to send to the client in the response.
|
||||
@@ -137,7 +137,7 @@ class WebSocketException(StarletteWebSocketException):
|
||||
),
|
||||
],
|
||||
reason: Annotated[
|
||||
str | None,
|
||||
Union[str, None],
|
||||
Doc(
|
||||
"""
|
||||
The reason to close the WebSocket connection.
|
||||
@@ -176,7 +176,7 @@ class ValidationException(Exception):
|
||||
self,
|
||||
errors: Sequence[Any],
|
||||
*,
|
||||
endpoint_ctx: EndpointContext | None = None,
|
||||
endpoint_ctx: Optional[EndpointContext] = None,
|
||||
) -> None:
|
||||
self._errors = errors
|
||||
self.endpoint_ctx = endpoint_ctx
|
||||
@@ -215,7 +215,7 @@ class RequestValidationError(ValidationException):
|
||||
errors: Sequence[Any],
|
||||
*,
|
||||
body: Any = None,
|
||||
endpoint_ctx: EndpointContext | None = None,
|
||||
endpoint_ctx: Optional[EndpointContext] = None,
|
||||
) -> None:
|
||||
super().__init__(errors, endpoint_ctx=endpoint_ctx)
|
||||
self.body = body
|
||||
@@ -226,7 +226,7 @@ class WebSocketRequestValidationError(ValidationException):
|
||||
self,
|
||||
errors: Sequence[Any],
|
||||
*,
|
||||
endpoint_ctx: EndpointContext | None = None,
|
||||
endpoint_ctx: Optional[EndpointContext] = None,
|
||||
) -> None:
|
||||
super().__init__(errors, endpoint_ctx=endpoint_ctx)
|
||||
|
||||
@@ -237,7 +237,7 @@ class ResponseValidationError(ValidationException):
|
||||
errors: Sequence[Any],
|
||||
*,
|
||||
body: Any = None,
|
||||
endpoint_ctx: EndpointContext | None = None,
|
||||
endpoint_ctx: Optional[EndpointContext] = None,
|
||||
) -> None:
|
||||
super().__init__(errors, endpoint_ctx=endpoint_ctx)
|
||||
self.body = body
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import json
|
||||
from typing import Annotated, Any
|
||||
from typing import Annotated, Any, Optional
|
||||
|
||||
from annotated_doc import Doc
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
@@ -85,7 +85,7 @@ def get_swagger_ui_html(
|
||||
),
|
||||
] = "https://fastapi.tiangolo.com/img/favicon.png",
|
||||
oauth2_redirect_url: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
The OAuth2 redirect URL, it is normally automatically handled by FastAPI.
|
||||
@@ -96,7 +96,7 @@ def get_swagger_ui_html(
|
||||
),
|
||||
] = None,
|
||||
init_oauth: Annotated[
|
||||
dict[str, Any] | None,
|
||||
Optional[dict[str, Any]],
|
||||
Doc(
|
||||
"""
|
||||
A dictionary with Swagger UI OAuth2 initialization configurations.
|
||||
@@ -107,7 +107,7 @@ def get_swagger_ui_html(
|
||||
),
|
||||
] = None,
|
||||
swagger_ui_parameters: Annotated[
|
||||
dict[str, Any] | None,
|
||||
Optional[dict[str, Any]],
|
||||
Doc(
|
||||
"""
|
||||
Configuration parameters for Swagger UI.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from collections.abc import Callable, Iterable, Mapping
|
||||
from collections.abc import Iterable, Mapping
|
||||
from enum import Enum
|
||||
from typing import Annotated, Any, Literal, Optional, Union
|
||||
from typing import Annotated, Any, Callable, Optional, Union
|
||||
|
||||
from fastapi._compat import with_info_plain_validator_function
|
||||
from fastapi.logger import logger
|
||||
@@ -10,7 +10,7 @@ from pydantic import (
|
||||
Field,
|
||||
GetJsonSchemaHandler,
|
||||
)
|
||||
from typing_extensions import TypedDict
|
||||
from typing_extensions import Literal, TypedDict
|
||||
from typing_extensions import deprecated as typing_deprecated
|
||||
|
||||
try:
|
||||
@@ -59,37 +59,37 @@ class BaseModelWithConfig(BaseModel):
|
||||
|
||||
|
||||
class Contact(BaseModelWithConfig):
|
||||
name: str | None = None
|
||||
url: AnyUrl | None = None
|
||||
email: EmailStr | None = None
|
||||
name: Optional[str] = None
|
||||
url: Optional[AnyUrl] = None
|
||||
email: Optional[EmailStr] = None
|
||||
|
||||
|
||||
class License(BaseModelWithConfig):
|
||||
name: str
|
||||
identifier: str | None = None
|
||||
url: AnyUrl | None = None
|
||||
identifier: Optional[str] = None
|
||||
url: Optional[AnyUrl] = None
|
||||
|
||||
|
||||
class Info(BaseModelWithConfig):
|
||||
title: str
|
||||
summary: str | None = None
|
||||
description: str | None = None
|
||||
termsOfService: str | None = None
|
||||
contact: Contact | None = None
|
||||
license: License | None = None
|
||||
summary: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
termsOfService: Optional[str] = None
|
||||
contact: Optional[Contact] = None
|
||||
license: Optional[License] = None
|
||||
version: str
|
||||
|
||||
|
||||
class ServerVariable(BaseModelWithConfig):
|
||||
enum: Annotated[list[str] | None, Field(min_length=1)] = None
|
||||
enum: Annotated[Optional[list[str]], Field(min_length=1)] = None
|
||||
default: str
|
||||
description: str | None = None
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class Server(BaseModelWithConfig):
|
||||
url: AnyUrl | str
|
||||
description: str | None = None
|
||||
variables: dict[str, ServerVariable] | None = None
|
||||
url: Union[AnyUrl, str]
|
||||
description: Optional[str] = None
|
||||
variables: Optional[dict[str, ServerVariable]] = None
|
||||
|
||||
|
||||
class Reference(BaseModel):
|
||||
@@ -98,19 +98,19 @@ class Reference(BaseModel):
|
||||
|
||||
class Discriminator(BaseModel):
|
||||
propertyName: str
|
||||
mapping: dict[str, str] | None = None
|
||||
mapping: Optional[dict[str, str]] = None
|
||||
|
||||
|
||||
class XML(BaseModelWithConfig):
|
||||
name: str | None = None
|
||||
namespace: str | None = None
|
||||
prefix: str | None = None
|
||||
attribute: bool | None = None
|
||||
wrapped: bool | None = None
|
||||
name: Optional[str] = None
|
||||
namespace: Optional[str] = None
|
||||
prefix: Optional[str] = None
|
||||
attribute: Optional[bool] = None
|
||||
wrapped: Optional[bool] = None
|
||||
|
||||
|
||||
class ExternalDocumentation(BaseModelWithConfig):
|
||||
description: str | None = None
|
||||
description: Optional[str] = None
|
||||
url: AnyUrl
|
||||
|
||||
|
||||
@@ -123,80 +123,80 @@ SchemaType = Literal[
|
||||
class Schema(BaseModelWithConfig):
|
||||
# Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu
|
||||
# Core Vocabulary
|
||||
schema_: str | None = Field(default=None, alias="$schema")
|
||||
vocabulary: str | None = Field(default=None, alias="$vocabulary")
|
||||
id: str | None = Field(default=None, alias="$id")
|
||||
anchor: str | None = Field(default=None, alias="$anchor")
|
||||
dynamicAnchor: str | None = Field(default=None, alias="$dynamicAnchor")
|
||||
ref: str | None = Field(default=None, alias="$ref")
|
||||
dynamicRef: str | None = Field(default=None, alias="$dynamicRef")
|
||||
defs: dict[str, "SchemaOrBool"] | None = Field(default=None, alias="$defs")
|
||||
comment: str | None = Field(default=None, alias="$comment")
|
||||
schema_: Optional[str] = Field(default=None, alias="$schema")
|
||||
vocabulary: Optional[str] = Field(default=None, alias="$vocabulary")
|
||||
id: Optional[str] = Field(default=None, alias="$id")
|
||||
anchor: Optional[str] = Field(default=None, alias="$anchor")
|
||||
dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor")
|
||||
ref: Optional[str] = Field(default=None, alias="$ref")
|
||||
dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef")
|
||||
defs: Optional[dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs")
|
||||
comment: Optional[str] = Field(default=None, alias="$comment")
|
||||
# Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s
|
||||
# A Vocabulary for Applying Subschemas
|
||||
allOf: list["SchemaOrBool"] | None = None
|
||||
anyOf: list["SchemaOrBool"] | None = None
|
||||
oneOf: list["SchemaOrBool"] | None = None
|
||||
allOf: Optional[list["SchemaOrBool"]] = None
|
||||
anyOf: Optional[list["SchemaOrBool"]] = None
|
||||
oneOf: Optional[list["SchemaOrBool"]] = None
|
||||
not_: Optional["SchemaOrBool"] = Field(default=None, alias="not")
|
||||
if_: Optional["SchemaOrBool"] = Field(default=None, alias="if")
|
||||
then: Optional["SchemaOrBool"] = None
|
||||
else_: Optional["SchemaOrBool"] = Field(default=None, alias="else")
|
||||
dependentSchemas: dict[str, "SchemaOrBool"] | None = None
|
||||
prefixItems: list["SchemaOrBool"] | None = None
|
||||
dependentSchemas: Optional[dict[str, "SchemaOrBool"]] = None
|
||||
prefixItems: Optional[list["SchemaOrBool"]] = None
|
||||
items: Optional["SchemaOrBool"] = None
|
||||
contains: Optional["SchemaOrBool"] = None
|
||||
properties: dict[str, "SchemaOrBool"] | None = None
|
||||
patternProperties: dict[str, "SchemaOrBool"] | None = None
|
||||
properties: Optional[dict[str, "SchemaOrBool"]] = None
|
||||
patternProperties: Optional[dict[str, "SchemaOrBool"]] = None
|
||||
additionalProperties: Optional["SchemaOrBool"] = None
|
||||
propertyNames: Optional["SchemaOrBool"] = None
|
||||
unevaluatedItems: Optional["SchemaOrBool"] = None
|
||||
unevaluatedProperties: Optional["SchemaOrBool"] = None
|
||||
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural
|
||||
# A Vocabulary for Structural Validation
|
||||
type: SchemaType | list[SchemaType] | None = None
|
||||
enum: list[Any] | None = None
|
||||
const: Any | None = None
|
||||
multipleOf: float | None = Field(default=None, gt=0)
|
||||
maximum: float | None = None
|
||||
exclusiveMaximum: float | None = None
|
||||
minimum: float | None = None
|
||||
exclusiveMinimum: float | None = None
|
||||
maxLength: int | None = Field(default=None, ge=0)
|
||||
minLength: int | None = Field(default=None, ge=0)
|
||||
pattern: str | None = None
|
||||
maxItems: int | None = Field(default=None, ge=0)
|
||||
minItems: int | None = Field(default=None, ge=0)
|
||||
uniqueItems: bool | None = None
|
||||
maxContains: int | None = Field(default=None, ge=0)
|
||||
minContains: int | None = Field(default=None, ge=0)
|
||||
maxProperties: int | None = Field(default=None, ge=0)
|
||||
minProperties: int | None = Field(default=None, ge=0)
|
||||
required: list[str] | None = None
|
||||
dependentRequired: dict[str, set[str]] | None = None
|
||||
type: Optional[Union[SchemaType, list[SchemaType]]] = None
|
||||
enum: Optional[list[Any]] = None
|
||||
const: Optional[Any] = None
|
||||
multipleOf: Optional[float] = Field(default=None, gt=0)
|
||||
maximum: Optional[float] = None
|
||||
exclusiveMaximum: Optional[float] = None
|
||||
minimum: Optional[float] = None
|
||||
exclusiveMinimum: Optional[float] = None
|
||||
maxLength: Optional[int] = Field(default=None, ge=0)
|
||||
minLength: Optional[int] = Field(default=None, ge=0)
|
||||
pattern: Optional[str] = None
|
||||
maxItems: Optional[int] = Field(default=None, ge=0)
|
||||
minItems: Optional[int] = Field(default=None, ge=0)
|
||||
uniqueItems: Optional[bool] = None
|
||||
maxContains: Optional[int] = Field(default=None, ge=0)
|
||||
minContains: Optional[int] = Field(default=None, ge=0)
|
||||
maxProperties: Optional[int] = Field(default=None, ge=0)
|
||||
minProperties: Optional[int] = Field(default=None, ge=0)
|
||||
required: Optional[list[str]] = None
|
||||
dependentRequired: Optional[dict[str, set[str]]] = None
|
||||
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c
|
||||
# Vocabularies for Semantic Content With "format"
|
||||
format: str | None = None
|
||||
format: Optional[str] = None
|
||||
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten
|
||||
# A Vocabulary for the Contents of String-Encoded Data
|
||||
contentEncoding: str | None = None
|
||||
contentMediaType: str | None = None
|
||||
contentEncoding: Optional[str] = None
|
||||
contentMediaType: Optional[str] = None
|
||||
contentSchema: Optional["SchemaOrBool"] = None
|
||||
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta
|
||||
# A Vocabulary for Basic Meta-Data Annotations
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
default: Any | None = None
|
||||
deprecated: bool | None = None
|
||||
readOnly: bool | None = None
|
||||
writeOnly: bool | None = None
|
||||
examples: list[Any] | None = None
|
||||
title: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
default: Optional[Any] = None
|
||||
deprecated: Optional[bool] = None
|
||||
readOnly: Optional[bool] = None
|
||||
writeOnly: Optional[bool] = None
|
||||
examples: Optional[list[Any]] = None
|
||||
# Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object
|
||||
# Schema Object
|
||||
discriminator: Discriminator | None = None
|
||||
xml: XML | None = None
|
||||
externalDocs: ExternalDocumentation | None = None
|
||||
discriminator: Optional[Discriminator] = None
|
||||
xml: Optional[XML] = None
|
||||
externalDocs: Optional[ExternalDocumentation] = None
|
||||
example: Annotated[
|
||||
Any | None,
|
||||
Optional[Any],
|
||||
typing_deprecated(
|
||||
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
|
||||
"although still supported. Use examples instead."
|
||||
@@ -206,14 +206,14 @@ class Schema(BaseModelWithConfig):
|
||||
|
||||
# Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents
|
||||
# A JSON Schema MUST be an object or a boolean.
|
||||
SchemaOrBool = Schema | bool
|
||||
SchemaOrBool = Union[Schema, bool]
|
||||
|
||||
|
||||
class Example(TypedDict, total=False):
|
||||
summary: str | None
|
||||
description: str | None
|
||||
value: Any | None
|
||||
externalValue: AnyUrl | None
|
||||
summary: Optional[str]
|
||||
description: Optional[str]
|
||||
value: Optional[Any]
|
||||
externalValue: Optional[AnyUrl]
|
||||
|
||||
__pydantic_config__ = {"extra": "allow"} # type: ignore[misc]
|
||||
|
||||
@@ -226,33 +226,33 @@ class ParameterInType(Enum):
|
||||
|
||||
|
||||
class Encoding(BaseModelWithConfig):
|
||||
contentType: str | None = None
|
||||
headers: dict[str, Union["Header", Reference]] | None = None
|
||||
style: str | None = None
|
||||
explode: bool | None = None
|
||||
allowReserved: bool | None = None
|
||||
contentType: Optional[str] = None
|
||||
headers: Optional[dict[str, Union["Header", Reference]]] = None
|
||||
style: Optional[str] = None
|
||||
explode: Optional[bool] = None
|
||||
allowReserved: Optional[bool] = None
|
||||
|
||||
|
||||
class MediaType(BaseModelWithConfig):
|
||||
schema_: Schema | Reference | None = Field(default=None, alias="schema")
|
||||
example: Any | None = None
|
||||
examples: dict[str, Example | Reference] | None = None
|
||||
encoding: dict[str, Encoding] | None = None
|
||||
schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
|
||||
example: Optional[Any] = None
|
||||
examples: Optional[dict[str, Union[Example, Reference]]] = None
|
||||
encoding: Optional[dict[str, Encoding]] = None
|
||||
|
||||
|
||||
class ParameterBase(BaseModelWithConfig):
|
||||
description: str | None = None
|
||||
required: bool | None = None
|
||||
deprecated: bool | None = None
|
||||
description: Optional[str] = None
|
||||
required: Optional[bool] = None
|
||||
deprecated: Optional[bool] = None
|
||||
# Serialization rules for simple scenarios
|
||||
style: str | None = None
|
||||
explode: bool | None = None
|
||||
allowReserved: bool | None = None
|
||||
schema_: Schema | Reference | None = Field(default=None, alias="schema")
|
||||
example: Any | None = None
|
||||
examples: dict[str, Example | Reference] | None = None
|
||||
style: Optional[str] = None
|
||||
explode: Optional[bool] = None
|
||||
allowReserved: Optional[bool] = None
|
||||
schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
|
||||
example: Optional[Any] = None
|
||||
examples: Optional[dict[str, Union[Example, Reference]]] = None
|
||||
# Serialization rules for more complex scenarios
|
||||
content: dict[str, MediaType] | None = None
|
||||
content: Optional[dict[str, MediaType]] = None
|
||||
|
||||
|
||||
class Parameter(ParameterBase):
|
||||
@@ -265,57 +265,57 @@ class Header(ParameterBase):
|
||||
|
||||
|
||||
class RequestBody(BaseModelWithConfig):
|
||||
description: str | None = None
|
||||
description: Optional[str] = None
|
||||
content: dict[str, MediaType]
|
||||
required: bool | None = None
|
||||
required: Optional[bool] = None
|
||||
|
||||
|
||||
class Link(BaseModelWithConfig):
|
||||
operationRef: str | None = None
|
||||
operationId: str | None = None
|
||||
parameters: dict[str, Any | str] | None = None
|
||||
requestBody: Any | str | None = None
|
||||
description: str | None = None
|
||||
server: Server | None = None
|
||||
operationRef: Optional[str] = None
|
||||
operationId: Optional[str] = None
|
||||
parameters: Optional[dict[str, Union[Any, str]]] = None
|
||||
requestBody: Optional[Union[Any, str]] = None
|
||||
description: Optional[str] = None
|
||||
server: Optional[Server] = None
|
||||
|
||||
|
||||
class Response(BaseModelWithConfig):
|
||||
description: str
|
||||
headers: dict[str, Header | Reference] | None = None
|
||||
content: dict[str, MediaType] | None = None
|
||||
links: dict[str, Link | Reference] | None = None
|
||||
headers: Optional[dict[str, Union[Header, Reference]]] = None
|
||||
content: Optional[dict[str, MediaType]] = None
|
||||
links: Optional[dict[str, Union[Link, Reference]]] = None
|
||||
|
||||
|
||||
class Operation(BaseModelWithConfig):
|
||||
tags: list[str] | None = None
|
||||
summary: str | None = None
|
||||
description: str | None = None
|
||||
externalDocs: ExternalDocumentation | None = None
|
||||
operationId: str | None = None
|
||||
parameters: list[Parameter | Reference] | None = None
|
||||
requestBody: RequestBody | Reference | None = None
|
||||
tags: Optional[list[str]] = None
|
||||
summary: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
externalDocs: Optional[ExternalDocumentation] = None
|
||||
operationId: Optional[str] = None
|
||||
parameters: Optional[list[Union[Parameter, Reference]]] = None
|
||||
requestBody: Optional[Union[RequestBody, Reference]] = None
|
||||
# Using Any for Specification Extensions
|
||||
responses: dict[str, Response | Any] | None = None
|
||||
callbacks: dict[str, dict[str, "PathItem"] | Reference] | None = None
|
||||
deprecated: bool | None = None
|
||||
security: list[dict[str, list[str]]] | None = None
|
||||
servers: list[Server] | None = None
|
||||
responses: Optional[dict[str, Union[Response, Any]]] = None
|
||||
callbacks: Optional[dict[str, Union[dict[str, "PathItem"], Reference]]] = None
|
||||
deprecated: Optional[bool] = None
|
||||
security: Optional[list[dict[str, list[str]]]] = None
|
||||
servers: Optional[list[Server]] = None
|
||||
|
||||
|
||||
class PathItem(BaseModelWithConfig):
|
||||
ref: str | None = Field(default=None, alias="$ref")
|
||||
summary: str | None = None
|
||||
description: str | None = None
|
||||
get: Operation | None = None
|
||||
put: Operation | None = None
|
||||
post: Operation | None = None
|
||||
delete: Operation | None = None
|
||||
options: Operation | None = None
|
||||
head: Operation | None = None
|
||||
patch: Operation | None = None
|
||||
trace: Operation | None = None
|
||||
servers: list[Server] | None = None
|
||||
parameters: list[Parameter | Reference] | None = None
|
||||
ref: Optional[str] = Field(default=None, alias="$ref")
|
||||
summary: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
get: Optional[Operation] = None
|
||||
put: Optional[Operation] = None
|
||||
post: Optional[Operation] = None
|
||||
delete: Optional[Operation] = None
|
||||
options: Optional[Operation] = None
|
||||
head: Optional[Operation] = None
|
||||
patch: Optional[Operation] = None
|
||||
trace: Optional[Operation] = None
|
||||
servers: Optional[list[Server]] = None
|
||||
parameters: Optional[list[Union[Parameter, Reference]]] = None
|
||||
|
||||
|
||||
class SecuritySchemeType(Enum):
|
||||
@@ -327,7 +327,7 @@ class SecuritySchemeType(Enum):
|
||||
|
||||
class SecurityBase(BaseModelWithConfig):
|
||||
type_: SecuritySchemeType = Field(alias="type")
|
||||
description: str | None = None
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class APIKeyIn(Enum):
|
||||
@@ -349,11 +349,11 @@ class HTTPBase(SecurityBase):
|
||||
|
||||
class HTTPBearer(HTTPBase):
|
||||
scheme: Literal["bearer"] = "bearer"
|
||||
bearerFormat: str | None = None
|
||||
bearerFormat: Optional[str] = None
|
||||
|
||||
|
||||
class OAuthFlow(BaseModelWithConfig):
|
||||
refreshUrl: str | None = None
|
||||
refreshUrl: Optional[str] = None
|
||||
scopes: dict[str, str] = {}
|
||||
|
||||
|
||||
@@ -375,10 +375,10 @@ class OAuthFlowAuthorizationCode(OAuthFlow):
|
||||
|
||||
|
||||
class OAuthFlows(BaseModelWithConfig):
|
||||
implicit: OAuthFlowImplicit | None = None
|
||||
password: OAuthFlowPassword | None = None
|
||||
clientCredentials: OAuthFlowClientCredentials | None = None
|
||||
authorizationCode: OAuthFlowAuthorizationCode | None = None
|
||||
implicit: Optional[OAuthFlowImplicit] = None
|
||||
password: Optional[OAuthFlowPassword] = None
|
||||
clientCredentials: Optional[OAuthFlowClientCredentials] = None
|
||||
authorizationCode: Optional[OAuthFlowAuthorizationCode] = None
|
||||
|
||||
|
||||
class OAuth2(SecurityBase):
|
||||
@@ -393,41 +393,41 @@ class OpenIdConnect(SecurityBase):
|
||||
openIdConnectUrl: str
|
||||
|
||||
|
||||
SecurityScheme = APIKey | HTTPBase | OAuth2 | OpenIdConnect | HTTPBearer
|
||||
SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer]
|
||||
|
||||
|
||||
class Components(BaseModelWithConfig):
|
||||
schemas: dict[str, Schema | Reference] | None = None
|
||||
responses: dict[str, Response | Reference] | None = None
|
||||
parameters: dict[str, Parameter | Reference] | None = None
|
||||
examples: dict[str, Example | Reference] | None = None
|
||||
requestBodies: dict[str, RequestBody | Reference] | None = None
|
||||
headers: dict[str, Header | Reference] | None = None
|
||||
securitySchemes: dict[str, SecurityScheme | Reference] | None = None
|
||||
links: dict[str, Link | Reference] | None = None
|
||||
schemas: Optional[dict[str, Union[Schema, Reference]]] = None
|
||||
responses: Optional[dict[str, Union[Response, Reference]]] = None
|
||||
parameters: Optional[dict[str, Union[Parameter, Reference]]] = None
|
||||
examples: Optional[dict[str, Union[Example, Reference]]] = None
|
||||
requestBodies: Optional[dict[str, Union[RequestBody, Reference]]] = None
|
||||
headers: Optional[dict[str, Union[Header, Reference]]] = None
|
||||
securitySchemes: Optional[dict[str, Union[SecurityScheme, Reference]]] = None
|
||||
links: Optional[dict[str, Union[Link, Reference]]] = None
|
||||
# Using Any for Specification Extensions
|
||||
callbacks: dict[str, dict[str, PathItem] | Reference | Any] | None = None
|
||||
pathItems: dict[str, PathItem | Reference] | None = None
|
||||
callbacks: Optional[dict[str, Union[dict[str, PathItem], Reference, Any]]] = None
|
||||
pathItems: Optional[dict[str, Union[PathItem, Reference]]] = None
|
||||
|
||||
|
||||
class Tag(BaseModelWithConfig):
|
||||
name: str
|
||||
description: str | None = None
|
||||
externalDocs: ExternalDocumentation | None = None
|
||||
description: Optional[str] = None
|
||||
externalDocs: Optional[ExternalDocumentation] = None
|
||||
|
||||
|
||||
class OpenAPI(BaseModelWithConfig):
|
||||
openapi: str
|
||||
info: Info
|
||||
jsonSchemaDialect: str | None = None
|
||||
servers: list[Server] | None = None
|
||||
jsonSchemaDialect: Optional[str] = None
|
||||
servers: Optional[list[Server]] = None
|
||||
# Using Any for Specification Extensions
|
||||
paths: dict[str, PathItem | Any] | None = None
|
||||
webhooks: dict[str, PathItem | Reference] | None = None
|
||||
components: Components | None = None
|
||||
security: list[dict[str, list[str]]] | None = None
|
||||
tags: list[Tag] | None = None
|
||||
externalDocs: ExternalDocumentation | None = None
|
||||
paths: Optional[dict[str, Union[PathItem, Any]]] = None
|
||||
webhooks: Optional[dict[str, Union[PathItem, Reference]]] = None
|
||||
components: Optional[Components] = None
|
||||
security: Optional[list[dict[str, list[str]]]] = None
|
||||
tags: Optional[list[Tag]] = None
|
||||
externalDocs: Optional[ExternalDocumentation] = None
|
||||
|
||||
|
||||
Schema.model_rebuild()
|
||||
|
||||
@@ -3,7 +3,7 @@ import http.client
|
||||
import inspect
|
||||
import warnings
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, Literal, cast
|
||||
from typing import Any, Optional, Union, cast
|
||||
|
||||
from fastapi import routing
|
||||
from fastapi._compat import (
|
||||
@@ -38,6 +38,7 @@ from fastapi.utils import (
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.routing import BaseRoute
|
||||
from typing_extensions import Literal
|
||||
|
||||
validation_error_definition = {
|
||||
"title": "ValidationError",
|
||||
@@ -179,13 +180,13 @@ def _get_openapi_operation_parameters(
|
||||
|
||||
def get_openapi_operation_request_body(
|
||||
*,
|
||||
body_field: ModelField | None,
|
||||
body_field: Optional[ModelField],
|
||||
model_name_map: ModelNameMap,
|
||||
field_mapping: dict[
|
||||
tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any]
|
||||
],
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> dict[str, Any] | None:
|
||||
) -> Optional[dict[str, Any]]:
|
||||
if not body_field:
|
||||
return None
|
||||
assert isinstance(body_field, ModelField)
|
||||
@@ -278,7 +279,7 @@ def get_openapi_path(
|
||||
else:
|
||||
current_response_class = route.response_class
|
||||
assert current_response_class, "A response class is needed to generate OpenAPI"
|
||||
route_response_media_type: str | None = current_response_class.media_type
|
||||
route_response_media_type: Optional[str] = current_response_class.media_type
|
||||
if route.include_in_schema:
|
||||
for method in route.methods:
|
||||
operation = get_openapi_operation_metadata(
|
||||
@@ -392,7 +393,7 @@ def get_openapi_path(
|
||||
"An additional response must be a dict"
|
||||
)
|
||||
field = route.response_fields.get(additional_status_code)
|
||||
additional_field_schema: dict[str, Any] | None = None
|
||||
additional_field_schema: Optional[dict[str, Any]] = None
|
||||
if field:
|
||||
additional_field_schema = get_schema_from_model_field(
|
||||
field=field,
|
||||
@@ -407,7 +408,7 @@ def get_openapi_path(
|
||||
.setdefault("schema", {})
|
||||
)
|
||||
deep_dict_update(additional_schema, additional_field_schema)
|
||||
status_text: str | None = status_code_ranges.get(
|
||||
status_text: Optional[str] = status_code_ranges.get(
|
||||
str(additional_status_code).upper()
|
||||
) or http.client.responses.get(int(additional_status_code))
|
||||
description = (
|
||||
@@ -481,17 +482,17 @@ def get_openapi(
|
||||
title: str,
|
||||
version: str,
|
||||
openapi_version: str = "3.1.0",
|
||||
summary: str | None = None,
|
||||
description: str | None = None,
|
||||
summary: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
routes: Sequence[BaseRoute],
|
||||
webhooks: Sequence[BaseRoute] | None = None,
|
||||
tags: list[dict[str, Any]] | None = None,
|
||||
servers: list[dict[str, str | Any]] | None = None,
|
||||
terms_of_service: str | None = None,
|
||||
contact: dict[str, str | Any] | None = None,
|
||||
license_info: dict[str, str | Any] | None = None,
|
||||
webhooks: Optional[Sequence[BaseRoute]] = None,
|
||||
tags: Optional[list[dict[str, Any]]] = None,
|
||||
servers: Optional[list[dict[str, Union[str, Any]]]] = None,
|
||||
terms_of_service: Optional[str] = None,
|
||||
contact: Optional[dict[str, Union[str, Any]]] = None,
|
||||
license_info: Optional[dict[str, Union[str, Any]]] = None,
|
||||
separate_input_output_schemas: bool = True,
|
||||
external_docs: dict[str, Any] | None = None,
|
||||
external_docs: Optional[dict[str, Any]] = None,
|
||||
) -> dict[str, Any]:
|
||||
info: dict[str, Any] = {"title": title, "version": version}
|
||||
if summary:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
||||
import warnings
|
||||
from collections.abc import Callable, Sequence
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Annotated, Any, Literal
|
||||
from typing import Annotated, Any, Callable, Optional, Union
|
||||
|
||||
from fastapi.exceptions import FastAPIDeprecationWarning
|
||||
from fastapi.openapi.models import Example
|
||||
from pydantic import AliasChoices, AliasPath
|
||||
from pydantic.fields import FieldInfo
|
||||
from typing_extensions import deprecated
|
||||
from typing_extensions import Literal, deprecated
|
||||
|
||||
from ._compat import (
|
||||
Undefined,
|
||||
@@ -31,45 +31,45 @@ class Param(FieldInfo): # type: ignore[misc]
|
||||
self,
|
||||
default: Any = Undefined,
|
||||
*,
|
||||
default_factory: Callable[[], Any] | None = _Unset,
|
||||
annotation: Any | None = None,
|
||||
alias: str | None = None,
|
||||
alias_priority: int | None = _Unset,
|
||||
validation_alias: str | AliasPath | AliasChoices | None = None,
|
||||
serialization_alias: str | None = None,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
gt: float | None = None,
|
||||
ge: float | None = None,
|
||||
lt: float | None = None,
|
||||
le: float | None = None,
|
||||
min_length: int | None = None,
|
||||
max_length: int | None = None,
|
||||
pattern: str | None = None,
|
||||
default_factory: Union[Callable[[], Any], None] = _Unset,
|
||||
annotation: Optional[Any] = None,
|
||||
alias: Optional[str] = None,
|
||||
alias_priority: Union[int, None] = _Unset,
|
||||
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
|
||||
serialization_alias: Union[str, None] = None,
|
||||
title: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
gt: Optional[float] = None,
|
||||
ge: Optional[float] = None,
|
||||
lt: Optional[float] = None,
|
||||
le: Optional[float] = None,
|
||||
min_length: Optional[int] = None,
|
||||
max_length: Optional[int] = None,
|
||||
pattern: Optional[str] = None,
|
||||
regex: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
deprecated(
|
||||
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
|
||||
),
|
||||
] = None,
|
||||
discriminator: str | None = None,
|
||||
strict: bool | None = _Unset,
|
||||
multiple_of: float | None = _Unset,
|
||||
allow_inf_nan: bool | None = _Unset,
|
||||
max_digits: int | None = _Unset,
|
||||
decimal_places: int | None = _Unset,
|
||||
examples: list[Any] | None = None,
|
||||
discriminator: Union[str, None] = None,
|
||||
strict: Union[bool, None] = _Unset,
|
||||
multiple_of: Union[float, None] = _Unset,
|
||||
allow_inf_nan: Union[bool, None] = _Unset,
|
||||
max_digits: Union[int, None] = _Unset,
|
||||
decimal_places: Union[int, None] = _Unset,
|
||||
examples: Optional[list[Any]] = None,
|
||||
example: Annotated[
|
||||
Any | None,
|
||||
Optional[Any],
|
||||
deprecated(
|
||||
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
|
||||
"although still supported. Use examples instead."
|
||||
),
|
||||
] = _Unset,
|
||||
openapi_examples: dict[str, Example] | None = None,
|
||||
deprecated: deprecated | str | bool | None = None,
|
||||
openapi_examples: Optional[dict[str, Example]] = None,
|
||||
deprecated: Union[deprecated, str, bool, None] = None,
|
||||
include_in_schema: bool = True,
|
||||
json_schema_extra: dict[str, Any] | None = None,
|
||||
json_schema_extra: Union[dict[str, Any], None] = None,
|
||||
**extra: Any,
|
||||
):
|
||||
if example is not _Unset:
|
||||
@@ -142,45 +142,45 @@ class Path(Param): # type: ignore[misc]
|
||||
self,
|
||||
default: Any = ...,
|
||||
*,
|
||||
default_factory: Callable[[], Any] | None = _Unset,
|
||||
annotation: Any | None = None,
|
||||
alias: str | None = None,
|
||||
alias_priority: int | None = _Unset,
|
||||
validation_alias: str | AliasPath | AliasChoices | None = None,
|
||||
serialization_alias: str | None = None,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
gt: float | None = None,
|
||||
ge: float | None = None,
|
||||
lt: float | None = None,
|
||||
le: float | None = None,
|
||||
min_length: int | None = None,
|
||||
max_length: int | None = None,
|
||||
pattern: str | None = None,
|
||||
default_factory: Union[Callable[[], Any], None] = _Unset,
|
||||
annotation: Optional[Any] = None,
|
||||
alias: Optional[str] = None,
|
||||
alias_priority: Union[int, None] = _Unset,
|
||||
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
|
||||
serialization_alias: Union[str, None] = None,
|
||||
title: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
gt: Optional[float] = None,
|
||||
ge: Optional[float] = None,
|
||||
lt: Optional[float] = None,
|
||||
le: Optional[float] = None,
|
||||
min_length: Optional[int] = None,
|
||||
max_length: Optional[int] = None,
|
||||
pattern: Optional[str] = None,
|
||||
regex: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
deprecated(
|
||||
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
|
||||
),
|
||||
] = None,
|
||||
discriminator: str | None = None,
|
||||
strict: bool | None = _Unset,
|
||||
multiple_of: float | None = _Unset,
|
||||
allow_inf_nan: bool | None = _Unset,
|
||||
max_digits: int | None = _Unset,
|
||||
decimal_places: int | None = _Unset,
|
||||
examples: list[Any] | None = None,
|
||||
discriminator: Union[str, None] = None,
|
||||
strict: Union[bool, None] = _Unset,
|
||||
multiple_of: Union[float, None] = _Unset,
|
||||
allow_inf_nan: Union[bool, None] = _Unset,
|
||||
max_digits: Union[int, None] = _Unset,
|
||||
decimal_places: Union[int, None] = _Unset,
|
||||
examples: Optional[list[Any]] = None,
|
||||
example: Annotated[
|
||||
Any | None,
|
||||
Optional[Any],
|
||||
deprecated(
|
||||
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
|
||||
"although still supported. Use examples instead."
|
||||
),
|
||||
] = _Unset,
|
||||
openapi_examples: dict[str, Example] | None = None,
|
||||
deprecated: deprecated | str | bool | None = None,
|
||||
openapi_examples: Optional[dict[str, Example]] = None,
|
||||
deprecated: Union[deprecated, str, bool, None] = None,
|
||||
include_in_schema: bool = True,
|
||||
json_schema_extra: dict[str, Any] | None = None,
|
||||
json_schema_extra: Union[dict[str, Any], None] = None,
|
||||
**extra: Any,
|
||||
):
|
||||
assert default is ..., "Path parameters cannot have a default value"
|
||||
@@ -226,45 +226,45 @@ class Query(Param): # type: ignore[misc]
|
||||
self,
|
||||
default: Any = Undefined,
|
||||
*,
|
||||
default_factory: Callable[[], Any] | None = _Unset,
|
||||
annotation: Any | None = None,
|
||||
alias: str | None = None,
|
||||
alias_priority: int | None = _Unset,
|
||||
validation_alias: str | AliasPath | AliasChoices | None = None,
|
||||
serialization_alias: str | None = None,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
gt: float | None = None,
|
||||
ge: float | None = None,
|
||||
lt: float | None = None,
|
||||
le: float | None = None,
|
||||
min_length: int | None = None,
|
||||
max_length: int | None = None,
|
||||
pattern: str | None = None,
|
||||
default_factory: Union[Callable[[], Any], None] = _Unset,
|
||||
annotation: Optional[Any] = None,
|
||||
alias: Optional[str] = None,
|
||||
alias_priority: Union[int, None] = _Unset,
|
||||
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
|
||||
serialization_alias: Union[str, None] = None,
|
||||
title: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
gt: Optional[float] = None,
|
||||
ge: Optional[float] = None,
|
||||
lt: Optional[float] = None,
|
||||
le: Optional[float] = None,
|
||||
min_length: Optional[int] = None,
|
||||
max_length: Optional[int] = None,
|
||||
pattern: Optional[str] = None,
|
||||
regex: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
deprecated(
|
||||
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
|
||||
),
|
||||
] = None,
|
||||
discriminator: str | None = None,
|
||||
strict: bool | None = _Unset,
|
||||
multiple_of: float | None = _Unset,
|
||||
allow_inf_nan: bool | None = _Unset,
|
||||
max_digits: int | None = _Unset,
|
||||
decimal_places: int | None = _Unset,
|
||||
examples: list[Any] | None = None,
|
||||
discriminator: Union[str, None] = None,
|
||||
strict: Union[bool, None] = _Unset,
|
||||
multiple_of: Union[float, None] = _Unset,
|
||||
allow_inf_nan: Union[bool, None] = _Unset,
|
||||
max_digits: Union[int, None] = _Unset,
|
||||
decimal_places: Union[int, None] = _Unset,
|
||||
examples: Optional[list[Any]] = None,
|
||||
example: Annotated[
|
||||
Any | None,
|
||||
Optional[Any],
|
||||
deprecated(
|
||||
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
|
||||
"although still supported. Use examples instead."
|
||||
),
|
||||
] = _Unset,
|
||||
openapi_examples: dict[str, Example] | None = None,
|
||||
deprecated: deprecated | str | bool | None = None,
|
||||
openapi_examples: Optional[dict[str, Example]] = None,
|
||||
deprecated: Union[deprecated, str, bool, None] = None,
|
||||
include_in_schema: bool = True,
|
||||
json_schema_extra: dict[str, Any] | None = None,
|
||||
json_schema_extra: Union[dict[str, Any], None] = None,
|
||||
**extra: Any,
|
||||
):
|
||||
super().__init__(
|
||||
@@ -308,46 +308,46 @@ class Header(Param): # type: ignore[misc]
|
||||
self,
|
||||
default: Any = Undefined,
|
||||
*,
|
||||
default_factory: Callable[[], Any] | None = _Unset,
|
||||
annotation: Any | None = None,
|
||||
alias: str | None = None,
|
||||
alias_priority: int | None = _Unset,
|
||||
validation_alias: str | AliasPath | AliasChoices | None = None,
|
||||
serialization_alias: str | None = None,
|
||||
default_factory: Union[Callable[[], Any], None] = _Unset,
|
||||
annotation: Optional[Any] = None,
|
||||
alias: Optional[str] = None,
|
||||
alias_priority: Union[int, None] = _Unset,
|
||||
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
|
||||
serialization_alias: Union[str, None] = None,
|
||||
convert_underscores: bool = True,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
gt: float | None = None,
|
||||
ge: float | None = None,
|
||||
lt: float | None = None,
|
||||
le: float | None = None,
|
||||
min_length: int | None = None,
|
||||
max_length: int | None = None,
|
||||
pattern: str | None = None,
|
||||
title: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
gt: Optional[float] = None,
|
||||
ge: Optional[float] = None,
|
||||
lt: Optional[float] = None,
|
||||
le: Optional[float] = None,
|
||||
min_length: Optional[int] = None,
|
||||
max_length: Optional[int] = None,
|
||||
pattern: Optional[str] = None,
|
||||
regex: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
deprecated(
|
||||
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
|
||||
),
|
||||
] = None,
|
||||
discriminator: str | None = None,
|
||||
strict: bool | None = _Unset,
|
||||
multiple_of: float | None = _Unset,
|
||||
allow_inf_nan: bool | None = _Unset,
|
||||
max_digits: int | None = _Unset,
|
||||
decimal_places: int | None = _Unset,
|
||||
examples: list[Any] | None = None,
|
||||
discriminator: Union[str, None] = None,
|
||||
strict: Union[bool, None] = _Unset,
|
||||
multiple_of: Union[float, None] = _Unset,
|
||||
allow_inf_nan: Union[bool, None] = _Unset,
|
||||
max_digits: Union[int, None] = _Unset,
|
||||
decimal_places: Union[int, None] = _Unset,
|
||||
examples: Optional[list[Any]] = None,
|
||||
example: Annotated[
|
||||
Any | None,
|
||||
Optional[Any],
|
||||
deprecated(
|
||||
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
|
||||
"although still supported. Use examples instead."
|
||||
),
|
||||
] = _Unset,
|
||||
openapi_examples: dict[str, Example] | None = None,
|
||||
deprecated: deprecated | str | bool | None = None,
|
||||
openapi_examples: Optional[dict[str, Example]] = None,
|
||||
deprecated: Union[deprecated, str, bool, None] = None,
|
||||
include_in_schema: bool = True,
|
||||
json_schema_extra: dict[str, Any] | None = None,
|
||||
json_schema_extra: Union[dict[str, Any], None] = None,
|
||||
**extra: Any,
|
||||
):
|
||||
self.convert_underscores = convert_underscores
|
||||
@@ -392,45 +392,45 @@ class Cookie(Param): # type: ignore[misc]
|
||||
self,
|
||||
default: Any = Undefined,
|
||||
*,
|
||||
default_factory: Callable[[], Any] | None = _Unset,
|
||||
annotation: Any | None = None,
|
||||
alias: str | None = None,
|
||||
alias_priority: int | None = _Unset,
|
||||
validation_alias: str | AliasPath | AliasChoices | None = None,
|
||||
serialization_alias: str | None = None,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
gt: float | None = None,
|
||||
ge: float | None = None,
|
||||
lt: float | None = None,
|
||||
le: float | None = None,
|
||||
min_length: int | None = None,
|
||||
max_length: int | None = None,
|
||||
pattern: str | None = None,
|
||||
default_factory: Union[Callable[[], Any], None] = _Unset,
|
||||
annotation: Optional[Any] = None,
|
||||
alias: Optional[str] = None,
|
||||
alias_priority: Union[int, None] = _Unset,
|
||||
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
|
||||
serialization_alias: Union[str, None] = None,
|
||||
title: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
gt: Optional[float] = None,
|
||||
ge: Optional[float] = None,
|
||||
lt: Optional[float] = None,
|
||||
le: Optional[float] = None,
|
||||
min_length: Optional[int] = None,
|
||||
max_length: Optional[int] = None,
|
||||
pattern: Optional[str] = None,
|
||||
regex: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
deprecated(
|
||||
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
|
||||
),
|
||||
] = None,
|
||||
discriminator: str | None = None,
|
||||
strict: bool | None = _Unset,
|
||||
multiple_of: float | None = _Unset,
|
||||
allow_inf_nan: bool | None = _Unset,
|
||||
max_digits: int | None = _Unset,
|
||||
decimal_places: int | None = _Unset,
|
||||
examples: list[Any] | None = None,
|
||||
discriminator: Union[str, None] = None,
|
||||
strict: Union[bool, None] = _Unset,
|
||||
multiple_of: Union[float, None] = _Unset,
|
||||
allow_inf_nan: Union[bool, None] = _Unset,
|
||||
max_digits: Union[int, None] = _Unset,
|
||||
decimal_places: Union[int, None] = _Unset,
|
||||
examples: Optional[list[Any]] = None,
|
||||
example: Annotated[
|
||||
Any | None,
|
||||
Optional[Any],
|
||||
deprecated(
|
||||
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
|
||||
"although still supported. Use examples instead."
|
||||
),
|
||||
] = _Unset,
|
||||
openapi_examples: dict[str, Example] | None = None,
|
||||
deprecated: deprecated | str | bool | None = None,
|
||||
openapi_examples: Optional[dict[str, Example]] = None,
|
||||
deprecated: Union[deprecated, str, bool, None] = None,
|
||||
include_in_schema: bool = True,
|
||||
json_schema_extra: dict[str, Any] | None = None,
|
||||
json_schema_extra: Union[dict[str, Any], None] = None,
|
||||
**extra: Any,
|
||||
):
|
||||
super().__init__(
|
||||
@@ -472,47 +472,47 @@ class Body(FieldInfo): # type: ignore[misc]
|
||||
self,
|
||||
default: Any = Undefined,
|
||||
*,
|
||||
default_factory: Callable[[], Any] | None = _Unset,
|
||||
annotation: Any | None = None,
|
||||
embed: bool | None = None,
|
||||
default_factory: Union[Callable[[], Any], None] = _Unset,
|
||||
annotation: Optional[Any] = None,
|
||||
embed: Union[bool, None] = None,
|
||||
media_type: str = "application/json",
|
||||
alias: str | None = None,
|
||||
alias_priority: int | None = _Unset,
|
||||
validation_alias: str | AliasPath | AliasChoices | None = None,
|
||||
serialization_alias: str | None = None,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
gt: float | None = None,
|
||||
ge: float | None = None,
|
||||
lt: float | None = None,
|
||||
le: float | None = None,
|
||||
min_length: int | None = None,
|
||||
max_length: int | None = None,
|
||||
pattern: str | None = None,
|
||||
alias: Optional[str] = None,
|
||||
alias_priority: Union[int, None] = _Unset,
|
||||
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
|
||||
serialization_alias: Union[str, None] = None,
|
||||
title: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
gt: Optional[float] = None,
|
||||
ge: Optional[float] = None,
|
||||
lt: Optional[float] = None,
|
||||
le: Optional[float] = None,
|
||||
min_length: Optional[int] = None,
|
||||
max_length: Optional[int] = None,
|
||||
pattern: Optional[str] = None,
|
||||
regex: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
deprecated(
|
||||
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
|
||||
),
|
||||
] = None,
|
||||
discriminator: str | None = None,
|
||||
strict: bool | None = _Unset,
|
||||
multiple_of: float | None = _Unset,
|
||||
allow_inf_nan: bool | None = _Unset,
|
||||
max_digits: int | None = _Unset,
|
||||
decimal_places: int | None = _Unset,
|
||||
examples: list[Any] | None = None,
|
||||
discriminator: Union[str, None] = None,
|
||||
strict: Union[bool, None] = _Unset,
|
||||
multiple_of: Union[float, None] = _Unset,
|
||||
allow_inf_nan: Union[bool, None] = _Unset,
|
||||
max_digits: Union[int, None] = _Unset,
|
||||
decimal_places: Union[int, None] = _Unset,
|
||||
examples: Optional[list[Any]] = None,
|
||||
example: Annotated[
|
||||
Any | None,
|
||||
Optional[Any],
|
||||
deprecated(
|
||||
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
|
||||
"although still supported. Use examples instead."
|
||||
),
|
||||
] = _Unset,
|
||||
openapi_examples: dict[str, Example] | None = None,
|
||||
deprecated: deprecated | str | bool | None = None,
|
||||
openapi_examples: Optional[dict[str, Example]] = None,
|
||||
deprecated: Union[deprecated, str, bool, None] = None,
|
||||
include_in_schema: bool = True,
|
||||
json_schema_extra: dict[str, Any] | None = None,
|
||||
json_schema_extra: Union[dict[str, Any], None] = None,
|
||||
**extra: Any,
|
||||
):
|
||||
self.embed = embed
|
||||
@@ -584,46 +584,46 @@ class Form(Body): # type: ignore[misc]
|
||||
self,
|
||||
default: Any = Undefined,
|
||||
*,
|
||||
default_factory: Callable[[], Any] | None = _Unset,
|
||||
annotation: Any | None = None,
|
||||
default_factory: Union[Callable[[], Any], None] = _Unset,
|
||||
annotation: Optional[Any] = None,
|
||||
media_type: str = "application/x-www-form-urlencoded",
|
||||
alias: str | None = None,
|
||||
alias_priority: int | None = _Unset,
|
||||
validation_alias: str | AliasPath | AliasChoices | None = None,
|
||||
serialization_alias: str | None = None,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
gt: float | None = None,
|
||||
ge: float | None = None,
|
||||
lt: float | None = None,
|
||||
le: float | None = None,
|
||||
min_length: int | None = None,
|
||||
max_length: int | None = None,
|
||||
pattern: str | None = None,
|
||||
alias: Optional[str] = None,
|
||||
alias_priority: Union[int, None] = _Unset,
|
||||
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
|
||||
serialization_alias: Union[str, None] = None,
|
||||
title: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
gt: Optional[float] = None,
|
||||
ge: Optional[float] = None,
|
||||
lt: Optional[float] = None,
|
||||
le: Optional[float] = None,
|
||||
min_length: Optional[int] = None,
|
||||
max_length: Optional[int] = None,
|
||||
pattern: Optional[str] = None,
|
||||
regex: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
deprecated(
|
||||
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
|
||||
),
|
||||
] = None,
|
||||
discriminator: str | None = None,
|
||||
strict: bool | None = _Unset,
|
||||
multiple_of: float | None = _Unset,
|
||||
allow_inf_nan: bool | None = _Unset,
|
||||
max_digits: int | None = _Unset,
|
||||
decimal_places: int | None = _Unset,
|
||||
examples: list[Any] | None = None,
|
||||
discriminator: Union[str, None] = None,
|
||||
strict: Union[bool, None] = _Unset,
|
||||
multiple_of: Union[float, None] = _Unset,
|
||||
allow_inf_nan: Union[bool, None] = _Unset,
|
||||
max_digits: Union[int, None] = _Unset,
|
||||
decimal_places: Union[int, None] = _Unset,
|
||||
examples: Optional[list[Any]] = None,
|
||||
example: Annotated[
|
||||
Any | None,
|
||||
Optional[Any],
|
||||
deprecated(
|
||||
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
|
||||
"although still supported. Use examples instead."
|
||||
),
|
||||
] = _Unset,
|
||||
openapi_examples: dict[str, Example] | None = None,
|
||||
deprecated: deprecated | str | bool | None = None,
|
||||
openapi_examples: Optional[dict[str, Example]] = None,
|
||||
deprecated: Union[deprecated, str, bool, None] = None,
|
||||
include_in_schema: bool = True,
|
||||
json_schema_extra: dict[str, Any] | None = None,
|
||||
json_schema_extra: Union[dict[str, Any], None] = None,
|
||||
**extra: Any,
|
||||
):
|
||||
super().__init__(
|
||||
@@ -666,46 +666,46 @@ class File(Form): # type: ignore[misc]
|
||||
self,
|
||||
default: Any = Undefined,
|
||||
*,
|
||||
default_factory: Callable[[], Any] | None = _Unset,
|
||||
annotation: Any | None = None,
|
||||
default_factory: Union[Callable[[], Any], None] = _Unset,
|
||||
annotation: Optional[Any] = None,
|
||||
media_type: str = "multipart/form-data",
|
||||
alias: str | None = None,
|
||||
alias_priority: int | None = _Unset,
|
||||
validation_alias: str | AliasPath | AliasChoices | None = None,
|
||||
serialization_alias: str | None = None,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
gt: float | None = None,
|
||||
ge: float | None = None,
|
||||
lt: float | None = None,
|
||||
le: float | None = None,
|
||||
min_length: int | None = None,
|
||||
max_length: int | None = None,
|
||||
pattern: str | None = None,
|
||||
alias: Optional[str] = None,
|
||||
alias_priority: Union[int, None] = _Unset,
|
||||
validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
|
||||
serialization_alias: Union[str, None] = None,
|
||||
title: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
gt: Optional[float] = None,
|
||||
ge: Optional[float] = None,
|
||||
lt: Optional[float] = None,
|
||||
le: Optional[float] = None,
|
||||
min_length: Optional[int] = None,
|
||||
max_length: Optional[int] = None,
|
||||
pattern: Optional[str] = None,
|
||||
regex: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
deprecated(
|
||||
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
|
||||
),
|
||||
] = None,
|
||||
discriminator: str | None = None,
|
||||
strict: bool | None = _Unset,
|
||||
multiple_of: float | None = _Unset,
|
||||
allow_inf_nan: bool | None = _Unset,
|
||||
max_digits: int | None = _Unset,
|
||||
decimal_places: int | None = _Unset,
|
||||
examples: list[Any] | None = None,
|
||||
discriminator: Union[str, None] = None,
|
||||
strict: Union[bool, None] = _Unset,
|
||||
multiple_of: Union[float, None] = _Unset,
|
||||
allow_inf_nan: Union[bool, None] = _Unset,
|
||||
max_digits: Union[int, None] = _Unset,
|
||||
decimal_places: Union[int, None] = _Unset,
|
||||
examples: Optional[list[Any]] = None,
|
||||
example: Annotated[
|
||||
Any | None,
|
||||
Optional[Any],
|
||||
deprecated(
|
||||
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
|
||||
"although still supported. Use examples instead."
|
||||
),
|
||||
] = _Unset,
|
||||
openapi_examples: dict[str, Example] | None = None,
|
||||
deprecated: deprecated | str | bool | None = None,
|
||||
openapi_examples: Optional[dict[str, Example]] = None,
|
||||
deprecated: Union[deprecated, str, bool, None] = None,
|
||||
include_in_schema: bool = True,
|
||||
json_schema_extra: dict[str, Any] | None = None,
|
||||
json_schema_extra: Union[dict[str, Any], None] = None,
|
||||
**extra: Any,
|
||||
):
|
||||
super().__init__(
|
||||
@@ -745,11 +745,11 @@ class File(Form): # type: ignore[misc]
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Depends:
|
||||
dependency: Callable[..., Any] | None = None
|
||||
dependency: Optional[Callable[..., Any]] = None
|
||||
use_cache: bool = True
|
||||
scope: Literal["function", "request"] | None = None
|
||||
scope: Union[Literal["function", "request"], None] = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Security(Depends):
|
||||
scopes: Sequence[str] | None = None
|
||||
scopes: Optional[Sequence[str]] = None
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
from typing import Annotated
|
||||
from typing import Annotated, Optional, Union
|
||||
|
||||
from annotated_doc import Doc
|
||||
from fastapi.openapi.models import APIKey, APIKeyIn
|
||||
@@ -13,8 +13,8 @@ class APIKeyBase(SecurityBase):
|
||||
self,
|
||||
location: APIKeyIn,
|
||||
name: str,
|
||||
description: str | None,
|
||||
scheme_name: str | None,
|
||||
description: Union[str, None],
|
||||
scheme_name: Union[str, None],
|
||||
auto_error: bool,
|
||||
):
|
||||
self.auto_error = auto_error
|
||||
@@ -42,7 +42,7 @@ class APIKeyBase(SecurityBase):
|
||||
headers={"WWW-Authenticate": "APIKey"},
|
||||
)
|
||||
|
||||
def check_api_key(self, api_key: str | None) -> str | None:
|
||||
def check_api_key(self, api_key: Optional[str]) -> Optional[str]:
|
||||
if not api_key:
|
||||
if self.auto_error:
|
||||
raise self.make_not_authenticated_error()
|
||||
@@ -90,7 +90,7 @@ class APIKeyQuery(APIKeyBase):
|
||||
Doc("Query parameter name."),
|
||||
],
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -100,7 +100,7 @@ class APIKeyQuery(APIKeyBase):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -137,7 +137,7 @@ class APIKeyQuery(APIKeyBase):
|
||||
auto_error=auto_error,
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> str | None:
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
api_key = request.query_params.get(self.model.name)
|
||||
return self.check_api_key(api_key)
|
||||
|
||||
@@ -179,7 +179,7 @@ class APIKeyHeader(APIKeyBase):
|
||||
*,
|
||||
name: Annotated[str, Doc("Header name.")],
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -189,7 +189,7 @@ class APIKeyHeader(APIKeyBase):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -225,7 +225,7 @@ class APIKeyHeader(APIKeyBase):
|
||||
auto_error=auto_error,
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> str | None:
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
api_key = request.headers.get(self.model.name)
|
||||
return self.check_api_key(api_key)
|
||||
|
||||
@@ -267,7 +267,7 @@ class APIKeyCookie(APIKeyBase):
|
||||
*,
|
||||
name: Annotated[str, Doc("Cookie name.")],
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -277,7 +277,7 @@ class APIKeyCookie(APIKeyBase):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -313,6 +313,6 @@ class APIKeyCookie(APIKeyBase):
|
||||
auto_error=auto_error,
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> str | None:
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
api_key = request.cookies.get(self.model.name)
|
||||
return self.check_api_key(api_key)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import binascii
|
||||
from base64 import b64decode
|
||||
from typing import Annotated
|
||||
from typing import Annotated, Optional
|
||||
|
||||
from annotated_doc import Doc
|
||||
from fastapi.exceptions import HTTPException
|
||||
@@ -71,8 +71,8 @@ class HTTPBase(SecurityBase):
|
||||
self,
|
||||
*,
|
||||
scheme: str,
|
||||
scheme_name: str | None = None,
|
||||
description: str | None = None,
|
||||
scheme_name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
auto_error: bool = True,
|
||||
):
|
||||
self.model: HTTPBaseModel = HTTPBaseModel(
|
||||
@@ -91,7 +91,9 @@ class HTTPBase(SecurityBase):
|
||||
headers=self.make_authenticate_headers(),
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None:
|
||||
async def __call__(
|
||||
self, request: Request
|
||||
) -> Optional[HTTPAuthorizationCredentials]:
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, credentials = get_authorization_scheme_param(authorization)
|
||||
if not (authorization and scheme and credentials):
|
||||
@@ -141,7 +143,7 @@ class HTTPBasic(HTTPBase):
|
||||
self,
|
||||
*,
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -151,7 +153,7 @@ class HTTPBasic(HTTPBase):
|
||||
),
|
||||
] = None,
|
||||
realm: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
HTTP Basic authentication realm.
|
||||
@@ -159,7 +161,7 @@ class HTTPBasic(HTTPBase):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -201,7 +203,7 @@ class HTTPBasic(HTTPBase):
|
||||
|
||||
async def __call__( # type: ignore
|
||||
self, request: Request
|
||||
) -> HTTPBasicCredentials | None:
|
||||
) -> Optional[HTTPBasicCredentials]:
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, param = get_authorization_scheme_param(authorization)
|
||||
if not authorization or scheme.lower() != "basic":
|
||||
@@ -254,9 +256,9 @@ class HTTPBearer(HTTPBase):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
bearerFormat: Annotated[str | None, Doc("Bearer token format.")] = None,
|
||||
bearerFormat: Annotated[Optional[str], Doc("Bearer token format.")] = None,
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -266,7 +268,7 @@ class HTTPBearer(HTTPBase):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -300,7 +302,9 @@ class HTTPBearer(HTTPBase):
|
||||
self.scheme_name = scheme_name or self.__class__.__name__
|
||||
self.auto_error = auto_error
|
||||
|
||||
async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None:
|
||||
async def __call__(
|
||||
self, request: Request
|
||||
) -> Optional[HTTPAuthorizationCredentials]:
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, credentials = get_authorization_scheme_param(authorization)
|
||||
if not (authorization and scheme and credentials):
|
||||
@@ -358,7 +362,7 @@ class HTTPDigest(HTTPBase):
|
||||
self,
|
||||
*,
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -368,7 +372,7 @@ class HTTPDigest(HTTPBase):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -401,7 +405,9 @@ class HTTPDigest(HTTPBase):
|
||||
self.scheme_name = scheme_name or self.__class__.__name__
|
||||
self.auto_error = auto_error
|
||||
|
||||
async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None:
|
||||
async def __call__(
|
||||
self, request: Request
|
||||
) -> Optional[HTTPAuthorizationCredentials]:
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, credentials = get_authorization_scheme_param(authorization)
|
||||
if not (authorization and scheme and credentials):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Annotated, Any, cast
|
||||
from typing import Annotated, Any, Optional, Union, cast
|
||||
|
||||
from annotated_doc import Doc
|
||||
from fastapi.exceptions import HTTPException
|
||||
@@ -60,7 +60,7 @@ class OAuth2PasswordRequestForm:
|
||||
self,
|
||||
*,
|
||||
grant_type: Annotated[
|
||||
str | None,
|
||||
Union[str, None],
|
||||
Form(pattern="^password$"),
|
||||
Doc(
|
||||
"""
|
||||
@@ -128,7 +128,7 @@ class OAuth2PasswordRequestForm:
|
||||
),
|
||||
] = "",
|
||||
client_id: Annotated[
|
||||
str | None,
|
||||
Union[str, None],
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
@@ -139,7 +139,7 @@ class OAuth2PasswordRequestForm:
|
||||
),
|
||||
] = None,
|
||||
client_secret: Annotated[
|
||||
str | None,
|
||||
Union[str, None],
|
||||
Form(json_schema_extra={"format": "password"}),
|
||||
Doc(
|
||||
"""
|
||||
@@ -294,7 +294,7 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
|
||||
),
|
||||
] = "",
|
||||
client_id: Annotated[
|
||||
str | None,
|
||||
Union[str, None],
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
@@ -305,7 +305,7 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
|
||||
),
|
||||
] = None,
|
||||
client_secret: Annotated[
|
||||
str | None,
|
||||
Union[str, None],
|
||||
Form(),
|
||||
Doc(
|
||||
"""
|
||||
@@ -344,7 +344,7 @@ class OAuth2(SecurityBase):
|
||||
self,
|
||||
*,
|
||||
flows: Annotated[
|
||||
OAuthFlowsModel | dict[str, dict[str, Any]],
|
||||
Union[OAuthFlowsModel, dict[str, dict[str, Any]]],
|
||||
Doc(
|
||||
"""
|
||||
The dictionary of OAuth2 flows.
|
||||
@@ -352,7 +352,7 @@ class OAuth2(SecurityBase):
|
||||
),
|
||||
] = OAuthFlowsModel(),
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -362,7 +362,7 @@ class OAuth2(SecurityBase):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -420,7 +420,7 @@ class OAuth2(SecurityBase):
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> str | None:
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization = request.headers.get("Authorization")
|
||||
if not authorization:
|
||||
if self.auto_error:
|
||||
@@ -454,7 +454,7 @@ class OAuth2PasswordBearer(OAuth2):
|
||||
),
|
||||
],
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -464,7 +464,7 @@ class OAuth2PasswordBearer(OAuth2):
|
||||
),
|
||||
] = None,
|
||||
scopes: Annotated[
|
||||
dict[str, str] | None,
|
||||
Optional[dict[str, str]],
|
||||
Doc(
|
||||
"""
|
||||
The OAuth2 scopes that would be required by the *path operations* that
|
||||
@@ -476,7 +476,7 @@ class OAuth2PasswordBearer(OAuth2):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -506,7 +506,7 @@ class OAuth2PasswordBearer(OAuth2):
|
||||
),
|
||||
] = True,
|
||||
refreshUrl: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
The URL to refresh the token and obtain a new one.
|
||||
@@ -533,7 +533,7 @@ class OAuth2PasswordBearer(OAuth2):
|
||||
auto_error=auto_error,
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> str | None:
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, param = get_authorization_scheme_param(authorization)
|
||||
if not authorization or scheme.lower() != "bearer":
|
||||
@@ -562,7 +562,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
|
||||
),
|
||||
],
|
||||
refreshUrl: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
The URL to refresh the token and obtain a new one.
|
||||
@@ -570,7 +570,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
|
||||
),
|
||||
] = None,
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -580,7 +580,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
|
||||
),
|
||||
] = None,
|
||||
scopes: Annotated[
|
||||
dict[str, str] | None,
|
||||
Optional[dict[str, str]],
|
||||
Doc(
|
||||
"""
|
||||
The OAuth2 scopes that would be required by the *path operations* that
|
||||
@@ -589,7 +589,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -639,7 +639,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
|
||||
auto_error=auto_error,
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> str | None:
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization = request.headers.get("Authorization")
|
||||
scheme, param = get_authorization_scheme_param(authorization)
|
||||
if not authorization or scheme.lower() != "bearer":
|
||||
@@ -666,7 +666,7 @@ class SecurityScopes:
|
||||
def __init__(
|
||||
self,
|
||||
scopes: Annotated[
|
||||
list[str] | None,
|
||||
Optional[list[str]],
|
||||
Doc(
|
||||
"""
|
||||
This will be filled by FastAPI.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Annotated
|
||||
from typing import Annotated, Optional
|
||||
|
||||
from annotated_doc import Doc
|
||||
from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel
|
||||
@@ -31,7 +31,7 @@ class OpenIdConnect(SecurityBase):
|
||||
),
|
||||
],
|
||||
scheme_name: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme name.
|
||||
@@ -41,7 +41,7 @@ class OpenIdConnect(SecurityBase):
|
||||
),
|
||||
] = None,
|
||||
description: Annotated[
|
||||
str | None,
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
Security scheme description.
|
||||
@@ -84,7 +84,7 @@ class OpenIdConnect(SecurityBase):
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
async def __call__(self, request: Request) -> str | None:
|
||||
async def __call__(self, request: Request) -> Optional[str]:
|
||||
authorization = request.headers.get("Authorization")
|
||||
if not authorization:
|
||||
if self.auto_error:
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_authorization_scheme_param(
|
||||
authorization_header_value: str | None,
|
||||
authorization_header_value: Optional[str],
|
||||
) -> tuple[str, str]:
|
||||
if not authorization_header_value:
|
||||
return "", ""
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import types
|
||||
from collections.abc import Callable
|
||||
from enum import Enum
|
||||
from typing import Any, TypeVar, Union
|
||||
from typing import Any, Callable, Optional, TypeVar, Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic.main import IncEx as IncEx
|
||||
|
||||
DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any])
|
||||
UnionType = getattr(types, "UnionType", Union)
|
||||
ModelNameMap = dict[type[BaseModel] | type[Enum], str]
|
||||
DependencyCacheKey = tuple[Callable[..., Any] | None, tuple[str, ...], str]
|
||||
ModelNameMap = dict[Union[type[BaseModel], type[Enum]], str]
|
||||
DependencyCacheKey = tuple[Optional[Callable[..., Any]], tuple[str, ...], str]
|
||||
|
||||
@@ -3,7 +3,8 @@ import warnings
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Literal,
|
||||
Optional,
|
||||
Union,
|
||||
)
|
||||
|
||||
import fastapi
|
||||
@@ -16,6 +17,7 @@ from fastapi._compat import (
|
||||
from fastapi.datastructures import DefaultPlaceholder, DefaultType
|
||||
from fastapi.exceptions import FastAPIDeprecationWarning, PydanticV1NotSupportedError
|
||||
from pydantic.fields import FieldInfo
|
||||
from typing_extensions import Literal
|
||||
|
||||
from ._compat import v2
|
||||
|
||||
@@ -23,7 +25,7 @@ if TYPE_CHECKING: # pragma: nocover
|
||||
from .routing import APIRoute
|
||||
|
||||
|
||||
def is_body_allowed_for_status_code(status_code: int | str | None) -> bool:
|
||||
def is_body_allowed_for_status_code(status_code: Union[int, str, None]) -> bool:
|
||||
if status_code is None:
|
||||
return True
|
||||
# Ref: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#patterned-fields-1
|
||||
@@ -58,9 +60,9 @@ _invalid_args_message = (
|
||||
def create_model_field(
|
||||
name: str,
|
||||
type_: Any,
|
||||
default: Any | None = Undefined,
|
||||
field_info: FieldInfo | None = None,
|
||||
alias: str | None = None,
|
||||
default: Optional[Any] = Undefined,
|
||||
field_info: Optional[FieldInfo] = None,
|
||||
alias: Optional[str] = None,
|
||||
mode: Literal["validation", "serialization"] = "validation",
|
||||
) -> ModelField:
|
||||
if annotation_is_pydantic_v1(type_):
|
||||
@@ -119,9 +121,9 @@ def deep_dict_update(main_dict: dict[Any, Any], update_dict: dict[Any, Any]) ->
|
||||
|
||||
|
||||
def get_value_or_default(
|
||||
first_item: DefaultPlaceholder | DefaultType,
|
||||
*extra_items: DefaultPlaceholder | DefaultType,
|
||||
) -> DefaultPlaceholder | DefaultType:
|
||||
first_item: Union[DefaultPlaceholder, DefaultType],
|
||||
*extra_items: Union[DefaultPlaceholder, DefaultType],
|
||||
) -> Union[DefaultPlaceholder, DefaultType]:
|
||||
"""
|
||||
Pass items or `DefaultPlaceholder`s by descending priority.
|
||||
|
||||
|
||||
28
pdm_build.py
28
pdm_build.py
@@ -3,38 +3,18 @@ from typing import Any
|
||||
|
||||
from pdm.backend.hooks import Context
|
||||
|
||||
TIANGOLO_BUILD_PACKAGE = os.getenv("TIANGOLO_BUILD_PACKAGE")
|
||||
TIANGOLO_BUILD_PACKAGE = os.getenv("TIANGOLO_BUILD_PACKAGE", "fastapi")
|
||||
|
||||
|
||||
def pdm_build_initialize(context: Context) -> None:
|
||||
metadata = context.config.metadata
|
||||
# Get main version
|
||||
version = metadata["version"]
|
||||
# Get custom config for the current package, from the env var
|
||||
all_configs_config: dict[str, Any] = context.config.data["tool"]["tiangolo"][
|
||||
config: dict[str, Any] = context.config.data["tool"]["tiangolo"][
|
||||
"_internal-slim-build"
|
||||
]["packages"]
|
||||
|
||||
if TIANGOLO_BUILD_PACKAGE not in all_configs_config:
|
||||
]["packages"].get(TIANGOLO_BUILD_PACKAGE)
|
||||
if not config:
|
||||
return
|
||||
|
||||
config = all_configs_config[TIANGOLO_BUILD_PACKAGE]
|
||||
project_config: dict[str, Any] = config["project"]
|
||||
# Override main [project] configs with custom configs for this package
|
||||
for key, value in project_config.items():
|
||||
metadata[key] = value
|
||||
# Get custom build config for the current package
|
||||
build_config: dict[str, Any] = (
|
||||
config.get("tool", {}).get("pdm", {}).get("build", {})
|
||||
)
|
||||
# Override PDM build config with custom build config for this package
|
||||
for key, value in build_config.items():
|
||||
context.config.build_config[key] = value
|
||||
# Get main dependencies
|
||||
dependencies: list[str] = metadata.get("dependencies", [])
|
||||
# Sync versions in dependencies
|
||||
new_dependencies = []
|
||||
for dep in dependencies:
|
||||
new_dep = f"{dep}>={version}"
|
||||
new_dependencies.append(new_dep)
|
||||
metadata["dependencies"] = new_dependencies
|
||||
|
||||
@@ -9,7 +9,7 @@ description = "FastAPI framework, high performance, easy to learn, fast to code,
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
license-files = ["LICENSE"]
|
||||
requires-python = ">=3.10"
|
||||
requires-python = ">=3.9"
|
||||
authors = [
|
||||
{ name = "Sebastián Ramírez", email = "tiangolo@gmail.com" },
|
||||
]
|
||||
@@ -33,6 +33,7 @@ classifiers = [
|
||||
"Framework :: Pydantic :: 2",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
@@ -201,29 +202,6 @@ source-includes = [
|
||||
|
||||
[tool.tiangolo._internal-slim-build.packages.fastapi-slim.project]
|
||||
name = "fastapi-slim"
|
||||
readme = "fastapi-slim/README.md"
|
||||
dependencies = [
|
||||
"fastapi",
|
||||
]
|
||||
optional-dependencies = {}
|
||||
scripts = {}
|
||||
|
||||
[tool.tiangolo._internal-slim-build.packages.fastapi-slim.tool.pdm.build]
|
||||
# excludes needs to explicitly exclude the top level python packages,
|
||||
# otherwise PDM includes them by default
|
||||
# A "*" glob pattern can't be used here because in PDM internals, the patterns are put
|
||||
# in a set (unordered, order varies) and each excluded file is assigned one of the
|
||||
# glob patterns that matches, as the set is unordered, the matched pattern could be "*"
|
||||
# independent of the order here. And then the internal code would give it a lower score
|
||||
# than the one for a default included file.
|
||||
# By not using "*" and explicitly excluding the top level packages, they get a higher
|
||||
# score than the default inclusion
|
||||
excludes = ["fastapi", "tests", "pdm_build.py"]
|
||||
# source-includes needs to explicitly define some value because PDM will check the
|
||||
# truthy value of the list, and if empty, will include some defaults, including "tests",
|
||||
# an empty string doesn't match anything, but makes the list truthy, so that PDM
|
||||
# doesn't override it during the build.
|
||||
source-includes = [""]
|
||||
|
||||
[tool.mypy]
|
||||
plugins = ["pydantic.mypy"]
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
from fastapi.dependencies.utils import get_typed_annotation
|
||||
|
||||
|
||||
def test_get_typed_annotation():
|
||||
# For coverage
|
||||
annotation = "None"
|
||||
typed_annotation = get_typed_annotation(annotation, globals())
|
||||
assert typed_annotation is None
|
||||
Reference in New Issue
Block a user