Coverage for fastapi/openapi/models.py: 100%

277 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-12-04 08:29 +0000

1from enum import Enum 1abcdefg

2from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Type, Union 1abcdefg

3 

4from fastapi._compat import ( 1abcdefg

5 PYDANTIC_V2, 

6 CoreSchema, 

7 GetJsonSchemaHandler, 

8 JsonSchemaValue, 

9 _model_rebuild, 

10 with_info_plain_validator_function, 

11) 

12from fastapi.logger import logger 1abcdefg

13from pydantic import AnyUrl, BaseModel, Field 1abcdefg

14from typing_extensions import Annotated, Literal, TypedDict 1abcdefg

15from typing_extensions import deprecated as typing_deprecated 1abcdefg

16 

17try: 1abcdefg

18 import email_validator 1abcdefg

19 

20 assert email_validator # make autoflake ignore the unused import 1abcdefg

21 from pydantic import EmailStr 1abcdefg

22except ImportError: # pragma: no cover 

23 

24 class EmailStr(str): # type: ignore 

25 @classmethod 

26 def __get_validators__(cls) -> Iterable[Callable[..., Any]]: 

27 yield cls.validate 

28 

29 @classmethod 

30 def validate(cls, v: Any) -> str: 

31 logger.warning( 

32 "email-validator not installed, email fields will be treated as str.\n" 

33 "To install, run: pip install email-validator" 

34 ) 

35 return str(v) 

36 

37 @classmethod 

38 def _validate(cls, __input_value: Any, _: Any) -> str: 

39 logger.warning( 

40 "email-validator not installed, email fields will be treated as str.\n" 

41 "To install, run: pip install email-validator" 

42 ) 

43 return str(__input_value) 

44 

45 @classmethod 

46 def __get_pydantic_json_schema__( 

47 cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler 

48 ) -> JsonSchemaValue: 

49 return {"type": "string", "format": "email"} 

50 

51 @classmethod 

52 def __get_pydantic_core_schema__( 

53 cls, source: Type[Any], handler: Callable[[Any], CoreSchema] 

54 ) -> CoreSchema: 

55 return with_info_plain_validator_function(cls._validate) 

56 

57 

58class BaseModelWithConfig(BaseModel): 1abcdefg

59 if PYDANTIC_V2: 1abcdefg

60 model_config = {"extra": "allow"} 1abcdefg

61 

62 else: 

63 

64 class Config: 1abcdef

65 extra = "allow" 1abcdef

66 

67 

68class Contact(BaseModelWithConfig): 1abcdefg

69 name: Optional[str] = None 1abcdefg

70 url: Optional[AnyUrl] = None 1abcdefg

71 email: Optional[EmailStr] = None 1abcdefg

72 

73 

74class License(BaseModelWithConfig): 1abcdefg

75 name: str 1abcdefg

76 identifier: Optional[str] = None 1abcdefg

77 url: Optional[AnyUrl] = None 1abcdefg

78 

79 

80class Info(BaseModelWithConfig): 1abcdefg

81 title: str 1abcdefg

82 summary: Optional[str] = None 1abcdefg

83 description: Optional[str] = None 1abcdefg

84 termsOfService: Optional[str] = None 1abcdefg

85 contact: Optional[Contact] = None 1abcdefg

86 license: Optional[License] = None 1abcdefg

87 version: str 1abcdefg

88 

89 

90class ServerVariable(BaseModelWithConfig): 1abcdefg

91 enum: Annotated[Optional[List[str]], Field(min_length=1)] = None 1abcdefg

92 default: str 1abcdefg

93 description: Optional[str] = None 1abcdefg

94 

95 

96class Server(BaseModelWithConfig): 1abcdefg

97 url: Union[AnyUrl, str] 1abcdefg

98 description: Optional[str] = None 1abcdefg

99 variables: Optional[Dict[str, ServerVariable]] = None 1abcdefg

100 

101 

102class Reference(BaseModel): 1abcdefg

103 ref: str = Field(alias="$ref") 1abcdefg

104 

105 

106class Discriminator(BaseModel): 1abcdefg

107 propertyName: str 1abcdefg

108 mapping: Optional[Dict[str, str]] = None 1abcdefg

109 

110 

111class XML(BaseModelWithConfig): 1abcdefg

