- --unsafe
- id: end-of-file-fixer
- id: trailing-whitespace
-- repo: https://github.com/asottile/pyupgrade
- rev: v3.7.0
- hooks:
- - id: pyupgrade
- args:
- - --py3-plus
- - --keep-runtime-typing
- repo: https://github.com/charliermarsh/ruff-pre-commit
- rev: v0.0.275
+ rev: v0.1.2
hooks:
- id: ruff
args:
- --fix
-- repo: https://github.com/psf/black
- rev: 23.3.0
- hooks:
- - id: black
+ - id: ruff-format
ci:
autofix_commit_msg: ๐จ [pre-commit.ci] Auto format from pre-commit.com hooks
autoupdate_commit_msg: โฌ [pre-commit.ci] pre-commit autoupdate
```
!!! tip
- ๐ฅ ๐ ๐ฌ `@lru_cache()` ๐.
+ ๐ฅ ๐ ๐ฌ `@lru_cache` ๐.
๐ ๐ ๐ช ๐ค `get_settings()` ๐ ๐ข.
๐ฅ ๐ โ ๐ ๐ ๐ ๐จ, & ๐ฅ ๐ ๐ `.env` ๐ ๐ ๐จ. ๐ถ ๐ถ
-โ๏ธ ๐ฅ โ๏ธ `@lru_cache()` ๐จโ๐จ ๐ ๐, `Settings` ๐ ๐ โ ๐ด ๐, ๐ฅ ๐ฐ โซ๏ธ ๐ค. ๐ถ ๐ถ
+โ๏ธ ๐ฅ โ๏ธ `@lru_cache` ๐จโ๐จ ๐ ๐, `Settings` ๐ ๐ โ ๐ด ๐, ๐ฅ ๐ฐ โซ๏ธ ๐ค. ๐ถ ๐ถ
```Python hl_lines="1 10"
{!../../../docs_src/settings/app03/main.py!}
#### `lru_cache` ๐ก โน
-`@lru_cache()` ๐ ๐ข โซ๏ธ ๐ ๐จ ๐ ๐ฒ ๐ ๐จ ๐ฅ ๐ฐ, โฉ๏ธ ๐ป โซ๏ธ ๐, ๐ ๏ธ ๐ ๐ข ๐ ๐ฐ.
+`@lru_cache` ๐ ๐ข โซ๏ธ ๐ ๐จ ๐ ๐ฒ ๐ ๐จ ๐ฅ ๐ฐ, โฉ๏ธ ๐ป โซ๏ธ ๐, ๐ ๏ธ ๐ ๐ข ๐ ๐ฐ.
, ๐ข ๐ โซ๏ธ ๐ ๐ ๏ธ ๐ ๐ ๐ โ. & โคด๏ธ ๐ฒ ๐จ ๐ ๐ ๐ โ ๐ โ๏ธ ๐ & ๐ ๐โ ๐ข ๐ค โฎ๏ธ โซ๏ธโ ๐ ๐ โ.
๐ผ, ๐ฅ ๐ โ๏ธ ๐ข:
```Python
-@lru_cache()
+@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
๐ ๐, โซ๏ธ ๐ญ ๐ ๐ฅ โซ๏ธ ๐ ๐ข. โ๏ธ โซ๏ธ โ๏ธ ๐ ๐ข, โคด๏ธ ๐ฅ ๐ช ๐ โซ๏ธ ๐ช ๐ฌ.
-`@lru_cache()` ๐ `functools` โ ๐ ๐ ๐ฉ ๐, ๐ ๐ช โ ๐
๐ โซ๏ธ <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">๐ ๐ฉบ `@lru_cache()`</a>.
+`@lru_cache` ๐ `functools` โ ๐ ๐ ๐ฉ ๐, ๐ ๐ช โ ๐
๐ โซ๏ธ <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">๐ ๐ฉบ `@lru_cache`</a>.
## ๐
* โ๏ธ ๐ ๐ ๐ช ๐ ๐ฌ.
* ๐ ๐ช โ๏ธ `.env` ๐ โฎ๏ธ โซ๏ธ.
-* โ๏ธ `@lru_cache()` โก๏ธ ๐ โ ๐ ๐จ๐ป ๐ ๐ & ๐ ๐ ๐จ, โช ๐ค ๐ ๐ โซ๏ธ โฎ๏ธ ๐ฌ.
+* โ๏ธ `@lru_cache` โก๏ธ ๐ โ ๐ ๐จ๐ป ๐ ๐ & ๐ ๐ ๐จ, โช ๐ค ๐ ๐ โซ๏ธ โฎ๏ธ ๐ฌ.
=== "๐ 3๏ธโฃ.1๏ธโฃ0๏ธโฃ & ๐"
- ```Python hl_lines="12"
+ ```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!}
```
=== "๐ 3๏ธโฃ.1๏ธโฃ0๏ธโฃ & ๐"
- ```Python hl_lines="17"
+ ```Python hl_lines="16"
{!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!}
```
```
!!! tip
- We'll discuss the `@lru_cache()` in a bit.
+ We'll discuss the `@lru_cache` in a bit.
For now you can assume `get_settings()` is a normal function.
we would create that object for each request, and we would be reading the `.env` file for each request. โ ๏ธ
-But as we are using the `@lru_cache()` decorator on top, the `Settings` object will be created only once, the first time it's called. โ๏ธ
+But as we are using the `@lru_cache` decorator on top, the `Settings` object will be created only once, the first time it's called. โ๏ธ
=== "Python 3.9+"
#### `lru_cache` Technical Details
-`@lru_cache()` modifies the function it decorates to return the same value that was returned the first time, instead of computing it again, executing the code of the function every time.
+`@lru_cache` modifies the function it decorates to return the same value that was returned the first time, instead of computing it again, executing the code of the function every time.
So, the function below it will be executed once for each combination of arguments. And then the values returned by each of those combinations of arguments will be used again and again whenever the function is called with exactly the same combination of arguments.
For example, if you have a function:
```Python
-@lru_cache()
+@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
That way, it behaves almost as if it was just a global variable. But as it uses a dependency function, then we can override it easily for testing.
-`@lru_cache()` is part of `functools` which is part of Python's standard library, you can read more about it in the <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">Python docs for `@lru_cache()`</a>.
+`@lru_cache` is part of `functools` which is part of Python's standard library, you can read more about it in the <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">Python docs for `@lru_cache`</a>.
## Recap
* By using a dependency you can simplify testing.
* You can use `.env` files with it.
-* Using `@lru_cache()` lets you avoid reading the dotenv file again and again for each request, while allowing you to override it during testing.
+* Using `@lru_cache` lets you avoid reading the dotenv file again and again for each request, while allowing you to override it during testing.
!!! tip
Prefer to use the `Annotated` version if possible.
- ```Python hl_lines="12"
+ ```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!}
```
!!! tip
Prefer to use the `Annotated` version if possible.
- ```Python hl_lines="17"
+ ```Python hl_lines="16"
{!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!}
```
!!! tip "ะะพะดัะบะฐะทะบะฐ"
ะ ะตะบะพะผะตะฝะดัะตััั ะธัะฟะพะปัะทะพะฒะฐัั ะฒะตััะธั ั `Annotated` ะตัะปะธ ะฒะพะทะผะพะถะฝะพ.
- ```Python hl_lines="12"
+ ```Python hl_lines="11"
{!> ../../../docs_src/query_params_str_validations/tutorial008_py310.py!}
```
!!! tip "ะะพะดัะบะฐะทะบะฐ"
ะ ะตะบะพะผะตะฝะดัะตััั ะธัะฟะพะปัะทะพะฒะฐัั ะฒะตััะธั ั `Annotated` ะตัะปะธ ะฒะพะทะผะพะถะฝะพ.
- ```Python hl_lines="17"
+ ```Python hl_lines="16"
{!> ../../../docs_src/query_params_str_validations/tutorial010_py310.py!}
```
```
!!! tip
- ๆไปฌ็จๅไผ่ฎจ่ฎบ `@lru_cache()`ใ
+ ๆไปฌ็จๅไผ่ฎจ่ฎบ `@lru_cache`ใ
็ฎๅ๏ผๆจๅฏไปฅๅฐ `get_settings()` ่งไธบๆฎ้ๅฝๆฐใ
ๆไปฌๅฐไธบๆฏไธช่ฏทๆฑๅๅปบ่ฏฅๅฏน่ฑก๏ผๅนถไธๅฐๅจๆฏไธช่ฏทๆฑไธญ่ฏปๅ `.env` ๆไปถใ โ ๏ธ
-ไฝๆฏ๏ผ็ฑไบๆไปฌๅจ้กถ้จไฝฟ็จไบ `@lru_cache()` ่ฃ
้ฅฐๅจ๏ผๅ ๆญคๅชๆๅจ็ฌฌไธๆฌก่ฐ็จๅฎๆถ๏ผๆไผๅๅปบ `Settings` ๅฏน่ฑกไธๆฌกใ โ๏ธ
+ไฝๆฏ๏ผ็ฑไบๆไปฌๅจ้กถ้จไฝฟ็จไบ `@lru_cache` ่ฃ
้ฅฐๅจ๏ผๅ ๆญคๅชๆๅจ็ฌฌไธๆฌก่ฐ็จๅฎๆถ๏ผๆไผๅๅปบ `Settings` ๅฏน่ฑกไธๆฌกใ โ๏ธ
=== "Python 3.9+"
#### `lru_cache` ๆๆฏ็ป่
-`@lru_cache()` ไฟฎๆนไบๅฎๆ่ฃ
้ฅฐ็ๅฝๆฐ๏ผไปฅ่ฟๅ็ฌฌไธๆฌก่ฟๅ็็ธๅๅผ๏ผ่ไธๆฏๅๆฌก่ฎก็ฎๅฎ๏ผๆฏๆฌก้ฝๆง่กๅฝๆฐ็ไปฃ็ ใ
+`@lru_cache` ไฟฎๆนไบๅฎๆ่ฃ
้ฅฐ็ๅฝๆฐ๏ผไปฅ่ฟๅ็ฌฌไธๆฌก่ฟๅ็็ธๅๅผ๏ผ่ไธๆฏๅๆฌก่ฎก็ฎๅฎ๏ผๆฏๆฌก้ฝๆง่กๅฝๆฐ็ไปฃ็ ใ
ๅ ๆญค๏ผไธ้ข็ๅฝๆฐๅฐๅฏนๆฏไธชๅๆฐ็ปๅๆง่กไธๆฌกใ็ถๅ๏ผๆฏไธชๅๆฐ็ปๅ่ฟๅ็ๅผๅฐๅจไฝฟ็จๅฎๅ
จ็ธๅ็ๅๆฐ็ปๅ่ฐ็จๅฝๆฐๆถๅๆฌกไฝฟ็จใ
ไพๅฆ๏ผๅฆๆๆจๆไธไธชๅฝๆฐ๏ผ
```Python
-@lru_cache()
+@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
```
่ฟๆ ท๏ผๅฎ็่กไธบๅ ไนๅฐฑๅๆฏไธไธชๅ
จๅฑๅ้ใไฝๆฏ็ฑไบๅฎไฝฟ็จไบไพ่ต้กนๅฝๆฐ๏ผๅ ๆญคๆไปฌๅฏไปฅ่ฝปๆพๅฐ่ฟ่กๆต่ฏๆถ็่ฆ็ใ
-`@lru_cache()` ๆฏ `functools` ็ไธ้จๅ๏ผๅฎๆฏ Python ๆ ๅๅบ็ไธ้จๅ๏ผๆจๅฏไปฅๅจ<a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">Python ๆๆกฃไธญไบ่งฃๆๅ
ณ `@lru_cache()` ็ๆดๅคไฟกๆฏ</a>ใ
+`@lru_cache` ๆฏ `functools` ็ไธ้จๅ๏ผๅฎๆฏ Python ๆ ๅๅบ็ไธ้จๅ๏ผๆจๅฏไปฅๅจ<a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">Python ๆๆกฃไธญไบ่งฃๆๅ
ณ `@lru_cache` ็ๆดๅคไฟกๆฏ</a>ใ
## ๅฐ็ป
* ้่ฟไฝฟ็จไพ่ต้กน๏ผๆจๅฏไปฅ็ฎๅๆต่ฏใ
* ๆจๅฏไปฅไฝฟ็จ `.env` ๆไปถใ
-* ไฝฟ็จ `@lru_cache()` ๅฏไปฅ้ฟๅ
ไธบๆฏไธช่ฏทๆฑ้ๅค่ฏปๅ dotenv ๆไปถ๏ผๅๆถๅ
่ฎธๆจๅจๆต่ฏๆถ่ฟ่ก่ฆ็ใ
+* ไฝฟ็จ `@lru_cache` ๅฏไปฅ้ฟๅ
ไธบๆฏไธช่ฏทๆฑ้ๅค่ฏปๅ dotenv ๆไปถ๏ผๅๆถๅ
่ฎธๆจๅจๆต่ฏๆถ่ฟ่ก่ฆ็ใ
async def read_items(
strange_header: Annotated[
Union[str, None], Header(convert_underscores=False)
- ] = None
+ ] = None,
):
return {"strange_header": strange_header}
async def read_items(
strange_header: Annotated[
Union[str, None], Header(convert_underscores=False)
- ] = None
+ ] = None,
):
return {"strange_header": strange_header}
async def read_items(
q: Union[str, None] = Query(
default=None, min_length=3, max_length=50, pattern="^fixedquery$"
- )
+ ),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
async def read_items(
q: Annotated[
Union[str, None], Query(min_length=3, max_length=50, pattern="^fixedquery$")
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
async def read_items(
q: Annotated[
str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
async def read_items(
q: Annotated[
str | None, Query(min_length=3, max_length=50, regex="^fixedquery$")
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
async def read_items(
q: Annotated[
Union[str, None], Query(min_length=3, max_length=50, pattern="^fixedquery$")
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
@app.get("/items/")
async def read_items(
- q: str
- | None = Query(default=None, min_length=3, max_length=50, pattern="^fixedquery$")
+ q: str | None = Query(
+ default=None, min_length=3, max_length=50, pattern="^fixedquery$"
+ ),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
- )
+ ),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
@app.get("/items/")
async def read_items(
- q: str
- | None = Query(
+ q: str | None = Query(
default=None,
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
- )
+ ),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
max_length=50,
pattern="^fixedquery$",
deprecated=True,
- )
+ ),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
pattern="^fixedquery$",
deprecated=True,
),
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
pattern="^fixedquery$",
deprecated=True,
),
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
pattern="^fixedquery$",
deprecated=True,
),
- ] = None
+ ] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
@app.get("/items/")
async def read_items(
- q: str
- | None = Query(
+ q: str | None = Query(
default=None,
alias="item-query",
title="Query string",
max_length=50,
pattern="^fixedquery$",
deprecated=True,
- )
+ ),
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
app = FastAPI()
-@lru_cache()
+@lru_cache
def get_settings():
return Settings()
app = FastAPI()
-@lru_cache()
+@lru_cache
def get_settings():
return Settings()
app = FastAPI()
-@lru_cache()
+@lru_cache
def get_settings():
return Settings()
app = FastAPI()
-@lru_cache()
+@lru_cache
def get_settings():
return config.Settings()
app = FastAPI()
-@lru_cache()
+@lru_cache
def get_settings():
return config.Settings()
app = FastAPI()
-@lru_cache()
+@lru_cache
def get_settings():
return config.Settings()
if "$ref" not in json_schema:
# TODO remove when deprecating Pydantic v1
# Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207
- json_schema[
- "title"
- ] = field.field_info.title or field.alias.title().replace("_", " ")
+ json_schema["title"] = (
+ field.field_info.title or field.alias.title().replace("_", " ")
+ )
return json_schema
def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
[FastAPI docs for OpenAPI Webhooks](https://fastapi.tiangolo.com/advanced/openapi-webhooks/).
"""
),
- ] = (
- webhooks or routing.APIRouter()
- )
+ ] = webhooks or routing.APIRouter()
self.root_path = root_path or openapi_prefix
self.state: Annotated[
State,
)
self.exception_handlers: Dict[
Any, Callable[[Request, Any], Union[Response, Awaitable[Response]]]
- ] = ({} if exception_handlers is None else dict(exception_handlers))
+ ] = {} if exception_handlers is None else dict(exception_handlers)
self.exception_handlers.setdefault(HTTPException, http_exception_handler)
self.exception_handlers.setdefault(
RequestValidationError, request_validation_exception_handler
try:
data = b64decode(param).decode("ascii")
except (ValueError, UnicodeDecodeError, binascii.Error):
- raise invalid_user_credentials_exc
+ raise invalid_user_credentials_exc # noqa: B904
username, separator, password = data.partition(":")
if not separator:
raise invalid_user_credentials_exc
The list of all the scopes required by dependencies.
"""
),
- ] = (
- scopes or []
- )
+ ] = scopes or []
self.scope_str: Annotated[
str,
Doc(
]
if field.key_field: # type: ignore[attr-defined]
new_field.key_field = create_cloned_field( # type: ignore[attr-defined]
- field.key_field, cloned_types=cloned_types # type: ignore[attr-defined]
+ field.key_field, # type: ignore[attr-defined]
+ cloned_types=cloned_types,
)
new_field.validators = field.validators # type: ignore[attr-defined]
new_field.pre_validators = field.pre_validators # type: ignore[attr-defined]
"I", # isort
"C", # flake8-comprehensions
"B", # flake8-bugbear
+ "UP", # pyupgrade
]
ignore = [
"E501", # line too long, handled by black
"B008", # do not perform function calls in argument defaults
"C901", # too complex
+ "W191", # indentation contains tabs
]
[tool.ruff.per-file-ignores]
"docs_src/query_params_str_validations/tutorial012_an_py39.py" = ["B006"]
"docs_src/query_params_str_validations/tutorial013_an.py" = ["B006"]
"docs_src/query_params_str_validations/tutorial013_an_py39.py" = ["B006"]
+"docs_src/security/tutorial004.py" = ["B904"]
+"docs_src/security/tutorial004_an.py" = ["B904"]
+"docs_src/security/tutorial004_an_py310.py" = ["B904"]
+"docs_src/security/tutorial004_an_py39.py" = ["B904"]
+"docs_src/security/tutorial004_py310.py" = ["B904"]
+"docs_src/security/tutorial005.py" = ["B904"]
+"docs_src/security/tutorial005_an.py" = ["B904"]
+"docs_src/security/tutorial005_an_py310.py" = ["B904"]
+"docs_src/security/tutorial005_an_py39.py" = ["B904"]
+"docs_src/security/tutorial005_py310.py" = ["B904"]
+"docs_src/security/tutorial005_py39.py" = ["B904"]
+
[tool.ruff.isort]
known-third-party = ["fastapi", "pydantic", "starlette"]
+
+[tool.ruff.pyupgrade]
+# Preserve types, even if a file imports `from __future__ import annotations`.
+keep-runtime-typing = true
# For mkdocstrings and tests
httpx >=0.23.0,<0.25.0
-black == 23.3.0
cairosvg==2.7.0
mkdocstrings[python]==0.23.0
griffe-typingdoc==0.2.2
+# For griffe, it formats with black
+black==23.3.0
pytest >=7.1.3,<8.0.0
coverage[toml] >= 6.5.0,< 8.0
mypy ==1.4.1
-ruff ==0.0.275
+ruff ==0.1.2
email_validator >=1.1.1,<3.0.0
dirty-equals ==0.6.0
# TODO: once removing databases from tutorial, upgrade SQLAlchemy
build_site_path = Path("site_build").absolute()
-@lru_cache()
+@lru_cache
def is_mkdocs_insiders() -> bool:
version = metadata.version("mkdocs-material")
return "insiders" in version
def build_lang(
lang: str = typer.Argument(
..., callback=lang_callback, autocompletion=complete_existing_lang
- )
+ ),
) -> None:
"""
Build the docs for a language.
def live(
lang: str = typer.Argument(
None, callback=lang_callback, autocompletion=complete_existing_lang
- )
+ ),
) -> None:
"""
Serve with livereload a docs site for a specific language.
set -x
ruff fastapi tests docs_src scripts --fix
-black fastapi tests docs_src scripts
+ruff format fastapi tests docs_src scripts
mypy fastapi
ruff fastapi tests docs_src scripts
-black fastapi tests --check
+ruff format fastapi tests --check
]
-@lru_cache()
+@lru_cache
def get_missing_translation_content(docs_dir: str) -> str:
docs_dir_path = Path(docs_dir)
missing_translation_path = docs_dir_path.parent.parent / "missing-translation.md"
return missing_translation_path.read_text(encoding="utf-8")
-@lru_cache()
+@lru_cache
def get_mkdocs_material_langs() -> List[str]:
material_path = Path(material.__file__).parent
material_langs_path = material_path / "templates" / "partials" / "languages"
"value": {"data": "Data in Body examples, example2"},
},
},
- )
+ ),
):
return item
{"data": "Data in Body examples, example1"},
{"data": "Data in Body examples, example2"},
],
- )
+ ),
):
return item
{"data": "examples example_examples 1"},
{"data": "examples example_examples 2"},
],
- )
+ ),
):
return item