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

272 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-12 18:15 +0000

1from collections.abc import Callable, Iterable, Mapping 1abcd

2from enum import Enum 1abcd

3from typing import Annotated, Any, Literal, Optional, Union 1abcd

4 

5from fastapi._compat import with_info_plain_validator_function 1abcd

6from fastapi.logger import logger 1abcd

7from pydantic import ( 1abcd

8 AnyUrl, 

9 BaseModel, 

10 Field, 

11 GetJsonSchemaHandler, 

12) 

13from typing_extensions import TypedDict 1abcd

14from typing_extensions import deprecated as typing_deprecated 1abcd

15 

16try: 1abcd

17 import email_validator 1abcd

18 

19 assert email_validator # make autoflake ignore the unused import 1abcd

20 from pydantic import EmailStr 1abcd

21except ImportError: # pragma: no cover 

22 

23 class EmailStr(str): # type: ignore 

24 @classmethod 

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

26 yield cls.validate 

27 

28 @classmethod 

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

30 logger.warning( 

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

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

33 ) 

34 return str(v) 

35 

36 @classmethod 

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

38 logger.warning( 

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

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

41 ) 

42 return str(__input_value) 

43 

44 @classmethod 

45 def __get_pydantic_json_schema__( 

46 cls, core_schema: Mapping[str, Any], handler: GetJsonSchemaHandler 

47 ) -> dict[str, Any]: 

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

49 

50 @classmethod 

51 def __get_pydantic_core_schema__( 

52 cls, source: type[Any], handler: Callable[[Any], Mapping[str, Any]] 

53 ) -> Mapping[str, Any]: 

54 return with_info_plain_validator_function(cls._validate) 

55 

56 

57class BaseModelWithConfig(BaseModel): 1abcd

58 model_config = {"extra": "allow"} 1abcd

59 

60 

61class Contact(BaseModelWithConfig): 1abcd

62 name: str | None = None 1abcd

63 url: AnyUrl | None = None 1abcd

64 email: EmailStr | None = None 1abcd

65 

66 

67class License(BaseModelWithConfig): 1abcd

68 name: str 1abcd

69 identifier: str | None = None 1abcd

70 url: AnyUrl | None = None 1abcd

71 

72 

73class Info(BaseModelWithConfig): 1abcd

74 title: str 1abcd

75 summary: str | None = None 1abcd

76 description: str | None = None 1abcd

77 termsOfService: str | None = None 1abcd

78 contact: Contact | None = None 1abcd

79 license: License | None = None 1abcd

80 version: str 1abcd

81 

82 

83class ServerVariable(BaseModelWithConfig): 1abcd

84 enum: Annotated[list[str] | None, Field(min_length=1)] = None 1abcd

85 default: str 1abcd

86 description: str | None = None 1abcd

87 

88 

89class Server(BaseModelWithConfig): 1abcd

90 url: AnyUrl | str 1abcd

91 description: str | None = None 1abcd

92 variables: dict[str, ServerVariable] | None = None 1abcd

93 

94 

95class Reference(BaseModel): 1abcd

96 ref: str = Field(alias="$ref") 1abcd

97 

98 

99class Discriminator(BaseModel): 1abcd

100 propertyName: str 1abcd

101 mapping: dict[str, str] | None = None 1abcd

102 

103 

104class XML(BaseModelWithConfig): 1abcd

105 name: str | None = None 1abcd

106 namespace: str | None = None 1abcd

107 prefix: str | None = None 1abcd

108 attribute: bool | None = None 1abcd

109 wrapped: bool | None = None 1abcd

110 

111 

112class ExternalDocumentation(BaseModelWithConfig): 1abcd

113 description: str | None = None 1abcd

114 url: AnyUrl 1abcd

115 

116 

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

118SchemaType = Literal[ 1abcd

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

120] 

121 

122 

123class Schema(BaseModelWithConfig): 1abcd

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

125 # Core Vocabulary 

126 schema_: str | None = Field(default=None, alias="$schema") 1abcd

127 vocabulary: str | None = Field(default=None, alias="$vocabulary") 1abcd

128 id: str | None = Field(default=None, alias="$id") 1abcd

129 anchor: str | None = Field(default=None, alias="$anchor") 1abcd

130 dynamicAnchor: str | None = Field(default=None, alias="$dynamicAnchor") 1abcd

131 ref: str | None = Field(default=None, alias="$ref") 1abcd

