From c7dc26b7609f63d6f056b75f1050ab69a9356ef5 Mon Sep 17 00:00:00 2001 From: svalouch <54674660+svalouch@users.noreply.github.com> Date: Fri, 4 Oct 2019 22:02:40 +0200 Subject: [PATCH] :sparkles: Allow docstrings to be truncated before being used for OpenAPI (#556) --- .../tutorial003.py | 30 +++++ .../path-operation-advanced-configuration.md | 12 ++ fastapi/routing.py | 3 + .../test_tutorial003.py | 112 ++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 docs/src/path_operation_advanced_configuration/tutorial003.py create mode 100644 tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial003.py diff --git a/docs/src/path_operation_advanced_configuration/tutorial003.py b/docs/src/path_operation_advanced_configuration/tutorial003.py new file mode 100644 index 000000000..36bf02b11 --- /dev/null +++ b/docs/src/path_operation_advanced_configuration/tutorial003.py @@ -0,0 +1,30 @@ +from typing import Set + +from fastapi import FastAPI +from pydantic import BaseModel + +app = FastAPI() + + +class Item(BaseModel): + name: str + description: str = None + price: float + tax: float = None + tags: Set[str] = [] + + +@app.post("/items/", response_model=Item, summary="Create an item") +async def create_item(*, item: Item): + """ + Create an item with all the information: + + - **name**: each item must have a name + - **description**: a long description + - **price**: required + - **tax**: if the item doesn't have tax, you can omit this + - **tags**: a set of unique tag strings for this item + \f + :param item: User input. + """ + return item diff --git a/docs/tutorial/path-operation-advanced-configuration.md b/docs/tutorial/path-operation-advanced-configuration.md index 7d1b8e734..b316159c5 100644 --- a/docs/tutorial/path-operation-advanced-configuration.md +++ b/docs/tutorial/path-operation-advanced-configuration.md @@ -18,3 +18,15 @@ To exclude a path operation from the generated OpenAPI schema (and thus, from th ```Python hl_lines="6" {!./src/path_operation_advanced_configuration/tutorial002.py!} ``` + +## Advanced description from docstring + +You can limit the lines used from the docstring of a *path operation function* for OpenAPI. + +Adding an `\f` (an escaped "form feed" character) causes **FastAPI** to truncate the output used for OpenAPI at this point. + +It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest. + +```Python hl_lines="19 20 21 22 23 24 25 26 27 28 29" +{!./src/path_operation_advanced_configuration/tutorial003.py!} +``` diff --git a/fastapi/routing.py b/fastapi/routing.py index aeafd0718..8f61ea50c 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -246,6 +246,9 @@ class APIRoute(routing.Route): self.dependencies = [] self.summary = summary self.description = description or inspect.cleandoc(self.endpoint.__doc__ or "") + # if a "form feed" character (page break) is found in the description text, + # truncate description text to the content preceding the first "form feed" + self.description = self.description.split("\f")[0] self.response_description = response_description self.responses = responses or {} response_fields = {} diff --git a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial003.py b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial003.py new file mode 100644 index 000000000..9fae3160f --- /dev/null +++ b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial003.py @@ -0,0 +1,112 @@ +from starlette.testclient import TestClient + +from path_operation_advanced_configuration.tutorial003 import app + +client = TestClient(app) + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "Fast API", "version": "0.1.0"}, + "paths": { + "/items/": { + "post": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item"} + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + "summary": "Create an item", + "description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item\n", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item"} + } + }, + "required": True, + }, + } + } + }, + "components": { + "schemas": { + "Item": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "price": {"title": "Price", "type": "number"}, + "description": {"title": "Description", "type": "string"}, + "tax": {"title": "Tax", "type": "number"}, + "tags": { + "title": "Tags", + "uniqueItems": True, + "type": "array", + "items": {"type": "string"}, + "default": [], + }, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": {"type": "string"}, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + } + }, +} + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200 + assert response.json() == openapi_schema + + +def test_query_params_str_validations(): + response = client.post("/items/", json={"name": "Foo", "price": 42}) + assert response.status_code == 200 + assert response.json() == { + "name": "Foo", + "price": 42, + "description": None, + "tax": None, + "tags": [], + }