Browse Source

Merge branch 'master' into fix-doc-typo-advanced-dependencies

pull/14815/head
Rayyan oumlil 5 months ago
committed by GitHub
parent
commit
5fefadd8c5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      docs/en/docs/release-notes.md
  2. 11
      docs/en/docs/translation-banner.md
  3. 11
      docs/ru/docs/translation-banner.md
  4. 2
      fastapi/dependencies/utils.py
  5. 3
      fastapi/openapi/utils.py
  6. 28
      scripts/mkdocs_hooks.py
  7. 123
      tests/test_additional_responses_union_duplicate_anyof.py
  8. 20
      tests/test_invalid_sequence_param.py

9
docs/en/docs/release-notes.md

@ -7,12 +7,21 @@ hide:
## Latest Changes
### Features
* 🚸 Improve error message for invalid query parameter type annotations. PR [#14479](https://github.com/fastapi/fastapi/pull/14479) by [@retwish](https://github.com/retwish).
### Fixes
* 🐛 Fix OpenAPI duplication of `anyOf` refs for app-level responses with specified `content` and `model` as `Union`. PR [#14463](https://github.com/fastapi/fastapi/pull/14463) by [@DJMcoder](https://github.com/DJMcoder).
### Refactors
* 💡 Update comment for Pydantic internals. PR [#14814](https://github.com/fastapi/fastapi/pull/14814) by [@tiangolo](https://github.com/tiangolo).
### Docs
* 📝 Add banner to translated pages. PR [#14809](https://github.com/fastapi/fastapi/pull/14809) by [@YuriiMotov](https://github.com/YuriiMotov).
* 📝 Add links to related sections of docs to docstrings. PR [#14776](https://github.com/fastapi/fastapi/pull/14776) by [@YuriiMotov](https://github.com/YuriiMotov).
* 📝 Update embedded code examples to Python 3.10 syntax. PR [#14758](https://github.com/fastapi/fastapi/pull/14758) by [@YuriiMotov](https://github.com/YuriiMotov).
* 📝 Fix dependency installation command in `docs/en/docs/contributing.md`. PR [#14757](https://github.com/fastapi/fastapi/pull/14757) by [@YuriiMotov](https://github.com/YuriiMotov).

11
docs/en/docs/translation-banner.md

@ -0,0 +1,11 @@
/// details | 🌐 Translation by AI and humans
This translation was made by AI guided by humans. 🤝
It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖
You can improve this translation by [helping us guide the AI LLM better](https://fastapi.tiangolo.com/contributing/#translations).
[English version](ENGLISH_VERSION_URL)
///

11
docs/ru/docs/translation-banner.md

@ -0,0 +1,11 @@
/// details | 🌐 Перевод выполнен с помощью ИИ и людей
Этот перевод был сделан ИИ под руководством людей. 🤝
В нем могут быть ошибки из-за неправильного понимания оригинального смысла или неестественности и т. д. 🤖
Вы можете улучшить этот перевод, [помогая нам лучше направлять ИИ LLM](https://fastapi.tiangolo.com/ru/contributing/#translations).
[Английская версия](ENGLISH_VERSION_URL)
///

2
fastapi/dependencies/utils.py

@ -519,7 +519,7 @@ def analyze_param(
# For Pydantic v1
and getattr(field, "shape", 1) == 1
)
)
), f"Query parameter {param_name!r} must be one of the supported types"
return ParamDetails(type_annotation=type_annotation, depends=depends, field=field)

3
fastapi/openapi/utils.py

@ -1,3 +1,4 @@
import copy
import http.client
import inspect
import warnings
@ -377,7 +378,7 @@ def get_openapi_path(
additional_status_code,
additional_response,
) in route.responses.items():
process_response = additional_response.copy()
process_response = copy.deepcopy(additional_response)
process_response.pop("model", None)
status_code_key = str(additional_status_code).upper()
if status_code_key == "DEFAULT":

28
scripts/mkdocs_hooks.py

@ -26,6 +26,17 @@ def get_missing_translation_content(docs_dir: str) -> str:
return missing_translation_path.read_text(encoding="utf-8")
@lru_cache
def get_translation_banner_content(docs_dir: str) -> str:
docs_dir_path = Path(docs_dir)
translation_banner_path = docs_dir_path / "translation-banner.md"
if not translation_banner_path.is_file():
translation_banner_path = (
docs_dir_path.parent.parent / "en" / "docs" / "translation-banner.md"
)
return translation_banner_path.read_text(encoding="utf-8")
@lru_cache
def get_mkdocs_material_langs() -> list[str]:
material_path = Path(material.__file__).parent
@ -151,4 +162,21 @@ def on_page_markdown(
if markdown.startswith("#"):
header, _, body = markdown.partition("\n\n")
return f"{header}\n\n{missing_translation_content}\n\n{body}"
docs_dir_path = Path(config.docs_dir)
en_docs_dir_path = docs_dir_path.parent.parent / "en/docs"
if docs_dir_path == en_docs_dir_path:
return markdown
# For translated pages add translation banner
translation_banner_content = get_translation_banner_content(config.docs_dir)
en_url = "https://fastapi.tiangolo.com/" + page.url.lstrip("/")
translation_banner_content = translation_banner_content.replace(
"ENGLISH_VERSION_URL", en_url
)
header = ""
body = markdown
if markdown.startswith("#"):
header, _, body = markdown.partition("\n\n")
return f"{header}\n\n{translation_banner_content}\n\n{body}"

123
tests/test_additional_responses_union_duplicate_anyof.py

@ -0,0 +1,123 @@
"""
Regression test: Ensure app-level responses with Union models and content/examples
don't accumulate duplicate $ref entries in anyOf arrays.
See https://github.com/fastapi/fastapi/pull/14463
"""
from typing import Union
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
class ModelA(BaseModel):
a: str
class ModelB(BaseModel):
b: str
app = FastAPI(
responses={
500: {
"model": Union[ModelA, ModelB],
"content": {"application/json": {"examples": {"Case A": {"value": "a"}}}},
}
}
)
@app.get("/route1")
async def route1():
pass # pragma: no cover
@app.get("/route2")
async def route2():
pass # pragma: no cover
client = TestClient(app)
def test_openapi_schema():
response = client.get("/openapi.json")
assert response.status_code == 200, response.text
assert response.json() == {
"openapi": "3.1.0",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/route1": {
"get": {
"summary": "Route1",
"operationId": "route1_route1_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"anyOf": [
{"$ref": "#/components/schemas/ModelA"},
{"$ref": "#/components/schemas/ModelB"},
],
"title": "Response 500 Route1 Route1 Get",
},
"examples": {"Case A": {"value": "a"}},
}
},
},
},
}
},
"/route2": {
"get": {
"summary": "Route2",
"operationId": "route2_route2_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
},
"500": {
"description": "Internal Server Error",
"content": {
"application/json": {
"schema": {
"anyOf": [
{"$ref": "#/components/schemas/ModelA"},
{"$ref": "#/components/schemas/ModelB"},
],
"title": "Response 500 Route2 Route2 Get",
},
"examples": {"Case A": {"value": "a"}},
}
},
},
},
}
},
},
"components": {
"schemas": {
"ModelA": {
"properties": {"a": {"type": "string", "title": "A"}},
"type": "object",
"required": ["a"],
"title": "ModelA",
},
"ModelB": {
"properties": {"b": {"type": "string", "title": "B"}},
"type": "object",
"required": ["b"],
"title": "ModelB",
},
}
},
}

20
tests/test_invalid_sequence_param.py

@ -6,7 +6,10 @@ from pydantic import BaseModel
def test_invalid_sequence():
with pytest.raises(AssertionError):
with pytest.raises(
AssertionError,
match="Query parameter 'q' must be one of the supported types",
):
app = FastAPI()
class Item(BaseModel):
@ -18,7 +21,10 @@ def test_invalid_sequence():
def test_invalid_tuple():
with pytest.raises(AssertionError):
with pytest.raises(
AssertionError,
match="Query parameter 'q' must be one of the supported types",
):
app = FastAPI()
class Item(BaseModel):
@ -30,7 +36,10 @@ def test_invalid_tuple():
def test_invalid_dict():
with pytest.raises(AssertionError):
with pytest.raises(
AssertionError,
match="Query parameter 'q' must be one of the supported types",
):
app = FastAPI()
class Item(BaseModel):
@ -42,7 +51,10 @@ def test_invalid_dict():
def test_invalid_simple_dict():
with pytest.raises(AssertionError):
with pytest.raises(
AssertionError,
match="Query parameter 'q' must be one of the supported types",
):
app = FastAPI()
class Item(BaseModel):

Loading…
Cancel
Save