132 dynamicRef: str | None = Field(default=None, alias="$dynamicRef") 1abcd

133 defs: dict[str, "SchemaOrBool"] | None = Field(default=None, alias="$defs") 1abcd

134 comment: str | None = Field(default=None, alias="$comment") 1abcd

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

136 # A Vocabulary for Applying Subschemas 

137 allOf: list["SchemaOrBool"] | None = None 1abcd

138 anyOf: list["SchemaOrBool"] | None = None 1abcd

139 oneOf: list["SchemaOrBool"] | None = None 1abcd

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

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

142 then: Optional["SchemaOrBool"] = None 1abcd

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

144 dependentSchemas: dict[str, "SchemaOrBool"] | None = None 1abcd

145 prefixItems: list["SchemaOrBool"] | None = None 1abcd

146 items: Optional["SchemaOrBool"] = None 1abcd

147 contains: Optional["SchemaOrBool"] = None 1abcd

148 properties: dict[str, "SchemaOrBool"] | None = None 1abcd

149 patternProperties: dict[str, "SchemaOrBool"] | None = None 1abcd

150 additionalProperties: Optional["SchemaOrBool"] = None 1abcd

151 propertyNames: Optional["SchemaOrBool"] = None 1abcd

152 unevaluatedItems: Optional["SchemaOrBool"] = None 1abcd

153 unevaluatedProperties: Optional["SchemaOrBool"] = None 1abcd

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

155 # A Vocabulary for Structural Validation 

156 type: SchemaType | list[SchemaType] | None = None 1abcd

157 enum: list[Any] | None = None 1abcd

158 const: Any | None = None 1abcd

159 multipleOf: float | None = Field(default=None, gt=0) 1abcd

160 maximum: float | None = None 1abcd

161 exclusiveMaximum: float | None = None 1abcd

162 minimum: float | None = None 1abcd

163 exclusiveMinimum: float | None = None 1abcd

164 maxLength: int | None = Field(default=None, ge=0) 1abcd

165 minLength: int | None = Field(default=None, ge=0) 1abcd

166 pattern: str | None = None 1abcd

167 maxItems: int | None = Field(default=None, ge=0) 1abcd

168 minItems: int | None = Field(default=None, ge=0) 1abcd

169 uniqueItems: bool | None = None 1abcd

170 maxContains: int | None = Field(default=None, ge=0) 1abcd

171 minContains: int | None = Field(default=None, ge=0) 1abcd

172 maxProperties: int | None = Field(default=None, ge=0) 1abcd

173 minProperties: int | None = Field(default=None, ge=0) 1abcd

174 required: list[str] | None = None 1abcd

175 dependentRequired: dict[str, set[str]] | None = None 1abcd

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

177 # Vocabularies for Semantic Content With "format" 

178 format: str | None = None 1abcd

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

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

181 contentEncoding: str | None = None 1abcd

182 contentMediaType: str | None = None 1abcd

183 contentSchema: Optional["SchemaOrBool"] = None 1abcd

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

185 # A Vocabulary for Basic Meta-Data Annotations 

186 title: str | None = None 1abcd

187 description: str | None = None 1abcd

188 default: Any | None = None 1abcd

189 deprecated: bool | None = None 1abcd

190 readOnly: bool | None = None 1abcd

191 writeOnly: bool | None = None 1abcd

192 examples: list[Any] | None = None 1abcd

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

194 # Schema Object 

195 discriminator: Discriminator | None = None 1abcd

196 xml: XML | None = None 1abcd

197 externalDocs: ExternalDocumentation | None = None 1abcd

198 example: Annotated[ 1abcd

199 Any | None, 

200 typing_deprecated( 

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

202 "although still supported. Use examples instead." 

203 ), 

204 ] = None 

205 

206 

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

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

209SchemaOrBool = Schema | bool 1abcd

210 

211 

212class Example(TypedDict, total=False): 1abcd

213 summary: str | None 1abcd

214 description: str | None 1abcd

215 value: Any | None 1abcd

216 externalValue: AnyUrl | None 1abcd

217 

218 __pydantic_config__ = {"extra": "allow"} # type: ignore[misc] 1abcd

219 

220 

221class ParameterInType(Enum): 1abcd

222 query = "query" 1abcd

223 header = "header" 1abcd

224 path = "path" 1abcd

225 cookie = "cookie" 1abcd

226 

227 

228class Encoding(BaseModelWithConfig): 1abcd