112 name: Optional[str] = None 1abcdefg

113 namespace: Optional[str] = None 1abcdefg

114 prefix: Optional[str] = None 1abcdefg

115 attribute: Optional[bool] = None 1abcdefg

116 wrapped: Optional[bool] = None 1abcdefg

117 

118 

119class ExternalDocumentation(BaseModelWithConfig): 1abcdefg

120 description: Optional[str] = None 1abcdefg

121 url: AnyUrl 1abcdefg

122 

123 

124# Ref JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation#name-type 

125SchemaType = Literal[ 1abcdefg

126 "array", "boolean", "integer", "null", "number", "object", "string" 

127] 

128 

129 

130class Schema(BaseModelWithConfig): 1abcdefg

131 # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu 

132 # Core Vocabulary 

133 schema_: Optional[str] = Field(default=None, alias="$schema") 1abcdefg

134 vocabulary: Optional[str] = Field(default=None, alias="$vocabulary") 1abcdefg

135 id: Optional[str] = Field(default=None, alias="$id") 1abcdefg

136 anchor: Optional[str] = Field(default=None, alias="$anchor") 1abcdefg

137 dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor") 1abcdefg

138 ref: Optional[str] = Field(default=None, alias="$ref") 1abcdefg

139 dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef") 1abcdefg

