Fastapi: Extracting both summary and description from path function docstring in PEP 257 compliant way

Created on 26 Jun 2020  路  7Comments  路  Source: tiangolo/fastapi

First check

  • [X] I added a very descriptive title to this issue.
  • [X] I used the GitHub search to find a similar issue and didn't find it.
  • [X] I searched the FastAPI documentation, with the integrated search.
  • [X] I already searched in Google "How to X in FastAPI" and didn't find any information.
  • [X] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [X] I already checked if it is not related to FastAPI but to Pydantic.
  • [X] I already checked if it is not related to FastAPI but to Swagger UI.
  • [X] I already checked if it is not related to FastAPI but to ReDoc.
  • [X] After submitting this, I commit to:

    • Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

    • Or, I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

    • Implement a Pull Request for a confirmed bug.

Recent library adopter and first-time issue opener here. :) Thanks to the team for the great library!

The library has a helpful feature of extracting OpenAPI path documentation from the docstring of the handler function. However, I find the feature counter-intuitive from PEP 257 point of view. The best practices defined by the PEP defines that a multi-line docstring consists of a summary line and an elaborate description. I believe that, instead of using the whole docstring as description, the summary line and description should be mapped to OpenAPI summary _and_ description, respectively.

I'm willing to contribute the PR, but would like to clear my idea with the maintainers first.

Example

Here's a self-contained minimal, reproducible, example with my use case:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def hello():
    """Respond with the friendly greeting

    This is a long long description of the API call.
    """
    return { "greeting": "Hello, world!" }

Description

  • Open the browser and call the endpoint /openapi.json.
  • It returns a the following JSON doument
{
  "openapi": "3.0.2",
  "info": {
    "title": "FastAPI",
    "version": "0.1.0"
  },
  "paths": {
    "/": {
      "get": {
        "summary": "Hello",
        "description": "Respond with the friendly greeting\n\nThis is a long long description of the API call.",
        "operationId": "hello__get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    }
  }
}
  • I would instead expect the summary line and the description in the docstring to be mapped to the summary and the description of the OpenAPI document, respectively:
{
  "openapi": "3.0.2",
  "info": {
    "title": "FastAPI",
    "version": "0.1.0"
  },
  "paths": {
    "/": {
      "get": {
        "summary": "Respond with the friendly greeting",
        "description": "This is a long long description of the API call.",
        "operationId": "hello__get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    }
  }
}

The solution you would like

I realize that this is a backward-incompatible change. Nevertheless, I think it's justified for compliance with docstring best practices and a more natural way to write documentation. I suggest the following to mitigate the impact:

  • If the route decorator has both summary and description set, ignore the docstring
  • If the route decorator has summary set but description not set, interpret the whole docstring as a description (compatible with the current behavior)
  • If the route decorator has neither summary nor description set, populate both summary and description from the docstring as described above

As I said above, I'll be happy to write a PR for this, but I would like to get feedback on the idea first.

Describe alternatives you've considered

What I described can currently be done with decorator arguments, so this feature is just for enhancing productivity and, arguably, the cleanliness of documentation. The alternative is to document the paths like so far.

Environment

  • OS: Linux
  • FastAPI Version: 0.58.0
  • Python version: 3.8.2
answered enhancement

All 7 comments

I would like to add that it would be even better if Python docstrings for parameters were also extracted from the description, in Sphinx notation:

def foobar(id: int):
    """
    Get a single Item

    Returns the given item from its id.

    :param id: The item id, e.g. "2019asa3e8938" 
    """

I would like to add that it would be even better if Python docstrings for parameters were also extracted from the description, in Sphinx notation:

def foobar(id: int):
    """
    Get a single Item

    Returns the given item from its id.

    :param id: The item id, e.g. "2019asa3e8938" 
    """

Nice idea! I submitted a PR for the original issue but this is a good follow-up.

I think the main idea about the summary and description makes sense, but it will break all my documentation, so I'm skeptical about it. About having the parameters as well, I don't see the real usefulness of having it, considering that we already a nice way to do it and it adds an unnecessary complexity.

Does the documentation have to be mutually exclusive? The usefulness would be to be able to auto-generate user documentation using Sphinx that embeds the API documentation (for example using readthedocs.org, which allows cross-referencing and all that), and it's the de-facto standard way to do Python API documentation.

From what it appears to me, the guiding principle of FastAPI design is that it's possible to do things in multiple ways: One is the DWIM way which should work most of the time. Example: If I just say that a route handler accepts a parameter X, the library figures out from the context if X is a path, query or body parameter, and is correct most of the time. But there is a way to fine-tune everything by explicit configuration. So I can have a plain number from the request body if I want to.

I don't see why this couldn't be applied to documentation. Sphinx autodoc is widely used in the Python community, and allowing developers write API documentation just as they are used to documenting all their functions certainly qualifies as DWIM.

Breaking existing documentation is a valid concern. As a random first-time-contributor-hopeful I can't really decide if it's a blocker, and to which extent the library guarantees backward compatibility.

Thanks for the interest and discussion everyone!

But I see it would be cumbersome to support, being a breaking change, forcing everyone to change to the new behavior, it wouldn't be easy to opt-out, and it would also conflict with the auto-generated summary based on function names.

I also prefer the keyword argument summary that can be found with autocompletion. :shrug:

So I would prefer to keep it as it is. But thanks for the discussion! :coffee:


About the format, OpenAPI supports Markdown, and the docs (Swagger UI) render Markdown. You are free to use reStructuredText if you prefer that, but it won't be rendered by Swagger UI.

If you want to have both, you can add a \f after the main part to add additional info, it won't be added to OpenAPI, and you can extract it for Sphinx if you want, you can check more details here: https://github.com/tiangolo/fastapi/pull/556

Hi @tiangolo,
Thanks for taking the time for detailed response. Fair enough, I'll close this issue as won't do.

Was this page helpful?
0 / 5 - 0 ratings