229 contentType: str | None = None 1abcd

230 headers: dict[str, Union["Header", Reference]] | None = None 1abcd

231 style: str | None = None 1abcd

232 explode: bool | None = None 1abcd

233 allowReserved: bool | None = None 1abcd

234 

235 

236class MediaType(BaseModelWithConfig): 1abcd

237 schema_: Schema | Reference | None = Field(default=None, alias="schema") 1abcd

238 example: Any | None = None 1abcd

239 examples: dict[str, Example | Reference] | None = None 1abcd

240 encoding: dict[str, Encoding] | None = None 1abcd

241 

242 

243class ParameterBase(BaseModelWithConfig): 1abcd

244 description: str | None = None 1abcd

245 required: bool | None = None 1abcd

246 deprecated: bool | None = None 1abcd

247 # Serialization rules for simple scenarios 

248 style: str | None = None 1abcd

249 explode: bool | None = None 1abcd

250 allowReserved: bool | None = None 1abcd

251 schema_: Schema | Reference | None = Field(default=None, alias="schema") 1abcd

252 example: Any | None = None 1abcd

253 examples: dict[str, Example | Reference] | None = None 1abcd

254 # Serialization rules for more complex scenarios 

255 content: dict[str, MediaType] | None = None 1abcd

256 

257 

258class Parameter(ParameterBase): 1abcd

259 name: str 1abcd

260 in_: ParameterInType = Field(alias="in") 1abcd

261 

262 

263class Header(ParameterBase): 1abcd

264 pass 1abcd

265 

266 

267class RequestBody(BaseModelWithConfig): 1abcd

268 description: str | None = None 1abcd

269 content: dict[str, MediaType] 1abcd

270 required: bool | None = None 1abcd

271 

272 

273class Link(BaseModelWithConfig): 1abcd

274 operationRef: str | None = None 1abcd

275 operationId: str | None = None 1abcd

276 parameters: dict[str, Any | str] | None = None 1abcd

277 requestBody: Any | str | None = None 1abcd

278 description: str | None = None 1abcd

279 server: Server | None = None 1abcd

280 

281 

282class Response(BaseModelWithConfig): 1abcd

283 description: str 1abcd

284 headers: dict[str, Header | Reference] | None = None 1abcd

285 content: dict[str, MediaType] | None = None 1abcd

286 links: dict[str, Link | Reference] | None = None 1abcd

287 

288 

289class Operation(BaseModelWithConfig): 1abcd

290 tags: list[str] | None = None 1abcd

291 summary: str | None = None 1abcd

292 description: str | None = None 1abcd

293 externalDocs: ExternalDocumentation | None = None 1abcd

294 operationId: str | None = None 1abcd

295 parameters: list[Parameter | Reference] | None = None 1abcd

296 requestBody: RequestBody | Reference | None = None 1abcd

297 # Using Any for Specification Extensions 

298 responses: dict[str, Response | Any] | None = None 1abcd

299 callbacks: dict[str, dict[str, "PathItem"] | Reference] | None = None 1abcd

300 deprecated: bool | None = None 1abcd

301 security: list[dict[str, list[str]]] | None = None 1abcd

302 servers: list[Server] | None = None 1abcd

303 

304 

305class PathItem(BaseModelWithConfig): 1abcd

306 ref: str | None = Field(default=None, alias="$ref") 1abcd

307 summary: str | None = None 1abcd

308 description: str | None = None 1abcd

309 get: Operation | None = None 1abcd

310 put: Operation | None = None 1abcd

311 post: Operation | None = None 1abcd

312 delete: Operation | None = None 1abcd

313 options: Operation | None = None 1abcd

314 head: Operation | None = None 1abcd

315 patch: Operation | None = None 1abcd

316 trace: Operation | None = None 1abcd

317 servers: list[Server] | None = None 1abcd

318 parameters: list[Parameter | Reference] | None = None 1abcd

319 

320 

321class SecuritySchemeType(Enum): 1abcd

322 apiKey = "apiKey" 1abcd

323 http = "http" 1abcd

324 oauth2 = "oauth2" 1abcd

325 openIdConnect = "openIdConnect" 1abcd

326 

327 

328class SecurityBase(BaseModelWithConfig): 1abcd

329 type_: SecuritySchemeType = Field(alias="type") 1abcd

330 description: str | None = None 1abcd

331 

332 

333class APIKeyIn(Enum): 1abcd