140 defs: Optional[Dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs") 1abcdefg

141 comment: Optional[str] = Field(default=None, alias="$comment") 1abcdefg

142 # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s 

143 # A Vocabulary for Applying Subschemas 

144 allOf: Optional[List["SchemaOrBool"]] = None 1abcdefg

145 anyOf: Optional[List["SchemaOrBool"]] = None 1abcdefg

146 oneOf: Optional[List["SchemaOrBool"]] = None 1abcdefg

147 not_: Optional["SchemaOrBool"] = Field(default=None, alias="not") 1abcdefg

148 if_: Optional["SchemaOrBool"] = Field(default=None, alias="if") 1abcdefg

149 then: Optional["SchemaOrBool"] = None 1abcdefg

150 else_: Optional["SchemaOrBool"] = Field(default=None, alias="else") 1abcdefg

151 dependentSchemas: Optional[Dict[str, "SchemaOrBool"]] = None 1abcdefg

152 prefixItems: Optional[List["SchemaOrBool"]] = None 1abcdefg

153 # TODO: uncomment and remove below when deprecating Pydantic v1 

154 # It generates a list of schemas for tuples, before prefixItems was available 

155 # items: Optional["SchemaOrBool"] = None 

156 items: Optional[Union["SchemaOrBool", List["SchemaOrBool"]]] = None 1abcdefg

157 contains: Optional["SchemaOrBool"] = None 1abcdefg

158 properties: Optional[Dict[str, "SchemaOrBool"]] = None 1abcdefg

159 patternProperties: Optional[Dict[str, "SchemaOrBool"]] = None 1abcdefg

160 additionalProperties: Optional["SchemaOrBool"] = None 1abcdefg

161 propertyNames: Optional["SchemaOrBool"] = None 1abcdefg

162 unevaluatedItems: Optional["SchemaOrBool"] = None 1abcdefg

163 unevaluatedProperties: Optional["SchemaOrBool"] = None 1abcdefg

164 # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural 

165 # A Vocabulary for Structural Validation 

166 type: Optional[Union[SchemaType, List[SchemaType]]] = None 1abcdefg

167 enum: Optional[List[Any]] = None 1abcdefg

168 const: Optional[Any] = None 1abcdefg

169 multipleOf: Optional[float] = Field(default=None, gt=0) 1abcdefg

170 maximum: Optional[float] = None 1abcdefg

171 exclusiveMaximum: Optional[float] = None 1abcdefg

172 minimum: Optional[float] = None 1abcdefg

173 exclusiveMinimum: Optional[float] = None 1abcdefg

174 maxLength: Optional[int] = Field(default=None, ge=0) 1abcdefg

175 minLength: Optional[int] = Field(default=None, ge=0) 1abcdefg

176 pattern: Optional[str] = None 1abcdefg

177 maxItems: Optional[int] = Field(default=None, ge=0) 1abcdefg

178 minItems: Optional[int] = Field(default=None, ge=0) 1abcdefg

179 uniqueItems: Optional[bool] = None 1abcdefg

180 maxContains: Optional[int] = Field(default=None, ge=0) 1abcdefg

181 minContains: Optional[int] = Field(default=None, ge=0) 1abcdefg

182 maxProperties: Optional[int] = Field(default=None, ge=0) 1abcdefg

183 minProperties: Optional[int] = Field(default=None, ge=0) 1abcdefg

184 required: Optional[List[str]] = None 1abcdefg

185 dependentRequired: Optional[Dict[str, Set[str]]] = None 1abcdefg

186 # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c 

187 # Vocabularies for Semantic Content With "format" 

188 format: Optional[str] = None 1abcdefg

189 # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten 

190 # A Vocabulary for the Contents of String-Encoded Data 

191 contentEncoding: Optional[str] = None 1abcdefg

192 contentMediaType: Optional[str] = None 1abcdefg

193 contentSchema: Optional["SchemaOrBool"] = None 1abcdefg

194 # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta 

195 # A Vocabulary for Basic Meta-Data Annotations 

196 title: Optional[str] = None 1abcdefg

197 description: Optional[str] = None 1abcdefg

198 default: Optional[Any] = None 1abcdefg

199 deprecated: Optional[bool] = None 1abcdefg

200 readOnly: Optional[bool] = None 1abcdefg

201 writeOnly: Optional[bool] = None 1abcdefg

202 examples: Optional[List[Any]] = None 1abcdefg

203 # Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object 

204 # Schema Object 

205 discriminator: Optional[Discriminator] = None 1abcdefg

206 xml: Optional[XML] = None 1abcdefg

207 externalDocs: Optional[ExternalDocumentation] = None 1abcdefg

208 example: Annotated[ 1abcdefg

209 Optional[Any], 

210 typing_deprecated( 

211 "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " 

212 "although still supported. Use examples instead." 

213 ), 

214 ] = None 

215 

216 

217# Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents 

218# A JSON Schema MUST be an object or a boolean. 

219SchemaOrBool = Union[Schema, bool] 1abcdefg

220 

221 

222class Example(TypedDict, total=False): 1abcdefg

223 summary: Optional[str] 1abcdefg

224 description: Optional[str] 1abcdefg

225 value: Optional[Any] 1abcdefg

226 externalValue: Optional[AnyUrl] 1abcdefg

227 

228 if PYDANTIC_V2: # type: ignore [misc] 1abcdefg

229 __pydantic_config__ = {"extra": "allow"} 1abcdefg

230 

231 else: 

232 

233 class Config: 1abcdef

234 extra = "allow" 1abcdef

235 

236 

237class ParameterInType(Enum): 1abcdefg

238 query = "query" 1abcdefg

239 header = "header" 1abcdefg

240 path = "path" 1abcdefg

241 cookie = "cookie" 1abcdefg

242 

243 

244class Encoding(BaseModelWithConfig): 1abcdefg

245 contentType: Optional[str] = None 1abcdefg

246 headers: Optional[Dict[str, Union["Header", Reference]]] = None 1abcdefg

247 style: Optional[str] = None 1abcdefg

248 explode: Optional[bool] = None 1abcdefg

249 allowReserved: Optional[bool] = None 1abcdefg

250 

251 

252class MediaType(BaseModelWithConfig): 1abcdefg

253 schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema") 1abcdefg

254 example: Optional[Any] = None 1abcdefg

255 examples: Optional[Dict[str, Union[Example, Reference]]] = None 1abcdefg

256 encoding: Optional[Dict[str, Encoding]] = None 1abcdefg

257 

258 

259class ParameterBase(BaseModelWithConfig): 1abcdefg

260 description: Optional[str] = None 1abcdefg

261 required: Optional[bool] = None 1abcdefg

262 deprecated: Optional[bool] = None 1abcdefg

263 # Serialization rules for simple scenarios 

264 style: Optional[str] = None 1abcdefg

265 explode: Optional[bool] = None 1abcdefg

266 allowReserved: Optional[bool] = None 1abcdefg

267 schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema") 1abcdefg

268 example: Optional[Any] = None 1abcdefg

269 examples: Optional[Dict[str, Union[Example, Reference]]] = None 1abcdefg

270 # Serialization rules for more complex scenarios 

271 content: Optional[Dict[str, MediaType]] = None 1abcdefg

272 

273 

274class Parameter(ParameterBase): 1abcdefg

275 name: str 1abcdefg

276 in_: ParameterInType = Field(alias="in") 1abcdefg

277 

278 

279class Header(ParameterBase): 1abcdefg

280 pass 1abcdefg

281 

282 

283class RequestBody(BaseModelWithConfig): 1abcdefg

284 description: Optional[str] = None 1abcdefg

285 content: Dict[str, MediaType] 1abcdefg

286 required: Optional[bool] = None 1abcdefg

287 

288 

289class Link(BaseModelWithConfig): 1abcdefg

290 operationRef: Optional[str] = None 1abcdefg

291 operationId: Optional[str] = None 1abcdefg

292 parameters: Optional[Dict[str, Union[Any, str]]] = None 1abcdefg

293 requestBody: Optional[Union[Any, str]] = None 1abcdefg

294 description: Optional[str] = None 1abcdefg

295 server: Optional[Server] = None 1abcdefg

296 

297 

298class Response(BaseModelWithConfig): 1abcdefg

299 description: str 1abcdefg

300 headers: Optional[Dict[str, Union[Header, Reference]]] = None 1abcdefg

301 content: Optional[Dict[str, MediaType]] = None 1abcdefg

302 links: Optional[Dict[str, Union[Link, Reference]]] = None 1abcdefg

303 

304 

305class Operation(BaseModelWithConfig): 1abcdefg

306 tags: Optional[List[str]] = None 1abcdefg

307 summary: Optional[str] = None 1abcdefg

308 description: Optional[str] = None 1abcdefg

309 externalDocs: Optional[ExternalDocumentation] = None 1abcdefg

310 operationId: Optional[str] = None 1abcdefg

311 parameters: Optional[List[Union[Parameter, Reference]]] = None 1abcdefg

312 requestBody: Optional[Union[RequestBody, Reference]] = None 1abcdefg

313 # Using Any for Specification Extensions 

314 responses: Optional[Dict[str, Union[Response, Any]]] = None 1abcdefg

315 callbacks: Optional[Dict[str, Union[Dict[str, "PathItem"], Reference]]] = None 1abcdefg

316 deprecated: Optional[bool] = None 1abcdefg

317 security: Optional[List[Dict[str, List[str]]]] = None 1abcdefg

318 servers: Optional[List[Server]] = None 1abcdefg

319 

320 

321class PathItem(BaseModelWithConfig): 1abcdefg

322 ref: Optional[str] = Field(default=None, alias="$ref") 1abcdefg

323 summary: Optional[str] = None 1abcdefg

324 description: Optional[str] = None 1abcdefg

325 get: Optional[Operation] = None 1abcdefg

326 put: Optional[Operation] = None 1abcdefg

327 post: Optional[Operation] = None 1abcdefg

328 delete: Optional[Operation] = None 1abcdefg

329 options: Optional[Operation] = None 1abcdefg

330 head: Optional[Operation] = None 1abcdefg

331 patch: Optional[Operation] = None 1abcdefg

332 trace: Optional[Operation] = None 1abcdefg

333 servers: Optional[List[Server]] = None 1abcdefg

334 parameters: Optional[List[Union[Parameter, Reference]]] = None 1abcdefg

335 

336 

337class SecuritySchemeType(Enum): 1abcdefg

338 apiKey = "apiKey" 1abcdefg

339 http = "http" 1abcdefg

340 oauth2 = "oauth2" 1abcdefg

341 openIdConnect = "openIdConnect" 1abcdefg

342 

343 

344class SecurityBase(BaseModelWithConfig): 1abcdefg

345 type_: SecuritySchemeType = Field(alias="type") 1abcdefg

346 description: Optional[str] = None 1abcdefg

347 

348 

349class APIKeyIn(Enum): 1abcdefg

350 query = "query" 1abcdefg

351 header = "header" 1abcdefg

352 cookie = "cookie" 1abcdefg

353 

354 

355class APIKey(SecurityBase): 1abcdefg

356 type_: SecuritySchemeType = Field(default=SecuritySchemeType.apiKey, alias="type") 1abcdefg

357 in_: APIKeyIn = Field(alias="in") 1abcdefg

358 name: str 1abcdefg

359 

360 

361class HTTPBase(SecurityBase): 1abcdefg

362 type_: SecuritySchemeType = Field(default=SecuritySchemeType.http, alias="type") 1abcdefg

363 scheme: str 1abcdefg

364 

365 

366class HTTPBearer(HTTPBase): 1abcdefg

367 scheme: Literal["bearer"] = "bearer" 1abcdefg

368 bearerFormat: Optional[str] = None 1abcdefg

369 

370 

371class OAuthFlow(BaseModelWithConfig): 1abcdefg

372 refreshUrl: Optional[str] = None 1abcdefg

373 scopes: Dict[str, str] = {} 1abcdefg

374 

375 

376class OAuthFlowImplicit(OAuthFlow): 1abcdefg

377 authorizationUrl: str 1abcdefg

378 

379 

380class OAuthFlowPassword(OAuthFlow): 1abcdefg

381 tokenUrl: str 1abcdefg

382 

383 

384class OAuthFlowClientCredentials(OAuthFlow): 1abcdefg

385 tokenUrl: str 1abcdefg

386 

387 

388class OAuthFlowAuthorizationCode(OAuthFlow): 1abcdefg

389 authorizationUrl: str 1abcdefg

390 tokenUrl: str 1abcdefg

391 

392 

393class OAuthFlows(BaseModelWithConfig): 1abcdefg

394 implicit: Optional[OAuthFlowImplicit] = None 1abcdefg

395 password: Optional[OAuthFlowPassword] = None 1abcdefg

396 clientCredentials: Optional[OAuthFlowClientCredentials] = None 1abcdefg

397 authorizationCode: Optional[OAuthFlowAuthorizationCode] = None 1abcdefg

398 

399 

400class OAuth2(SecurityBase): 1abcdefg

401 type_: SecuritySchemeType = Field(default=SecuritySchemeType.oauth2, alias="type") 1abcdefg

402 flows: OAuthFlows 1abcdefg

403 

404 

405class OpenIdConnect(SecurityBase): 1abcdefg

406 type_: SecuritySchemeType = Field( 1abcdefg

407 default=SecuritySchemeType.openIdConnect, alias="type" 

408 ) 

409 openIdConnectUrl: str 1abcdefg

410 

411 

412SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer] 1abcdefg

