diff --git a/fastapi/applications.py b/fastapi/applications.py index f1d405221..b6b4538d2 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -1,5 +1,8 @@ from typing import Any, Callable, Dict, List, Optional, Type +from fastapi import routing +from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html +from fastapi.openapi.utils import get_openapi from pydantic import BaseModel from starlette.applications import Starlette from starlette.exceptions import ExceptionMiddleware, HTTPException @@ -8,10 +11,6 @@ from starlette.middleware.lifespan import LifespanMiddleware from starlette.requests import Request from starlette.responses import JSONResponse, Response -from fastapi import routing -from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html -from fastapi.openapi.utils import get_openapi - async def http_exception(request: Request, exc: HTTPException) -> JSONResponse: return JSONResponse({"detail": exc.detail}, status_code=exc.status_code) diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py index 5857f9202..da5d9b72d 100644 --- a/fastapi/dependencies/models.py +++ b/fastapi/dependencies/models.py @@ -1,5 +1,6 @@ from typing import Any, Callable, Dict, List, Sequence, Tuple +from fastapi.security.base import SecurityBase from pydantic import BaseConfig, Schema from pydantic.error_wrappers import ErrorWrapper from pydantic.errors import MissingError @@ -8,8 +9,6 @@ from pydantic.schema import get_annotation_from_schema from starlette.concurrency import run_in_threadpool from starlette.requests import Request -from fastapi.security.base import SecurityBase - param_supported_types = (str, int, float, bool) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 834774e1b..d91c83255 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -3,6 +3,10 @@ import inspect from copy import deepcopy from typing import Any, Callable, Dict, List, Mapping, Sequence, Tuple, Type +from fastapi import params +from fastapi.dependencies.models import Dependant, SecurityRequirement +from fastapi.security.base import SecurityBase +from fastapi.utils import get_path_param_names from pydantic import BaseConfig, Schema, create_model from pydantic.error_wrappers import ErrorWrapper from pydantic.errors import MissingError @@ -12,11 +16,6 @@ from pydantic.utils import lenient_issubclass from starlette.concurrency import run_in_threadpool from starlette.requests import Request -from fastapi import params -from fastapi.dependencies.models import Dependant, SecurityRequirement -from fastapi.security.base import SecurityBase -from fastapi.utils import get_path_param_names - param_supported_types = (str, int, float, bool) @@ -26,7 +25,6 @@ def get_sub_dependant(*, param: inspect.Parameter, path: str) -> Dependant: dependency = depends.dependency else: dependency = param.annotation - assert callable(dependency) sub_dependant = get_dependant(path=path, call=dependency, name=param.name) if isinstance(depends, params.Security) and isinstance(dependency, SecurityBase): security_requirement = SecurityRequirement( @@ -119,18 +117,18 @@ def add_param_to_fields( if isinstance(default_value, params.Param): schema = default_value default_value = schema.default - if schema.in_ is None: + if getattr(schema, "in_", None) is None: schema.in_ = default_schema.in_ if force_type: schema.in_ = force_type else: schema = default_schema(default_value) required = default_value == Required - annotation: Type = Type[Any] + annotation: Any = Any if not param.annotation == param.empty: annotation = param.annotation annotation = get_annotation_from_schema(annotation, schema) - if not schema.alias and getattr(schema, "alias_underscore_to_hyphen", None): + if not schema.alias and getattr(schema, "convert_underscores", None): alias = param.name.replace("_", "-") else: alias = schema.alias or param.name diff --git a/fastapi/openapi/constants.py b/fastapi/openapi/constants.py index 1d94a3377..3b50b05bd 100644 --- a/fastapi/openapi/constants.py +++ b/fastapi/openapi/constants.py @@ -1,2 +1,2 @@ -METHODS_WITH_BODY = set(("POST", "PUT")) +METHODS_WITH_BODY = set(("POST", "PUT", "DELETE", "PATCH")) REF_PREFIX = "#/components/schemas/" diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 1036d2012..bb75a024e 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -1,12 +1,5 @@ from typing import Any, Dict, List, Optional, Sequence, Tuple, Type -from pydantic.fields import Field -from pydantic.schema import Schema, field_schema, get_model_name_map -from pydantic.utils import lenient_issubclass -from starlette.responses import HTMLResponse, JSONResponse -from starlette.routing import BaseRoute, Route -from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY - from fastapi import routing from fastapi.dependencies.models import Dependant from fastapi.dependencies.utils import get_flat_dependant @@ -15,6 +8,12 @@ from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX from fastapi.openapi.models import OpenAPI from fastapi.params import Body, Param from fastapi.utils import get_flat_models_from_routes, get_model_definitions +from pydantic.fields import Field +from pydantic.schema import Schema, field_schema, get_model_name_map +from pydantic.utils import lenient_issubclass +from starlette.responses import HTMLResponse, JSONResponse +from starlette.routing import BaseRoute, Route +from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY validation_error_definition = { "title": "ValidationError", diff --git a/fastapi/params.py b/fastapi/params.py index 8df0112c8..ab71954ea 100644 --- a/fastapi/params.py +++ b/fastapi/params.py @@ -134,7 +134,7 @@ class Header(Param): *, deprecated: bool = None, alias: str = None, - alias_underscore_to_hyphen: bool = True, + convert_underscores: bool = True, title: str = None, description: str = None, gt: float = None, @@ -148,7 +148,7 @@ class Header(Param): ): self.description = description self.deprecated = deprecated - self.alias_underscore_to_hyphen = alias_underscore_to_hyphen + self.convert_underscores = convert_underscores super().__init__( default, alias=alias, diff --git a/fastapi/routing.py b/fastapi/routing.py index 8620db5db..4293f97ff 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -2,6 +2,10 @@ import asyncio import inspect from typing import Any, Callable, List, Optional, Type +from fastapi import params +from fastapi.dependencies.models import Dependant +from fastapi.dependencies.utils import get_body_field, get_dependant, solve_dependencies +from fastapi.encoders import jsonable_encoder from pydantic import BaseConfig, BaseModel, Schema from pydantic.error_wrappers import ErrorWrapper, ValidationError from pydantic.fields import Field @@ -15,11 +19,6 @@ from starlette.responses import JSONResponse, Response from starlette.routing import get_name, request_response from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY -from fastapi import params -from fastapi.dependencies.models import Dependant -from fastapi.dependencies.utils import get_body_field, get_dependant, solve_dependencies -from fastapi.encoders import jsonable_encoder - def serialize_response(*, field: Field = None, response: Response) -> Any: if field: diff --git a/fastapi/security/api_key.py b/fastapi/security/api_key.py index c4b045b71..12eba37ee 100644 --- a/fastapi/security/api_key.py +++ b/fastapi/security/api_key.py @@ -1,7 +1,6 @@ -from starlette.requests import Request - from fastapi.openapi.models import APIKey, APIKeyIn from fastapi.security.base import SecurityBase +from starlette.requests import Request class APIKeyBase(SecurityBase): diff --git a/fastapi/security/http.py b/fastapi/security/http.py index 480a1ae54..b1cba1921 100644 --- a/fastapi/security/http.py +++ b/fastapi/security/http.py @@ -1,10 +1,9 @@ -from starlette.requests import Request - from fastapi.openapi.models import ( HTTPBase as HTTPBaseModel, HTTPBearer as HTTPBearerModel, ) from fastapi.security.base import SecurityBase +from starlette.requests import Request class HTTPBase(SecurityBase): diff --git a/fastapi/security/oauth2.py b/fastapi/security/oauth2.py index 90838fdad..717a3287f 100644 --- a/fastapi/security/oauth2.py +++ b/fastapi/security/oauth2.py @@ -1,7 +1,39 @@ -from starlette.requests import Request +from typing import List, Optional from fastapi.openapi.models import OAuth2 as OAuth2Model, OAuthFlows as OAuthFlowsModel from fastapi.security.base import SecurityBase +from pydantic import BaseModel, Schema +from starlette.requests import Request + + +class OAuth2PasswordRequestData(BaseModel): + grant_type: str = "password" + username: str + password: str + scope: Optional[List[str]] = None + # Client ID and secret might come from headers + client_id: Optional[str] = None + client_secret: Optional[str] = None + + +class OAuth2PasswordRequestForm(BaseModel): + grant_type: str = Schema(..., regex="password") # it must have the value "password" + username: str + password: str + scope: str = "" + # Client ID and secret might come from headers + client_id: Optional[str] = None + client_secret: Optional[str] = None + + def parse(self) -> OAuth2PasswordRequestData: + return OAuth2PasswordRequestData( + grant_type=self.grant_type, + username=self.username, + password=self.password, + scope=self.scope.split(), + client_id=self.client_id, + client_secret=self.client_secret, + ) class OAuth2(SecurityBase): diff --git a/fastapi/security/open_id_connect_url.py b/fastapi/security/open_id_connect_url.py index b6c0a32dc..7d73ed81f 100644 --- a/fastapi/security/open_id_connect_url.py +++ b/fastapi/security/open_id_connect_url.py @@ -1,7 +1,6 @@ -from starlette.requests import Request - from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel from fastapi.security.base import SecurityBase +from starlette.requests import Request class OpenIdConnect(SecurityBase): diff --git a/fastapi/utils.py b/fastapi/utils.py index 81ca910cf..f3b4df82e 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -1,14 +1,13 @@ import re from typing import Any, Dict, List, Sequence, Set, Type +from fastapi import routing +from fastapi.openapi.constants import REF_PREFIX from pydantic import BaseModel from pydantic.fields import Field from pydantic.schema import get_flat_models_from_fields, model_process_schema from starlette.routing import BaseRoute -from fastapi import routing -from fastapi.openapi.constants import REF_PREFIX - def get_flat_models_from_routes( routes: Sequence[Type[BaseRoute]]