334 query = "query" 1abcd

335 header = "header" 1abcd

336 cookie = "cookie" 1abcd

337 

338 

339class APIKey(SecurityBase): 1abcd

340 type_: SecuritySchemeType = Field(default=SecuritySchemeType.apiKey, alias="type") 1abcd

341 in_: APIKeyIn = Field(alias="in") 1abcd

342 name: str 1abcd

343 

344 

345class HTTPBase(SecurityBase): 1abcd

346 type_: SecuritySchemeType = Field(default=SecuritySchemeType.http, alias="type") 1abcd

347 scheme: str 1abcd

348 

349 

350class HTTPBearer(HTTPBase): 1abcd

351 scheme: Literal["bearer"] = "bearer" 1abcd

352 bearerFormat: str | None = None 1abcd

353 

354 

355class OAuthFlow(BaseModelWithConfig): 1abcd

356 refreshUrl: str | None = None 1abcd

357 scopes: dict[str, str] = {} 1abcd

358 

359 

360class OAuthFlowImplicit(OAuthFlow): 1abcd

361 authorizationUrl: str 1abcd

362 

363 

364class OAuthFlowPassword(OAuthFlow): 1abcd

365 tokenUrl: str 1abcd

366 

367 

368class OAuthFlowClientCredentials(OAuthFlow): 1abcd

369 tokenUrl: str 1abcd

370 

371 

372class OAuthFlowAuthorizationCode(OAuthFlow): 1abcd

373 authorizationUrl: str 1abcd

374 tokenUrl: str 1abcd

375 

376 

377class OAuthFlows(BaseModelWithConfig): 1abcd

378 implicit: OAuthFlowImplicit | None = None 1abcd

379 password: OAuthFlowPassword | None = None 1abcd

380 clientCredentials: OAuthFlowClientCredentials | None = None 1abcd

381 authorizationCode: OAuthFlowAuthorizationCode | None = None 1abcd

382 

383 

384class OAuth2(SecurityBase): 1abcd

385 type_: SecuritySchemeType = Field(default=SecuritySchemeType.oauth2, alias="type") 1abcd

386 flows: OAuthFlows 1abcd

387 

388 

389class OpenIdConnect(SecurityBase): 1abcd

390 type_: SecuritySchemeType = Field( 1abcd

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

392 ) 

393 openIdConnectUrl: str 1abcd

394 

395 

396SecurityScheme = APIKey | HTTPBase | OAuth2 | OpenIdConnect | HTTPBearer 1abcd

397 

398 

399class Components(BaseModelWithConfig): 1abcd

400 schemas: dict[str, Schema | Reference] | None = None 1abcd

401 responses: dict[str, Response | Reference] | None = None 1abcd

402 parameters: dict[str, Parameter | Reference] | None = None 1abcd

403 examples: dict[str, Example | Reference] | None = None 1abcd

404 requestBodies: dict[str, RequestBody | Reference] | None = None 1abcd

405 headers: dict[str, Header | Reference] | None = None 1abcd

406 securitySchemes: dict[str, SecurityScheme | Reference] | None = None 1abcd

407 links: dict[str, Link | Reference] | None = None 1abcd

408 # Using Any for Specification Extensions 

409 callbacks: dict[str, dict[str, PathItem] | Reference | Any] | None = None 1abcd

410 pathItems: dict[str, PathItem | Reference] | None = None 1abcd

411 

412 

413class Tag(BaseModelWithConfig): 1abcd

414 name: str 1abcd

415 description: str | None = None 1abcd

416 externalDocs: ExternalDocumentation | None = None 1abcd

417 

418 

419class OpenAPI(BaseModelWithConfig): 1abcd

420 openapi: str 1abcd

421 info: Info 1abcd

422 jsonSchemaDialect: str | None = None 1abcd

423 servers: list[Server] | None = None 1abcd

424 # Using Any for Specification Extensions 

425 paths: dict[str, PathItem | Any] | None = None 1abcd

426 webhooks: dict[str, PathItem | Reference] | None = None 1abcd

427 components: Components | None = None 1abcd

428 security: list[dict[str, list[str]]] | None = None 1abcd

429 tags: list[Tag] | None = None 1abcd

430 externalDocs: ExternalDocumentation | None = None 1abcd

431 

432 

433Schema.model_rebuild() 1abcd

434Operation.model_rebuild() 1abcd

435Encoding.model_rebuild() 1abcd