413 

414 

415class Components(BaseModelWithConfig): 1abcdefg

416 schemas: Optional[Dict[str, Union[Schema, Reference]]] = None 1abcdefg

417 responses: Optional[Dict[str, Union[Response, Reference]]] = None 1abcdefg

418 parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None 1abcdefg

419 examples: Optional[Dict[str, Union[Example, Reference]]] = None 1abcdefg

420 requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None 1abcdefg

421 headers: Optional[Dict[str, Union[Header, Reference]]] = None 1abcdefg

422 securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None 1abcdefg

423 links: Optional[Dict[str, Union[Link, Reference]]] = None 1abcdefg

424 # Using Any for Specification Extensions 

425 callbacks: Optional[Dict[str, Union[Dict[str, PathItem], Reference, Any]]] = None 1abcdefg

426 pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None 1abcdefg

427 

428 

429class Tag(BaseModelWithConfig): 1abcdefg

430 name: str 1abcdefg

431 description: Optional[str] = None 1abcdefg

432 externalDocs: Optional[ExternalDocumentation] = None 1abcdefg

433 

434 

435class OpenAPI(BaseModelWithConfig): 1abcdefg

436 openapi: str 1abcdefg

437 info: Info 1abcdefg

438 jsonSchemaDialect: Optional[str] = None 1abcdefg

439 servers: Optional[List[Server]] = None 1abcdefg

440 # Using Any for Specification Extensions 

441 paths: Optional[Dict[str, Union[PathItem, Any]]] = None 1abcdefg

442 webhooks: Optional[Dict[str, Union[PathItem, Reference]]] = None 1abcdefg

443 components: Optional[Components] = None 1abcdefg

444 security: Optional[List[Dict[str, List[str]]]] = None 1abcdefg

445 tags: Optional[List[Tag]] = None 1abcdefg

446 externalDocs: Optional[ExternalDocumentation] = None 1abcdefg

447 

448 

449_model_rebuild(Schema) 1abcdefg

450_model_rebuild(Operation) 1abcdefg

451_model_rebuild(Encoding) 1abcdefg