Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pydantic/json_schema.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1159 statements  

1"""!!! abstract "Usage Documentation" 

2 [JSON Schema](../concepts/json_schema.md) 

3 

4The `json_schema` module contains classes and functions to allow the way [JSON Schema](https://json-schema.org/) 

5is generated to be customized. 

6 

7In general you shouldn't need to use this module directly; instead, you can use 

8[`BaseModel.model_json_schema`][pydantic.BaseModel.model_json_schema] and 

9[`TypeAdapter.json_schema`][pydantic.TypeAdapter.json_schema]. 

10""" 

11 

12from __future__ import annotations as _annotations 

13 

14import collections.abc 

15import dataclasses 

16import inspect 

17import math 

18import os 

19import re 

20import warnings 

21from collections import Counter, defaultdict 

22from collections.abc import Hashable, Iterable, Sequence 

23from copy import deepcopy 

24from enum import Enum 

25from re import Pattern 

26from typing import ( 

27 TYPE_CHECKING, 

28 Annotated, 

29 Any, 

30 Callable, 

31 Literal, 

32 NewType, 

33 TypeVar, 

34 Union, 

35 cast, 

36 overload, 

37) 

38 

39import pydantic_core 

40from pydantic_core import MISSING, CoreSchema, PydanticOmit, core_schema, to_jsonable_python 

41from pydantic_core.core_schema import ComputedField 

42from typing_extensions import TypeAlias, assert_never, deprecated, final 

43from typing_inspection.introspection import get_literal_values 

44 

45from pydantic.warnings import PydanticDeprecatedSince26, PydanticDeprecatedSince29 

46 

47from ._internal import ( 

48 _config, 

49 _core_metadata, 

50 _core_utils, 

51 _decorators, 

52 _internal_dataclass, 

53 _mock_val_ser, 

54 _schema_generation_shared, 

55 _typing_extra, 

56) 

57from .annotated_handlers import GetJsonSchemaHandler 

58from .config import JsonDict, JsonValue 

59from .errors import PydanticInvalidForJsonSchema, PydanticSchemaGenerationError, PydanticUserError 

60 

61if TYPE_CHECKING: 

62 from . import ConfigDict 

63 from ._internal._core_utils import CoreSchemaField, CoreSchemaOrField 

64 from ._internal._dataclasses import PydanticDataclass 

65 from ._internal._schema_generation_shared import GetJsonSchemaFunction 

66 from .main import BaseModel 

67 

68 

69CoreSchemaOrFieldType = Literal[core_schema.CoreSchemaType, core_schema.CoreSchemaFieldType] 

70""" 

71A type alias for defined schema types that represents a union of 

72`core_schema.CoreSchemaType` and 

73`core_schema.CoreSchemaFieldType`. 

74""" 

75 

76JsonSchemaValue = dict[str, Any] 

77""" 

78A type alias for a JSON schema value. This is a dictionary of string keys to arbitrary JSON values. 

79""" 

80 

81JsonSchemaMode = Literal['validation', 'serialization'] 

82""" 

83A type alias that represents the mode of a JSON schema; either 'validation' or 'serialization'. 

84 

85For some types, the inputs to validation differ from the outputs of serialization. For example, 

86computed fields will only be present when serializing, and should not be provided when 

87validating. This flag provides a way to indicate whether you want the JSON schema required 

88for validation inputs, or that will be matched by serialization outputs. 

89""" 

90 

91_MODE_TITLE_MAPPING: dict[JsonSchemaMode, str] = {'validation': 'Input', 'serialization': 'Output'} 

92 

93 

94JsonSchemaWarningKind = Literal['skipped-choice', 'non-serializable-default', 'skipped-discriminator'] 

95""" 

96A type alias representing the kinds of warnings that can be emitted during JSON schema generation. 

97 

98See [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message] 

99for more details. 

100""" 

101 

102 

103class PydanticJsonSchemaWarning(UserWarning): 

104 """This class is used to emit warnings produced during JSON schema generation. 

105 See the [`GenerateJsonSchema.emit_warning`][pydantic.json_schema.GenerateJsonSchema.emit_warning] and 

106 [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message] 

107 methods for more details; these can be overridden to control warning behavior. 

108 """ 

109 

110 

111NoDefault = object() 

112"""A sentinel value used to indicate that no default value should be used when generating a JSON Schema 

113for a core schema with a default value. 

114""" 

115 

116 

117# ##### JSON Schema Generation ##### 

118DEFAULT_REF_TEMPLATE = '#/$defs/{model}' 

119"""The default format string used to generate reference names.""" 

120 

121# There are three types of references relevant to building JSON schemas: 

122# 1. core_schema "ref" values; these are not exposed as part of the JSON schema 

123# * these might look like the fully qualified path of a model, its id, or something similar 

124CoreRef = NewType('CoreRef', str) 

125# 2. keys of the "definitions" object that will eventually go into the JSON schema 

126# * by default, these look like "MyModel", though may change in the presence of collisions 

127# * eventually, we may want to make it easier to modify the way these names are generated 

128DefsRef = NewType('DefsRef', str) 

129# 3. the values corresponding to the "$ref" key in the schema 

130# * By default, these look like "#/$defs/MyModel", as in {"$ref": "#/$defs/MyModel"} 

131JsonRef = NewType('JsonRef', str) 

132 

133CoreModeRef = tuple[CoreRef, JsonSchemaMode] 

134JsonSchemaKeyT = TypeVar('JsonSchemaKeyT', bound=Hashable) 

135 

136_PRIMITIVE_JSON_SCHEMA_TYPES = ('string', 'boolean', 'null', 'integer', 'number') 

137 

138 

139@dataclasses.dataclass(**_internal_dataclass.slots_true) 

140class _DefinitionsRemapping: 

141 defs_remapping: dict[DefsRef, DefsRef] 

142 json_remapping: dict[JsonRef, JsonRef] 

143 

144 @staticmethod 

145 def from_prioritized_choices( 

146 prioritized_choices: dict[DefsRef, list[DefsRef]], 

147 defs_to_json: dict[DefsRef, JsonRef], 

148 definitions: dict[DefsRef, JsonSchemaValue], 

149 ) -> _DefinitionsRemapping: 

150 """ 

151 This function should produce a remapping that replaces complex DefsRef with the simpler ones from the 

152 prioritized_choices such that applying the name remapping would result in an equivalent JSON schema. 

153 """ 

154 # We need to iteratively simplify the definitions until we reach a fixed point. 

155 # The reason for this is that outer definitions may reference inner definitions that get simplified 

156 # into an equivalent reference, and the outer definitions won't be equivalent until we've simplified 

157 # the inner definitions. 

158 copied_definitions = deepcopy(definitions) 

159 definitions_schema = {'$defs': copied_definitions} 

160 for _iter in range(100): # prevent an infinite loop in the case of a bug, 100 iterations should be enough 

161 # For every possible remapped DefsRef, collect all schemas that DefsRef might be used for: 

162 schemas_for_alternatives: dict[DefsRef, list[JsonSchemaValue]] = defaultdict(list) 

163 for defs_ref in copied_definitions: 

164 alternatives = prioritized_choices[defs_ref] 

165 for alternative in alternatives: 

166 schemas_for_alternatives[alternative].append(copied_definitions[defs_ref]) 

167 

168 # Deduplicate the schemas for each alternative; the idea is that we only want to remap to a new DefsRef 

169 # if it introduces no ambiguity, i.e., there is only one distinct schema for that DefsRef. 

170 for defs_ref in schemas_for_alternatives: 

171 schemas_for_alternatives[defs_ref] = _deduplicate_schemas(schemas_for_alternatives[defs_ref]) 

172 

173 # Build the remapping 

174 defs_remapping: dict[DefsRef, DefsRef] = {} 

175 json_remapping: dict[JsonRef, JsonRef] = {} 

176 for original_defs_ref in definitions: 

177 alternatives = prioritized_choices[original_defs_ref] 

178 # Pick the first alternative that has only one schema, since that means there is no collision 

179 remapped_defs_ref = next(x for x in alternatives if len(schemas_for_alternatives[x]) == 1) 

180 defs_remapping[original_defs_ref] = remapped_defs_ref 

181 

182 # Map all alternatives after the remapped one to the remapped one 

183 # This ensures that intermediate simplifications are also remapped 

184 remapped_index = alternatives.index(remapped_defs_ref) 

185 for alt in alternatives[remapped_index:]: 

186 json_remapping[defs_to_json[alt]] = defs_to_json[remapped_defs_ref] 

187 remapping = _DefinitionsRemapping(defs_remapping, json_remapping) 

188 new_definitions_schema = remapping.remap_json_schema({'$defs': copied_definitions}) 

189 if definitions_schema == new_definitions_schema: 

190 # We've reached the fixed point 

191 return remapping 

192 definitions_schema = new_definitions_schema 

193 

194 raise PydanticInvalidForJsonSchema('Failed to simplify the JSON schema definitions') 

195 

196 def remap_defs_ref(self, ref: DefsRef) -> DefsRef: 

197 return self.defs_remapping.get(ref, ref) 

198 

199 def remap_json_ref(self, ref: JsonRef) -> JsonRef: 

200 return self.json_remapping.get(ref, ref) 

201 

202 def remap_json_schema(self, schema: Any) -> Any: 

203 """ 

204 Recursively update the JSON schema replacing all $refs 

205 """ 

206 if isinstance(schema, str): 

207 # Note: this may not really be a JsonRef; we rely on having no collisions between JsonRefs and other strings 

208 return self.remap_json_ref(JsonRef(schema)) 

209 elif isinstance(schema, list): 

210 return [self.remap_json_schema(item) for item in schema] 

211 elif isinstance(schema, dict): 

212 for key, value in schema.items(): 

213 if key == '$ref' and isinstance(value, str): 

214 schema['$ref'] = self.remap_json_ref(JsonRef(value)) 

215 elif key == '$defs': 

216 schema['$defs'] = { 

217 self.remap_defs_ref(DefsRef(key)): self.remap_json_schema(value) 

218 for key, value in schema['$defs'].items() 

219 } 

220 else: 

221 schema[key] = self.remap_json_schema(value) 

222 return schema 

223 

224 

225class GenerateJsonSchema: 

226 """!!! abstract "Usage Documentation" 

227 [Customizing the JSON Schema Generation Process](../concepts/json_schema.md#customizing-the-json-schema-generation-process) 

228 

229 A class for generating JSON schemas. 

230 

231 This class generates JSON schemas based on configured parameters. The default schema dialect 

232 is [https://json-schema.org/draft/2020-12/schema](https://json-schema.org/draft/2020-12/schema). 

233 The class uses `by_alias` to configure how fields with 

234 multiple names are handled and `ref_template` to format reference names. 

235 

236 Attributes: 

237 schema_dialect: The JSON schema dialect used to generate the schema. See 

238 [Declaring a Dialect](https://json-schema.org/understanding-json-schema/reference/schema.html#id4) 

239 in the JSON Schema documentation for more information about dialects. 

240 ignored_warning_kinds: Warnings to ignore when generating the schema. `self.render_warning_message` will 

241 do nothing if its argument `kind` is in `ignored_warning_kinds`; 

242 this value can be modified on subclasses to easily control which warnings are emitted. 

243 by_alias: Whether to use field aliases when generating the schema. 

244 ref_template: The format string used when generating reference names. 

245 core_to_json_refs: A mapping of core refs to JSON refs. 

246 core_to_defs_refs: A mapping of core refs to definition refs. 

247 defs_to_core_refs: A mapping of definition refs to core refs. 

248 json_to_defs_refs: A mapping of JSON refs to definition refs. 

249 definitions: Definitions in the schema. 

250 

251 Args: 

252 by_alias: Whether to use field aliases in the generated schemas. 

253 ref_template: The format string to use when generating reference names. 

254 union_format: The format to use when combining schemas from unions together. Can be one of: 

255 

256 - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf) 

257 keyword to combine schemas (the default). 

258 - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type) 

259 keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive 

260 type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to 

261 `any_of`. 

262 

263 Raises: 

264 JsonSchemaError: If the instance of the class is inadvertently reused after generating a schema. 

265 """ 

266 

267 schema_dialect = 'https://json-schema.org/draft/2020-12/schema' 

268 

269 # `self.render_warning_message` will do nothing if its argument `kind` is in `ignored_warning_kinds`; 

270 # this value can be modified on subclasses to easily control which warnings are emitted 

271 ignored_warning_kinds: set[JsonSchemaWarningKind] = {'skipped-choice'} 

272 

273 def __init__( 

274 self, 

275 by_alias: bool = True, 

276 ref_template: str = DEFAULT_REF_TEMPLATE, 

277 union_format: Literal['any_of', 'primitive_type_array'] = 'any_of', 

278 ) -> None: 

279 self.by_alias = by_alias 

280 self.ref_template = ref_template 

281 self.union_format: Literal['any_of', 'primitive_type_array'] = union_format 

282 

283 self.core_to_json_refs: dict[CoreModeRef, JsonRef] = {} 

284 self.core_to_defs_refs: dict[CoreModeRef, DefsRef] = {} 

285 self.defs_to_core_refs: dict[DefsRef, CoreModeRef] = {} 

286 self.json_to_defs_refs: dict[JsonRef, DefsRef] = {} 

287 

288 self.definitions: dict[DefsRef, JsonSchemaValue] = {} 

289 self._config_wrapper_stack = _config.ConfigWrapperStack(_config.ConfigWrapper({})) 

290 

291 self._mode: JsonSchemaMode = 'validation' 

292 

293 # The following includes a mapping of a fully-unique defs ref choice to a list of preferred 

294 # alternatives, which are generally simpler, such as only including the class name. 

295 # At the end of schema generation, we use these to produce a JSON schema with more human-readable 

296 # definitions, which would also work better in a generated OpenAPI client, etc. 

297 self._prioritized_defsref_choices: dict[DefsRef, list[DefsRef]] = {} 

298 self._collision_counter: dict[str, int] = defaultdict(int) 

299 self._collision_index: dict[str, int] = {} 

300 

301 self._schema_type_to_method = self.build_schema_type_to_method() 

302 

303 # When we encounter definitions we need to try to build them immediately 

304 # so that they are available schemas that reference them 

305 # But it's possible that CoreSchema was never going to be used 

306 # (e.g. because the CoreSchema that references short circuits is JSON schema generation without needing 

307 # the reference) so instead of failing altogether if we can't build a definition we 

308 # store the error raised and re-throw it if we end up needing that def 

309 self._core_defs_invalid_for_json_schema: dict[DefsRef, PydanticInvalidForJsonSchema] = {} 

310 

311 # This changes to True after generating a schema, to prevent issues caused by accidental reuse 

312 # of a single instance of a schema generator 

313 self._used = False 

314 

315 @property 

316 def _config(self) -> _config.ConfigWrapper: 

317 return self._config_wrapper_stack.tail 

318 

319 @property 

320 def mode(self) -> JsonSchemaMode: 

321 if self._config.json_schema_mode_override is not None: 

322 return self._config.json_schema_mode_override 

323 else: 

324 return self._mode 

325 

326 def build_schema_type_to_method( 

327 self, 

328 ) -> dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]]: 

329 """Builds a dictionary mapping fields to methods for generating JSON schemas. 

330 

331 Returns: 

332 A dictionary containing the mapping of `CoreSchemaOrFieldType` to a handler method. 

333 

334 Raises: 

335 TypeError: If no method has been defined for generating a JSON schema for a given pydantic core schema type. 

336 """ 

337 mapping: dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]] = {} 

338 core_schema_types: list[CoreSchemaOrFieldType] = list(get_literal_values(CoreSchemaOrFieldType)) 

339 for key in core_schema_types: 

340 method_name = f'{key.replace("-", "_")}_schema' 

341 try: 

342 mapping[key] = getattr(self, method_name) 

343 except AttributeError as e: # pragma: no cover 

344 if os.getenv('PYDANTIC_PRIVATE_ALLOW_UNHANDLED_SCHEMA_TYPES'): 

345 continue 

346 raise TypeError( 

347 f'No method for generating JsonSchema for core_schema.type={key!r} ' 

348 f'(expected: {type(self).__name__}.{method_name})' 

349 ) from e 

350 return mapping 

351 

352 def generate_definitions( 

353 self, inputs: Sequence[tuple[JsonSchemaKeyT, JsonSchemaMode, core_schema.CoreSchema]] 

354 ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], dict[DefsRef, JsonSchemaValue]]: 

355 """Generates JSON schema definitions from a list of core schemas, pairing the generated definitions with a 

356 mapping that links the input keys to the definition references. 

357 

358 Args: 

359 inputs: A sequence of tuples, where: 

360 

361 - The first element is a JSON schema key type. 

362 - The second element is the JSON mode: either 'validation' or 'serialization'. 

363 - The third element is a core schema. 

364 

365 Returns: 

366 A tuple where: 

367 

368 - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and 

369 whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have 

370 JsonRef references to definitions that are defined in the second returned element.) 

371 - The second element is a dictionary whose keys are definition references for the JSON schemas 

372 from the first returned element, and whose values are the actual JSON schema definitions. 

373 

374 Raises: 

375 PydanticUserError: Raised if the JSON schema generator has already been used to generate a JSON schema. 

376 """ 

377 if self._used: 

378 raise PydanticUserError( 

379 'This JSON schema generator has already been used to generate a JSON schema. ' 

380 f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.', 

381 code='json-schema-already-used', 

382 ) 

383 

384 for _, mode, schema in inputs: 

385 self._mode = mode 

386 self.generate_inner(schema) 

387 

388 definitions_remapping = self._build_definitions_remapping() 

389 

390 json_schemas_map: dict[tuple[JsonSchemaKeyT, JsonSchemaMode], DefsRef] = {} 

391 for key, mode, schema in inputs: 

392 self._mode = mode 

393 json_schema = self.generate_inner(schema) 

394 json_schemas_map[(key, mode)] = definitions_remapping.remap_json_schema(json_schema) 

395 

396 json_schema = {'$defs': self.definitions} 

397 json_schema = definitions_remapping.remap_json_schema(json_schema) 

398 self._used = True 

399 return json_schemas_map, self.sort(json_schema['$defs']) # type: ignore 

400 

401 def generate(self, schema: CoreSchema, mode: JsonSchemaMode = 'validation') -> JsonSchemaValue: 

402 """Generates a JSON schema for a specified schema in a specified mode. 

403 

404 Args: 

405 schema: A Pydantic model. 

406 mode: The mode in which to generate the schema. Defaults to 'validation'. 

407 

408 Returns: 

409 A JSON schema representing the specified schema. 

410 

411 Raises: 

412 PydanticUserError: If the JSON schema generator has already been used to generate a JSON schema. 

413 """ 

414 self._mode = mode 

415 if self._used: 

416 raise PydanticUserError( 

417 'This JSON schema generator has already been used to generate a JSON schema. ' 

418 f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.', 

419 code='json-schema-already-used', 

420 ) 

421 

422 json_schema: JsonSchemaValue = self.generate_inner(schema) 

423 json_ref_counts = self.get_json_ref_counts(json_schema) 

424 

425 ref = cast(JsonRef, json_schema.get('$ref')) 

426 while ref is not None: # may need to unpack multiple levels 

427 ref_json_schema = self.get_schema_from_definitions(ref) 

428 if json_ref_counts[ref] == 1 and ref_json_schema is not None and len(json_schema) == 1: 

429 # "Unpack" the ref since this is the only reference and there are no sibling keys 

430 json_schema = ref_json_schema.copy() # copy to prevent recursive dict reference 

431 json_ref_counts[ref] -= 1 

432 ref = cast(JsonRef, json_schema.get('$ref')) 

433 ref = None 

434 

435 self._garbage_collect_definitions(json_schema) 

436 definitions_remapping = self._build_definitions_remapping() 

437 

438 if self.definitions: 

439 json_schema['$defs'] = self.definitions 

440 

441 json_schema = definitions_remapping.remap_json_schema(json_schema) 

442 

443 # For now, we will not set the $schema key. However, if desired, this can be easily added by overriding 

444 # this method and adding the following line after a call to super().generate(schema): 

445 # json_schema['$schema'] = self.schema_dialect 

446 

447 self._used = True 

448 return self.sort(json_schema) 

449 

450 def generate_inner(self, schema: CoreSchemaOrField) -> JsonSchemaValue: # noqa: C901 

451 """Generates a JSON schema for a given core schema. 

452 

453 Args: 

454 schema: The given core schema. 

455 

456 Returns: 

457 The generated JSON schema. 

458 

459 TODO: the nested function definitions here seem like bad practice, I'd like to unpack these 

460 in a future PR. It'd be great if we could shorten the call stack a bit for JSON schema generation, 

461 and I think there's potential for that here. 

462 """ 

463 # If a schema with the same CoreRef has been handled, just return a reference to it 

464 # Note that this assumes that it will _never_ be the case that the same CoreRef is used 

465 # on types that should have different JSON schemas 

466 if 'ref' in schema: 

467 core_ref = CoreRef(schema['ref']) # type: ignore[typeddict-item] 

468 core_mode_ref = (core_ref, self.mode) 

469 if core_mode_ref in self.core_to_defs_refs and self.core_to_defs_refs[core_mode_ref] in self.definitions: 

470 return {'$ref': self.core_to_json_refs[core_mode_ref]} 

471 

472 def populate_defs(core_schema: CoreSchema, json_schema: JsonSchemaValue) -> JsonSchemaValue: 

473 if 'ref' in core_schema: 

474 core_ref = CoreRef(core_schema['ref']) # type: ignore[typeddict-item] 

475 defs_ref, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 

476 json_ref = JsonRef(ref_json_schema['$ref']) 

477 # Replace the schema if it's not a reference to itself 

478 # What we want to avoid is having the def be just a ref to itself 

479 # which is what would happen if we blindly assigned any 

480 if json_schema.get('$ref', None) != json_ref: 

481 self.definitions[defs_ref] = json_schema 

482 self._core_defs_invalid_for_json_schema.pop(defs_ref, None) 

483 json_schema = ref_json_schema 

484 return json_schema 

485 

486 def handler_func(schema_or_field: CoreSchemaOrField) -> JsonSchemaValue: 

487 """Generate a JSON schema based on the input schema. 

488 

489 Args: 

490 schema_or_field: The core schema to generate a JSON schema from. 

491 

492 Returns: 

493 The generated JSON schema. 

494 

495 Raises: 

496 TypeError: If an unexpected schema type is encountered. 

497 """ 

498 # Generate the core-schema-type-specific bits of the schema generation: 

499 json_schema: JsonSchemaValue | None = None 

500 if self.mode == 'serialization' and 'serialization' in schema_or_field: 

501 # In this case, we skip the JSON Schema generation of the schema 

502 # and use the `'serialization'` schema instead (canonical example: 

503 # `Annotated[int, PlainSerializer(str)]`). 

504 ser_schema = schema_or_field['serialization'] # type: ignore 

505 json_schema = self.ser_schema(ser_schema) 

506 

507 # It might be that the 'serialization'` is skipped depending on `when_used`. 

508 # This is only relevant for `nullable` schemas though, so we special case here. 

509 if ( 

510 json_schema is not None 

511 and ser_schema.get('when_used') in ('unless-none', 'json-unless-none') 

512 and schema_or_field['type'] == 'nullable' 

513 ): 

514 json_schema = self.get_union_of_schemas([{'type': 'null'}, json_schema]) 

515 if json_schema is None: 

516 if _core_utils.is_core_schema(schema_or_field) or _core_utils.is_core_schema_field(schema_or_field): 

517 generate_for_schema_type = self._schema_type_to_method[schema_or_field['type']] 

518 json_schema = generate_for_schema_type(schema_or_field) 

519 else: 

520 raise TypeError(f'Unexpected schema type: schema={schema_or_field}') 

521 return json_schema 

522 

523 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, handler_func) 

524 

525 metadata = cast(_core_metadata.CoreMetadata, schema.get('metadata', {})) 

526 

527 # TODO: I dislike that we have to wrap these basic dict updates in callables, is there any way around this? 

528 

529 if js_updates := metadata.get('pydantic_js_updates'): 

530 

531 def js_updates_handler_func( 

532 schema_or_field: CoreSchemaOrField, 

533 current_handler: GetJsonSchemaHandler = current_handler, 

534 ) -> JsonSchemaValue: 

535 json_schema = {**current_handler(schema_or_field), **js_updates} 

536 return json_schema 

537 

538 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_updates_handler_func) 

539 

540 if js_extra := metadata.get('pydantic_js_extra'): 

541 

542 def js_extra_handler_func( 

543 schema_or_field: CoreSchemaOrField, 

544 current_handler: GetJsonSchemaHandler = current_handler, 

545 ) -> JsonSchemaValue: 

546 json_schema = current_handler(schema_or_field) 

547 if isinstance(js_extra, dict): 

548 json_schema.update(to_jsonable_python(js_extra)) 

549 elif callable(js_extra): 

550 # similar to typing issue in _update_class_schema when we're working with callable js extra 

551 js_extra(json_schema) # type: ignore 

552 return json_schema 

553 

554 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_extra_handler_func) 

555 

556 for js_modify_function in metadata.get('pydantic_js_functions', ()): 

557 

558 def new_handler_func( 

559 schema_or_field: CoreSchemaOrField, 

560 current_handler: GetJsonSchemaHandler = current_handler, 

561 js_modify_function: GetJsonSchemaFunction = js_modify_function, 

562 ) -> JsonSchemaValue: 

563 json_schema = js_modify_function(schema_or_field, current_handler) 

564 if _core_utils.is_core_schema(schema_or_field): 

565 json_schema = populate_defs(schema_or_field, json_schema) 

566 original_schema = current_handler.resolve_ref_schema(json_schema) 

567 ref = json_schema.pop('$ref', None) 

568 if ref and json_schema: 

569 original_schema.update(json_schema) 

570 return original_schema 

571 

572 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 

573 

574 for js_modify_function in metadata.get('pydantic_js_annotation_functions', ()): 

575 

576 def new_handler_func( 

577 schema_or_field: CoreSchemaOrField, 

578 current_handler: GetJsonSchemaHandler = current_handler, 

579 js_modify_function: GetJsonSchemaFunction = js_modify_function, 

580 ) -> JsonSchemaValue: 

581 return js_modify_function(schema_or_field, current_handler) 

582 

583 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 

584 

585 json_schema = current_handler(schema) 

586 if _core_utils.is_core_schema(schema): 

587 json_schema = populate_defs(schema, json_schema) 

588 return json_schema 

589 

590 def sort(self, value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue: 

591 """Override this method to customize the sorting of the JSON schema (e.g., don't sort at all, sort all keys unconditionally, etc.) 

592 

593 By default, alphabetically sort the keys in the JSON schema, skipping the 'properties' and 'default' keys to preserve field definition order. 

594 This sort is recursive, so it will sort all nested dictionaries as well. 

595 """ 

596 sorted_dict: dict[str, JsonSchemaValue] = {} 

597 keys = value.keys() 

598 if parent_key not in ('properties', 'default'): 

599 keys = sorted(keys) 

600 for key in keys: 

601 sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) 

602 return sorted_dict 

603 

604 def _sort_recursive(self, value: Any, parent_key: str | None = None) -> Any: 

605 """Recursively sort a JSON schema value.""" 

606 if isinstance(value, dict): 

607 sorted_dict: dict[str, JsonSchemaValue] = {} 

608 keys = value.keys() 

609 if parent_key not in ('properties', 'default'): 

610 keys = sorted(keys) 

611 for key in keys: 

612 sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) 

613 return sorted_dict 

614 elif isinstance(value, list): 

615 sorted_list: list[JsonSchemaValue] = [self._sort_recursive(item, parent_key) for item in value] 

616 return sorted_list 

617 else: 

618 return value 

619 

620 # ### Schema generation methods 

621 

622 def invalid_schema(self, schema: core_schema.InvalidSchema) -> JsonSchemaValue: 

623 """Placeholder - should never be called.""" 

624 

625 raise RuntimeError('Cannot generate schema for invalid_schema. This is a bug! Please report it.') 

626 

627 def any_schema(self, schema: core_schema.AnySchema) -> JsonSchemaValue: 

628 """Generates a JSON schema that matches any value. 

629 

630 Args: 

631 schema: The core schema. 

632 

633 Returns: 

634 The generated JSON schema. 

635 """ 

636 return {} 

637 

638 def none_schema(self, schema: core_schema.NoneSchema) -> JsonSchemaValue: 

639 """Generates a JSON schema that matches `None`. 

640 

641 Args: 

642 schema: The core schema. 

643 

644 Returns: 

645 The generated JSON schema. 

646 """ 

647 return {'type': 'null'} 

648 

649 def bool_schema(self, schema: core_schema.BoolSchema) -> JsonSchemaValue: 

650 """Generates a JSON schema that matches a bool value. 

651 

652 Args: 

653 schema: The core schema. 

654 

655 Returns: 

656 The generated JSON schema. 

657 """ 

658 return {'type': 'boolean'} 

659 

660 def int_schema(self, schema: core_schema.IntSchema) -> JsonSchemaValue: 

661 """Generates a JSON schema that matches an int value. 

662 

663 Args: 

664 schema: The core schema. 

665 

666 Returns: 

667 The generated JSON schema. 

668 """ 

669 json_schema: dict[str, Any] = {'type': 'integer'} 

670 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 

671 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 

672 return json_schema 

673 

674 def float_schema(self, schema: core_schema.FloatSchema) -> JsonSchemaValue: 

675 """Generates a JSON schema that matches a float value. 

676 

677 Args: 

678 schema: The core schema. 

679 

680 Returns: 

681 The generated JSON schema. 

682 """ 

683 json_schema: dict[str, Any] = {'type': 'number'} 

684 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 

685 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 

686 return json_schema 

687 

688 def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue: 

689 """Generates a JSON schema that matches a decimal value. 

690 

691 Args: 

692 schema: The core schema. 

693 

694 Returns: 

695 The generated JSON schema. 

696 """ 

697 

698 def get_decimal_pattern(schema: core_schema.DecimalSchema) -> str: 

699 max_digits = schema.get('max_digits') 

700 decimal_places = schema.get('decimal_places') 

701 

702 pattern = ( 

703 r'^(?!^[-+.]*$)[+-]?0*' # check it is not empty string and not one or sequence of ".+-" characters. 

704 ) 

705 

706 # Case 1: Both max_digits and decimal_places are set 

707 if max_digits is not None and decimal_places is not None: 

708 integer_places = max(0, max_digits - decimal_places) 

709 pattern += ( 

710 rf'(?:' 

711 rf'\d{{0,{integer_places}}}' 

712 rf'|' 

713 rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)' 

714 rf'\d{{0,{integer_places}}}\.\d{{0,{decimal_places}}}0*$' 

715 rf')' 

716 ) 

717 

718 # Case 2: Only max_digits is set 

719 elif max_digits is not None and decimal_places is None: 

720 pattern += ( 

721 rf'(?:' 

722 rf'\d{{0,{max_digits}}}' 

723 rf'|' 

724 rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)' 

725 rf'\d*\.\d*0*$' 

726 rf')' 

727 ) 

728 

729 # Case 3: Only decimal_places is set 

730 elif max_digits is None and decimal_places is not None: 

731 pattern += rf'\d*\.?\d{{0,{decimal_places}}}0*$' 

732 

733 # Case 4: Both are None (no restrictions) 

734 else: 

735 pattern += r'\d*\.?\d*$' # look for arbitrary integer or decimal 

736 

737 return pattern 

738 

739 json_schema = self.str_schema(core_schema.str_schema(pattern=get_decimal_pattern(schema))) 

740 if self.mode == 'validation': 

741 multiple_of = schema.get('multiple_of') 

742 le = schema.get('le') 

743 ge = schema.get('ge') 

744 lt = schema.get('lt') 

745 gt = schema.get('gt') 

746 json_schema = { 

747 'anyOf': [ 

748 self.float_schema( 

749 core_schema.float_schema( 

750 allow_inf_nan=schema.get('allow_inf_nan'), 

751 multiple_of=None if multiple_of is None else float(multiple_of), 

752 le=None if le is None else float(le), 

753 ge=None if ge is None else float(ge), 

754 lt=None if lt is None else float(lt), 

755 gt=None if gt is None else float(gt), 

756 ) 

757 ), 

758 json_schema, 

759 ], 

760 } 

761 return json_schema 

762 

763 def str_schema(self, schema: core_schema.StringSchema) -> JsonSchemaValue: 

764 """Generates a JSON schema that matches a string value. 

765 

766 Args: 

767 schema: The core schema. 

768 

769 Returns: 

770 The generated JSON schema. 

771 """ 

772 json_schema = {'type': 'string'} 

773 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 

774 if isinstance(json_schema.get('pattern'), Pattern): 

775 # TODO: should we add regex flags to the pattern? 

776 json_schema['pattern'] = json_schema.get('pattern').pattern # type: ignore 

777 return json_schema 

778 

779 def bytes_schema(self, schema: core_schema.BytesSchema) -> JsonSchemaValue: 

780 """Generates a JSON schema that matches a bytes value. 

781 

782 Args: 

783 schema: The core schema. 

784 

785 Returns: 

786 The generated JSON schema. 

787 """ 

788 json_schema = {'type': 'string', 'format': 'base64url' if self._config.ser_json_bytes == 'base64' else 'binary'} 

789 self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes) 

790 return json_schema 

791 

792 def date_schema(self, schema: core_schema.DateSchema) -> JsonSchemaValue: 

793 """Generates a JSON schema that matches a date value. 

794 

795 Args: 

796 schema: The core schema. 

797 

798 Returns: 

799 The generated JSON schema. 

800 """ 

801 return {'type': 'string', 'format': 'date'} 

802 

803 def time_schema(self, schema: core_schema.TimeSchema) -> JsonSchemaValue: 

804 """Generates a JSON schema that matches a time value. 

805 

806 Args: 

807 schema: The core schema. 

808 

809 Returns: 

810 The generated JSON schema. 

811 """ 

812 return {'type': 'string', 'format': 'time'} 

813 

814 def datetime_schema(self, schema: core_schema.DatetimeSchema) -> JsonSchemaValue: 

815 """Generates a JSON schema that matches a datetime value. 

816 

817 Args: 

818 schema: The core schema. 

819 

820 Returns: 

821 The generated JSON schema. 

822 """ 

823 return {'type': 'string', 'format': 'date-time'} 

824 

825 def timedelta_schema(self, schema: core_schema.TimedeltaSchema) -> JsonSchemaValue: 

826 """Generates a JSON schema that matches a timedelta value. 

827 

828 Args: 

829 schema: The core schema. 

830 

831 Returns: 

832 The generated JSON schema. 

833 """ 

834 if self._config.ser_json_timedelta == 'float': 

835 return {'type': 'number'} 

836 return {'type': 'string', 'format': 'duration'} 

837 

838 def literal_schema(self, schema: core_schema.LiteralSchema) -> JsonSchemaValue: 

839 """Generates a JSON schema that matches a literal value. 

840 

841 Args: 

842 schema: The core schema. 

843 

844 Returns: 

845 The generated JSON schema. 

846 """ 

847 expected = [to_jsonable_python(v.value if isinstance(v, Enum) else v) for v in schema['expected']] 

848 

849 result: dict[str, Any] = {} 

850 if len(expected) == 1: 

851 result['const'] = expected[0] 

852 else: 

853 result['enum'] = expected 

854 

855 types = {type(e) for e in expected} 

856 if types == {str}: 

857 result['type'] = 'string' 

858 elif types == {int}: 

859 result['type'] = 'integer' 

860 elif types == {float}: 

861 result['type'] = 'number' 

862 elif types == {bool}: 

863 result['type'] = 'boolean' 

864 elif types == {list}: 

865 result['type'] = 'array' 

866 elif types == {type(None)}: 

867 result['type'] = 'null' 

868 return result 

869 

870 def missing_sentinel_schema(self, schema: core_schema.MissingSentinelSchema) -> JsonSchemaValue: 

871 """Generates a JSON schema that matches the `MISSING` sentinel value. 

872 

873 Args: 

874 schema: The core schema. 

875 

876 Returns: 

877 The generated JSON schema. 

878 """ 

879 raise PydanticOmit 

880 

881 def enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue: 

882 """Generates a JSON schema that matches an Enum value. 

883 

884 Args: 

885 schema: The core schema. 

886 

887 Returns: 

888 The generated JSON schema. 

889 """ 

890 enum_type = schema['cls'] 

891 description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__) 

892 if ( 

893 description == 'An enumeration.' 

894 ): # This is the default value provided by enum.EnumMeta.__new__; don't use it 

895 description = None 

896 result: dict[str, Any] = {'title': enum_type.__name__, 'description': description} 

897 result = {k: v for k, v in result.items() if v is not None} 

898 

899 expected = [to_jsonable_python(v.value) for v in schema['members']] 

900 

901 result['enum'] = expected 

902 

903 types = {type(e) for e in expected} 

904 if isinstance(enum_type, str) or types == {str}: 

905 result['type'] = 'string' 

906 elif isinstance(enum_type, int) or types == {int}: 

907 result['type'] = 'integer' 

908 elif isinstance(enum_type, float) or types == {float}: 

909 result['type'] = 'number' 

910 elif types == {bool}: 

911 result['type'] = 'boolean' 

912 elif types == {list}: 

913 result['type'] = 'array' 

914 

915 return result 

916 

917 def is_instance_schema(self, schema: core_schema.IsInstanceSchema) -> JsonSchemaValue: 

918 """Handles JSON schema generation for a core schema that checks if a value is an instance of a class. 

919 

920 Unless overridden in a subclass, this raises an error. 

921 

922 Args: 

923 schema: The core schema. 

924 

925 Returns: 

926 The generated JSON schema. 

927 """ 

928 return self.handle_invalid_for_json_schema(schema, f'core_schema.IsInstanceSchema ({schema["cls"]})') 

929 

930 def is_subclass_schema(self, schema: core_schema.IsSubclassSchema) -> JsonSchemaValue: 

931 """Handles JSON schema generation for a core schema that checks if a value is a subclass of a class. 

932 

933 For backwards compatibility with v1, this does not raise an error, but can be overridden to change this. 

934 

935 Args: 

936 schema: The core schema. 

937 

938 Returns: 

939 The generated JSON schema. 

940 """ 

941 # Note: This is for compatibility with V1; you can override if you want different behavior. 

942 return {} 

943 

944 def callable_schema(self, schema: core_schema.CallableSchema) -> JsonSchemaValue: 

945 """Generates a JSON schema that matches a callable value. 

946 

947 Unless overridden in a subclass, this raises an error. 

948 

949 Args: 

950 schema: The core schema. 

951 

952 Returns: 

953 The generated JSON schema. 

954 """ 

955 return self.handle_invalid_for_json_schema(schema, 'core_schema.CallableSchema') 

956 

957 def list_schema(self, schema: core_schema.ListSchema) -> JsonSchemaValue: 

958 """Returns a schema that matches a list schema. 

959 

960 Args: 

961 schema: The core schema. 

962 

963 Returns: 

964 The generated JSON schema. 

965 """ 

966 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 

967 json_schema = {'type': 'array', 'items': items_schema} 

968 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 

969 return json_schema 

970 

971 @deprecated('`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', category=None) 

972 @final 

973 def tuple_positional_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 

974 """Replaced by `tuple_schema`.""" 

975 warnings.warn( 

976 '`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', 

977 PydanticDeprecatedSince26, 

978 stacklevel=2, 

979 ) 

980 return self.tuple_schema(schema) 

981 

982 @deprecated('`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', category=None) 

983 @final 

984 def tuple_variable_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 

985 """Replaced by `tuple_schema`.""" 

986 warnings.warn( 

987 '`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', 

988 PydanticDeprecatedSince26, 

989 stacklevel=2, 

990 ) 

991 return self.tuple_schema(schema) 

992 

993 def tuple_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 

994 """Generates a JSON schema that matches a tuple schema e.g. `tuple[int, 

995 str, bool]` or `tuple[int, ...]`. 

996 

997 Args: 

998 schema: The core schema. 

999 

1000 Returns: 

1001 The generated JSON schema. 

1002 """ 

1003 json_schema: JsonSchemaValue = {'type': 'array'} 

1004 if 'variadic_item_index' in schema: 

1005 variadic_item_index = schema['variadic_item_index'] 

1006 if variadic_item_index > 0: 

1007 json_schema['minItems'] = variadic_item_index 

1008 json_schema['prefixItems'] = [ 

1009 self.generate_inner(item) for item in schema['items_schema'][:variadic_item_index] 

1010 ] 

1011 if variadic_item_index + 1 == len(schema['items_schema']): 

1012 # if the variadic item is the last item, then represent it faithfully 

1013 json_schema['items'] = self.generate_inner(schema['items_schema'][variadic_item_index]) 

1014 else: 

1015 # otherwise, 'items' represents the schema for the variadic 

1016 # item plus the suffix, so just allow anything for simplicity 

1017 # for now 

1018 json_schema['items'] = True 

1019 else: 

1020 prefixItems = [self.generate_inner(item) for item in schema['items_schema']] 

1021 if prefixItems: 

1022 json_schema['prefixItems'] = prefixItems 

1023 json_schema['minItems'] = len(prefixItems) 

1024 json_schema['maxItems'] = len(prefixItems) 

1025 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 

1026 return json_schema 

1027 

1028 def set_schema(self, schema: core_schema.SetSchema) -> JsonSchemaValue: 

1029 """Generates a JSON schema that matches a set schema. 

1030 

1031 Args: 

1032 schema: The core schema. 

1033 

1034 Returns: 

1035 The generated JSON schema. 

1036 """ 

1037 return self._common_set_schema(schema) 

1038 

1039 def frozenset_schema(self, schema: core_schema.FrozenSetSchema) -> JsonSchemaValue: 

1040 """Generates a JSON schema that matches a frozenset schema. 

1041 

1042 Args: 

1043 schema: The core schema. 

1044 

1045 Returns: 

1046 The generated JSON schema. 

1047 """ 

1048 return self._common_set_schema(schema) 

1049 

1050 def _common_set_schema(self, schema: core_schema.SetSchema | core_schema.FrozenSetSchema) -> JsonSchemaValue: 

1051 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 

1052 json_schema = {'type': 'array', 'uniqueItems': True, 'items': items_schema} 

1053 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 

1054 return json_schema 

1055 

1056 def generator_schema(self, schema: core_schema.GeneratorSchema) -> JsonSchemaValue: 

1057 """Returns a JSON schema that represents the provided GeneratorSchema. 

1058 

1059 Args: 

1060 schema: The schema. 

1061 

1062 Returns: 

1063 The generated JSON schema. 

1064 """ 

1065 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 

1066 json_schema = {'type': 'array', 'items': items_schema} 

1067 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 

1068 return json_schema 

1069 

1070 def dict_schema(self, schema: core_schema.DictSchema) -> JsonSchemaValue: 

1071 """Generates a JSON schema that matches a dict schema. 

1072 

1073 Args: 

1074 schema: The core schema. 

1075 

1076 Returns: 

1077 The generated JSON schema. 

1078 """ 

1079 json_schema: JsonSchemaValue = {'type': 'object'} 

1080 

1081 keys_schema = self.generate_inner(schema['keys_schema']).copy() if 'keys_schema' in schema else {} 

1082 if '$ref' not in keys_schema: 

1083 keys_pattern = keys_schema.pop('pattern', None) 

1084 # Don't give a title to patternProperties/propertyNames: 

1085 keys_schema.pop('title', None) 

1086 else: 

1087 # Here, we assume that if the keys schema is a definition reference, 

1088 # it can't be a simple string core schema (and thus no pattern can exist). 

1089 # However, this is only in practice (in theory, a definition reference core 

1090 # schema could be generated for a simple string schema). 

1091 # Note that we avoid calling `self.resolve_ref_schema`, as it might not exist yet. 

1092 keys_pattern = None 

1093 

1094 values_schema = self.generate_inner(schema['values_schema']).copy() if 'values_schema' in schema else {} 

1095 # don't give a title to additionalProperties: 

1096 values_schema.pop('title', None) 

1097 

1098 if values_schema or keys_pattern is not None: 

1099 if keys_pattern is None: 

1100 json_schema['additionalProperties'] = values_schema 

1101 else: 

1102 json_schema['patternProperties'] = {keys_pattern: values_schema} 

1103 else: # for `dict[str, Any]`, we allow any key and any value, since `str` is the default key type 

1104 json_schema['additionalProperties'] = True 

1105 

1106 if ( 

1107 # The len check indicates that constraints are probably present: 

1108 (keys_schema.get('type') == 'string' and len(keys_schema) > 1) 

1109 # If this is a definition reference schema, it most likely has constraints: 

1110 or '$ref' in keys_schema 

1111 ): 

1112 keys_schema.pop('type', None) 

1113 json_schema['propertyNames'] = keys_schema 

1114 

1115 self.update_with_validations(json_schema, schema, self.ValidationsMapping.object) 

1116 return json_schema 

1117 

1118 def function_before_schema(self, schema: core_schema.BeforeValidatorFunctionSchema) -> JsonSchemaValue: 

1119 """Generates a JSON schema that matches a function-before schema. 

1120 

1121 Args: 

1122 schema: The core schema. 

1123 

1124 Returns: 

1125 The generated JSON schema. 

1126 """ 

1127 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 

1128 return self.generate_inner(input_schema) 

1129 

1130 return self.generate_inner(schema['schema']) 

1131 

1132 def function_after_schema(self, schema: core_schema.AfterValidatorFunctionSchema) -> JsonSchemaValue: 

1133 """Generates a JSON schema that matches a function-after schema. 

1134 

1135 Args: 

1136 schema: The core schema. 

1137 

1138 Returns: 

1139 The generated JSON schema. 

1140 """ 

1141 return self.generate_inner(schema['schema']) 

1142 

1143 def function_plain_schema(self, schema: core_schema.PlainValidatorFunctionSchema) -> JsonSchemaValue: 

1144 """Generates a JSON schema that matches a function-plain schema. 

1145 

1146 Args: 

1147 schema: The core schema. 

1148 

1149 Returns: 

1150 The generated JSON schema. 

1151 """ 

1152 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 

1153 return self.generate_inner(input_schema) 

1154 

1155 return self.handle_invalid_for_json_schema( 

1156 schema, f'core_schema.PlainValidatorFunctionSchema ({schema["function"]})' 

1157 ) 

1158 

1159 def function_wrap_schema(self, schema: core_schema.WrapValidatorFunctionSchema) -> JsonSchemaValue: 

1160 """Generates a JSON schema that matches a function-wrap schema. 

1161 

1162 Args: 

1163 schema: The core schema. 

1164 

1165 Returns: 

1166 The generated JSON schema. 

1167 """ 

1168 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 

1169 return self.generate_inner(input_schema) 

1170 

1171 return self.generate_inner(schema['schema']) 

1172 

1173 def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue: 

1174 """Generates a JSON schema that matches a schema with a default value. 

1175 

1176 Args: 

1177 schema: The core schema. 

1178 

1179 Returns: 

1180 The generated JSON schema. 

1181 """ 

1182 json_schema = self.generate_inner(schema['schema']) 

1183 

1184 default = self.get_default_value(schema) 

1185 if default is NoDefault or default is MISSING: 

1186 return json_schema 

1187 

1188 # we reflect the application of custom plain, no-info serializers to defaults for 

1189 # JSON Schemas viewed in serialization mode: 

1190 # TODO: improvements along with https://github.com/pydantic/pydantic/issues/8208 

1191 if self.mode == 'serialization': 

1192 # `_get_ser_schema_for_default_value()` is used to unpack potentially nested validator schemas: 

1193 ser_schema = _get_ser_schema_for_default_value(schema['schema']) 

1194 if ( 

1195 ser_schema is not None 

1196 and (ser_func := ser_schema.get('function')) 

1197 and not (default is None and ser_schema.get('when_used') in ('unless-none', 'json-unless-none')) 

1198 ): 

1199 try: 

1200 default = ser_func(default) # type: ignore 

1201 except Exception: 

1202 # It might be that the provided default needs to be validated (read: parsed) first 

1203 # (assuming `validate_default` is enabled). However, we can't perform 

1204 # such validation during JSON Schema generation so we don't support 

1205 # this pattern for now. 

1206 # (One example is when using `foo: ByteSize = '1MB'`, which validates and 

1207 # serializes as an int. In this case, `ser_func` is `int` and `int('1MB')` fails). 

1208 self.emit_warning( 

1209 'non-serializable-default', 

1210 f'Unable to serialize value {default!r} with the plain serializer; excluding default from JSON schema', 

1211 ) 

1212 return json_schema 

1213 

1214 # Sort set/frozenset defaults to ensure deterministic JSON schema generation 

1215 # We only sort if len > 1 because sets of size 0 or 1 are already deterministic 

1216 if isinstance(default, collections.abc.Set) and len(default) > 1: 

1217 try: 

1218 default = sorted(default) 

1219 except TypeError: # pragma: no cover 

1220 # If items aren't comparable (e.g. mixed types), we can't sort them. 

1221 pass 

1222 

1223 try: 

1224 encoded_default = self.encode_default(default) 

1225 except pydantic_core.PydanticSerializationError: 

1226 self.emit_warning( 

1227 'non-serializable-default', 

1228 f'Default value {default} is not JSON serializable; excluding default from JSON schema', 

1229 ) 

1230 # Return the inner schema, as though there was no default 

1231 return json_schema 

1232 

1233 json_schema['default'] = encoded_default 

1234 return json_schema 

1235 

1236 def get_default_value(self, schema: core_schema.WithDefaultSchema) -> Any: 

1237 """Get the default value to be used when generating a JSON Schema for a core schema with a default. 

1238 

1239 The default implementation is to use the statically defined default value. This method can be overridden 

1240 if you want to make use of the default factory. 

1241 

1242 Args: 

1243 schema: The `'with-default'` core schema. 

1244 

1245 Returns: 

1246 The default value to use, or [`NoDefault`][pydantic.json_schema.NoDefault] if no default 

1247 value is available. 

1248 """ 

1249 return schema.get('default', NoDefault) 

1250 

1251 def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue: 

1252 """Generates a JSON schema that matches a schema that allows null values. 

1253 

1254 Args: 

1255 schema: The core schema. 

1256 

1257 Returns: 

1258 The generated JSON schema. 

1259 """ 

1260 null_schema = {'type': 'null'} 

1261 inner_json_schema = self.generate_inner(schema['schema']) 

1262 

1263 if inner_json_schema == null_schema: 

1264 return null_schema 

1265 else: 

1266 return self.get_union_of_schemas([inner_json_schema, null_schema]) 

1267 

1268 def union_schema(self, schema: core_schema.UnionSchema) -> JsonSchemaValue: 

1269 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas. 

1270 

1271 Args: 

1272 schema: The core schema. 

1273 

1274 Returns: 

1275 The generated JSON schema. 

1276 """ 

1277 generated: list[JsonSchemaValue] = [] 

1278 

1279 for choice in core_schema.iter_union_choices(schema): 

1280 try: 

1281 generated.append(self.generate_inner(choice)) 

1282 except PydanticOmit: # noqa: PERF203 

1283 continue 

1284 except PydanticInvalidForJsonSchema as exc: 

1285 self.emit_warning('skipped-choice', exc.message) 

1286 if len(generated) == 1: 

1287 return generated[0] 

1288 return self.get_union_of_schemas(generated) 

1289 

1290 def get_union_of_schemas(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue: 

1291 """Returns the JSON Schema representation for the union of the provided JSON Schemas. 

1292 

1293 The result depends on the configured `'union_format'`. 

1294 

1295 Args: 

1296 schemas: The list of JSON Schemas to be included in the union. 

1297 

1298 Returns: 

1299 The JSON Schema representing the union of schemas. 

1300 """ 

1301 if self.union_format == 'primitive_type_array': 

1302 types: list[str] = [] 

1303 for schema in schemas: 

1304 schema_types: list[str] | str | None = schema.get('type') 

1305 if schema_types is None: 

1306 # No type, meaning it can be a ref or an empty schema. 

1307 break 

1308 if not isinstance(schema_types, list): 

1309 schema_types = [schema_types] 

1310 if not all(t in _PRIMITIVE_JSON_SCHEMA_TYPES for t in schema_types): 

1311 break 

1312 if len(schema) != 1: 

1313 # We only want to include types that don't have any constraints. For instance, 

1314 # if `schemas = [{'type': 'string', 'maxLength': 3}, {'type': 'string', 'minLength': 5}]`, 

1315 # we don't want to produce `{'type': 'string', 'maxLength': 3, 'minLength': 5}`. 

1316 # Same if we have some metadata (e.g. `title`) on a specific union member, we want to preserve it. 

1317 break 

1318 

1319 types.extend(schema_types) 

1320 else: 

1321 # If we got there, all the schemas where valid to be used with the `'primitive_type_array` format 

1322 return {'type': list(dict.fromkeys(types))} 

1323 

1324 return self.get_flattened_anyof(schemas) 

1325 

1326 def tagged_union_schema(self, schema: core_schema.TaggedUnionSchema) -> JsonSchemaValue: 

1327 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas, where 

1328 the schemas are tagged with a discriminator field that indicates which schema should be used to validate 

1329 the value. 

1330 

1331 Args: 

1332 schema: The core schema. 

1333 

1334 Returns: 

1335 The generated JSON schema. 

1336 """ 

1337 generated: dict[str, JsonSchemaValue] = {} 

1338 for k, v in schema['choices'].items(): 

1339 if isinstance(k, Enum): 

1340 k = k.value 

1341 try: 

1342 # Use str(k) since keys must be strings for json; while not technically correct, 

1343 # it's the closest that can be represented in valid JSON 

1344 generated[str(k)] = self.generate_inner(v).copy() 

1345 except PydanticOmit: 

1346 continue 

1347 except PydanticInvalidForJsonSchema as exc: 

1348 self.emit_warning('skipped-choice', exc.message) 

1349 

1350 one_of_choices = _deduplicate_schemas(generated.values()) 

1351 json_schema: JsonSchemaValue = {'oneOf': one_of_choices} 

1352 

1353 # This reflects the v1 behavior; TODO: we should make it possible to exclude OpenAPI stuff from the JSON schema 

1354 openapi_discriminator = self._extract_discriminator(schema, one_of_choices) 

1355 if openapi_discriminator is not None: 

1356 json_schema['discriminator'] = { 

1357 'propertyName': openapi_discriminator, 

1358 'mapping': {k: v.get('$ref', v) for k, v in generated.items()}, 

1359 } 

1360 

1361 return json_schema 

1362 

1363 def _extract_discriminator( 

1364 self, schema: core_schema.TaggedUnionSchema, one_of_choices: list[JsonDict] 

1365 ) -> str | None: 

1366 """Extract a compatible OpenAPI discriminator from the schema and one_of choices that end up in the final 

1367 schema.""" 

1368 openapi_discriminator: str | None = None 

1369 

1370 if isinstance(schema['discriminator'], str): 

1371 return schema['discriminator'] 

1372 

1373 if isinstance(schema['discriminator'], list): 

1374 # If the discriminator is a single item list containing a string, that is equivalent to the string case 

1375 if len(schema['discriminator']) == 1 and isinstance(schema['discriminator'][0], str): 

1376 return schema['discriminator'][0] 

1377 # When an alias is used that is different from the field name, the discriminator will be a list of single 

1378 # str lists, one for the attribute and one for the actual alias. The logic here will work even if there is 

1379 # more than one possible attribute, and looks for whether a single alias choice is present as a documented 

1380 # property on all choices. If so, that property will be used as the OpenAPI discriminator. 

1381 for alias_path in schema['discriminator']: 

1382 if not isinstance(alias_path, list): 

1383 break # this means that the discriminator is not a list of alias paths 

1384 if len(alias_path) != 1: 

1385 continue # this means that the "alias" does not represent a single field 

1386 alias = alias_path[0] 

1387 if not isinstance(alias, str): 

1388 continue # this means that the "alias" does not represent a field 

1389 alias_is_present_on_all_choices = True 

1390 for choice in one_of_choices: 

1391 try: 

1392 choice = self.resolve_ref_schema(choice) 

1393 except RuntimeError as exc: 

1394 # TODO: fixme - this is a workaround for the fact that we can't always resolve refs 

1395 # for tagged union choices at this point in the schema gen process, we might need to do 

1396 # another pass at the end like we do for core schemas 

1397 self.emit_warning('skipped-discriminator', str(exc)) 

1398 choice = {} 

1399 properties = choice.get('properties', {}) 

1400 if not isinstance(properties, dict) or alias not in properties: 

1401 alias_is_present_on_all_choices = False 

1402 break 

1403 if alias_is_present_on_all_choices: 

1404 openapi_discriminator = alias 

1405 break 

1406 return openapi_discriminator 

1407 

1408 def chain_schema(self, schema: core_schema.ChainSchema) -> JsonSchemaValue: 

1409 """Generates a JSON schema that matches a core_schema.ChainSchema. 

1410 

1411 When generating a schema for validation, we return the validation JSON schema for the first step in the chain. 

1412 For serialization, we return the serialization JSON schema for the last step in the chain. 

1413 

1414 Args: 

1415 schema: The core schema. 

1416 

1417 Returns: 

1418 The generated JSON schema. 

1419 """ 

1420 step_index = 0 if self.mode == 'validation' else -1 # use first step for validation, last for serialization 

1421 return self.generate_inner(schema['steps'][step_index]) 

1422 

1423 def lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema) -> JsonSchemaValue: 

1424 """Generates a JSON schema that matches a schema that allows values matching either the lax schema or the 

1425 strict schema. 

1426 

1427 Args: 

1428 schema: The core schema. 

1429 

1430 Returns: 

1431 The generated JSON schema. 

1432 """ 

1433 # TODO: Need to read the default value off of model config or whatever 

1434 use_strict = schema.get('strict', False) # TODO: replace this default False 

1435 # If your JSON schema fails to generate it is probably 

1436 # because one of the following two branches failed. 

1437 if use_strict: 

1438 return self.generate_inner(schema['strict_schema']) 

1439 else: 

1440 return self.generate_inner(schema['lax_schema']) 

1441 

1442 def json_or_python_schema(self, schema: core_schema.JsonOrPythonSchema) -> JsonSchemaValue: 

1443 """Generates a JSON schema that matches a schema that allows values matching either the JSON schema or the 

1444 Python schema. 

1445 

1446 The JSON schema is used instead of the Python schema. If you want to use the Python schema, you should override 

1447 this method. 

1448 

1449 Args: 

1450 schema: The core schema. 

1451 

1452 Returns: 

1453 The generated JSON schema. 

1454 """ 

1455 return self.generate_inner(schema['json_schema']) 

1456 

1457 def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue: 

1458 """Generates a JSON schema that matches a schema that defines a typed dict. 

1459 

1460 Args: 

1461 schema: The core schema. 

1462 

1463 Returns: 

1464 The generated JSON schema. 

1465 """ 

1466 total = schema.get('total', True) 

1467 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 

1468 (name, self.field_is_required(field, total), field) 

1469 for name, field in schema['fields'].items() 

1470 if self.field_is_present(field) 

1471 ] 

1472 if self.mode == 'serialization': 

1473 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 

1474 cls = schema.get('cls') 

1475 config = _get_typed_dict_config(cls) 

1476 with self._config_wrapper_stack.push(config): 

1477 json_schema = self._named_required_fields_schema(named_required_fields) 

1478 

1479 # There's some duplication between `extra_behavior` and 

1480 # the config's `extra`/core config's `extra_fields_behavior`. 

1481 # However, it is common to manually create TypedDictSchemas, 

1482 # where you don't necessarily have a class. 

1483 # At runtime, `extra_behavior` takes priority over the config 

1484 # for validation, so follow the same for the JSON Schema: 

1485 if 'extras_schema' in schema and schema['extras_schema'] != core_schema.any_schema(): 

1486 allow_additional_props = self.generate_inner(schema['extras_schema']) 

1487 else: 

1488 allow_additional_props = True 

1489 

1490 if schema.get('extra_behavior') == 'forbid': 

1491 json_schema['additionalProperties'] = False 

1492 elif schema.get('extra_behavior') == 'allow': 

1493 json_schema['additionalProperties'] = allow_additional_props 

1494 

1495 if cls is not None: 

1496 # `_update_class_schema()` will not override 

1497 # `additionalProperties` if already present: 

1498 self._update_class_schema(json_schema, cls, config) 

1499 elif 'additionalProperties' not in json_schema: 

1500 extra = schema.get('config', {}).get('extra_fields_behavior') 

1501 if extra == 'forbid': 

1502 json_schema['additionalProperties'] = False 

1503 elif extra == 'allow': 

1504 json_schema['additionalProperties'] = allow_additional_props 

1505 

1506 return json_schema 

1507 

1508 @staticmethod 

1509 def _name_required_computed_fields( 

1510 computed_fields: list[ComputedField], 

1511 ) -> list[tuple[str, bool, core_schema.ComputedField]]: 

1512 return [(field['property_name'], True, field) for field in computed_fields] 

1513 

1514 def _named_required_fields_schema( 

1515 self, named_required_fields: Sequence[tuple[str, bool, CoreSchemaField]] 

1516 ) -> JsonSchemaValue: 

1517 properties: dict[str, JsonSchemaValue] = {} 

1518 required_fields: list[str] = [] 

1519 for name, required, field in named_required_fields: 

1520 if self.by_alias: 

1521 name = self._get_alias_name(field, name) 

1522 try: 

1523 field_json_schema = self.generate_inner(field).copy() 

1524 except PydanticOmit: 

1525 continue 

1526 if 'title' not in field_json_schema and self.field_title_should_be_set(field): 

1527 title = self.get_title_from_name(name) 

1528 field_json_schema['title'] = title 

1529 field_json_schema = self.handle_ref_overrides(field_json_schema) 

1530 properties[name] = field_json_schema 

1531 if required: 

1532 required_fields.append(name) 

1533 

1534 json_schema = {'type': 'object', 'properties': properties} 

1535 if required_fields: 

1536 json_schema['required'] = required_fields 

1537 return json_schema 

1538 

1539 def _get_alias_name(self, field: CoreSchemaField, name: str) -> str: 

1540 if field['type'] == 'computed-field': 

1541 alias: Any = field.get('alias', name) 

1542 elif self.mode == 'validation': 

1543 alias = field.get('validation_alias', name) 

1544 else: 

1545 alias = field.get('serialization_alias', name) 

1546 if isinstance(alias, str): 

1547 name = alias 

1548 elif isinstance(alias, list): 

1549 alias = cast('list[str] | str', alias) 

1550 for path in alias: 

1551 if isinstance(path, list) and len(path) == 1 and isinstance(path[0], str): 

1552 # Use the first valid single-item string path; the code that constructs the alias array 

1553 # should ensure the first such item is what belongs in the JSON schema 

1554 name = path[0] 

1555 break 

1556 else: 

1557 assert_never(alias) 

1558 return name 

1559 

1560 def typed_dict_field_schema(self, schema: core_schema.TypedDictField) -> JsonSchemaValue: 

1561 """Generates a JSON schema that matches a schema that defines a typed dict field. 

1562 

1563 Args: 

1564 schema: The core schema. 

1565 

1566 Returns: 

1567 The generated JSON schema. 

1568 """ 

1569 return self.generate_inner(schema['schema']) 

1570 

1571 def dataclass_field_schema(self, schema: core_schema.DataclassField) -> JsonSchemaValue: 

1572 """Generates a JSON schema that matches a schema that defines a dataclass field. 

1573 

1574 Args: 

1575 schema: The core schema. 

1576 

1577 Returns: 

1578 The generated JSON schema. 

1579 """ 

1580 return self.generate_inner(schema['schema']) 

1581 

1582 def model_field_schema(self, schema: core_schema.ModelField) -> JsonSchemaValue: 

1583 """Generates a JSON schema that matches a schema that defines a model field. 

1584 

1585 Args: 

1586 schema: The core schema. 

1587 

1588 Returns: 

1589 The generated JSON schema. 

1590 """ 

1591 return self.generate_inner(schema['schema']) 

1592 

1593 def computed_field_schema(self, schema: core_schema.ComputedField) -> JsonSchemaValue: 

1594 """Generates a JSON schema that matches a schema that defines a computed field. 

1595 

1596 Args: 

1597 schema: The core schema. 

1598 

1599 Returns: 

1600 The generated JSON schema. 

1601 """ 

1602 return self.generate_inner(schema['return_schema']) 

1603 

1604 def model_schema(self, schema: core_schema.ModelSchema) -> JsonSchemaValue: 

1605 """Generates a JSON schema that matches a schema that defines a model. 

1606 

1607 Args: 

1608 schema: The core schema. 

1609 

1610 Returns: 

1611 The generated JSON schema. 

1612 """ 

1613 # We do not use schema['model'].model_json_schema() here 

1614 # because it could lead to inconsistent refs handling, etc. 

1615 cls = cast('type[BaseModel]', schema['cls']) 

1616 config = cls.model_config 

1617 

1618 with self._config_wrapper_stack.push(config): 

1619 json_schema = self.generate_inner(schema['schema']) 

1620 

1621 self._update_class_schema(json_schema, cls, config) 

1622 

1623 return json_schema 

1624 

1625 def _update_class_schema(self, json_schema: JsonSchemaValue, cls: type[Any], config: ConfigDict) -> None: 

1626 """Update json_schema with the following, extracted from `config` and `cls`: 

1627 

1628 * title 

1629 * description 

1630 * additional properties 

1631 * json_schema_extra 

1632 * deprecated 

1633 

1634 Done in place, hence there's no return value as the original json_schema is mutated. 

1635 No ref resolving is involved here, as that's not appropriate for simple updates. 

1636 """ 

1637 from ._internal._dataclasses import is_stdlib_dataclass 

1638 from .main import BaseModel 

1639 from .root_model import RootModel 

1640 

1641 if (config_title := config.get('title')) is not None: 

1642 json_schema.setdefault('title', config_title) 

1643 elif model_title_generator := config.get('model_title_generator'): 

1644 title = model_title_generator(cls) 

1645 if not isinstance(title, str): 

1646 raise TypeError(f'model_title_generator {model_title_generator} must return str, not {title.__class__}') 

1647 json_schema.setdefault('title', title) 

1648 if 'title' not in json_schema: 

1649 json_schema['title'] = cls.__name__ 

1650 

1651 # BaseModel and dataclasses; don't use cls.__doc__ as it will contain the verbose class signature by default 

1652 if cls is BaseModel: 

1653 docstring = None 

1654 elif is_stdlib_dataclass(cls): # For Pydantic dataclasses, we already handle this at class creation 

1655 # The `dataclass` module generates a `__doc__` based on the `inspect.signature()` 

1656 # result, which we don't want to use as a description. Such `__doc__` startswith 

1657 # `cls.__name__(`, which could lead to mistakenly discarding it if for some reason 

1658 # an explicitly set class docstring follows the same pattern, but this is unlikely 

1659 # to happen. 

1660 doc = cls.__doc__ 

1661 docstring = None if doc is None or doc.startswith(f'{cls.__name__}(') else doc 

1662 else: 

1663 docstring = cls.__doc__ 

1664 

1665 if docstring: 

1666 json_schema.setdefault('description', inspect.cleandoc(docstring)) 

1667 elif issubclass(cls, RootModel) and (root_description := cls.__pydantic_fields__['root'].description): 

1668 json_schema.setdefault('description', root_description) 

1669 

1670 extra = config.get('extra') 

1671 if 'additionalProperties' not in json_schema: # This check is particularly important for `typed_dict_schema()` 

1672 if extra == 'allow': 

1673 json_schema['additionalProperties'] = True 

1674 elif extra == 'forbid': 

1675 json_schema['additionalProperties'] = False 

1676 

1677 json_schema_extra = config.get('json_schema_extra') 

1678 if issubclass(cls, BaseModel) and cls.__pydantic_root_model__: 

1679 root_json_schema_extra = cls.model_fields['root'].json_schema_extra 

1680 if json_schema_extra and root_json_schema_extra: 

1681 raise ValueError( 

1682 '"model_config[\'json_schema_extra\']" and "Field.json_schema_extra" on "RootModel.root"' 

1683 ' field must not be set simultaneously' 

1684 ) 

1685 if root_json_schema_extra: 

1686 json_schema_extra = root_json_schema_extra 

1687 

1688 if isinstance(json_schema_extra, (staticmethod, classmethod)): 

1689 # In older versions of python, this is necessary to ensure staticmethod/classmethods are callable 

1690 json_schema_extra = json_schema_extra.__get__(cls) 

1691 

1692 if isinstance(json_schema_extra, dict): 

1693 json_schema.update(json_schema_extra) 

1694 elif callable(json_schema_extra): 

1695 if len(_typing_extra.signature_no_eval(json_schema_extra).parameters) > 1: 

1696 json_schema_extra = cast(Callable[[JsonDict, type[Any]], None], json_schema_extra) 

1697 json_schema_extra(json_schema, cls) 

1698 else: 

1699 json_schema_extra = cast(Callable[[JsonDict], None], json_schema_extra) 

1700 json_schema_extra(json_schema) 

1701 elif json_schema_extra is not None: 

1702 raise ValueError( 

1703 f"model_config['json_schema_extra']={json_schema_extra} should be a dict, callable, or None" 

1704 ) 

1705 

1706 if hasattr(cls, '__deprecated__'): 

1707 json_schema['deprecated'] = True 

1708 

1709 def resolve_ref_schema(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 

1710 """Resolve a JsonSchemaValue to the non-ref schema if it is a $ref schema. 

1711 

1712 Args: 

1713 json_schema: The schema to resolve. 

1714 

1715 Returns: 

1716 The resolved schema. 

1717 

1718 Raises: 

1719 RuntimeError: If the schema reference can't be found in definitions. 

1720 """ 

1721 while '$ref' in json_schema: 

1722 ref = json_schema['$ref'] 

1723 schema_to_update = self.get_schema_from_definitions(JsonRef(ref)) 

1724 if schema_to_update is None: 

1725 raise RuntimeError(f'Cannot update undefined schema for $ref={ref}') 

1726 json_schema = schema_to_update 

1727 return json_schema 

1728 

1729 def model_fields_schema(self, schema: core_schema.ModelFieldsSchema) -> JsonSchemaValue: 

1730 """Generates a JSON schema that matches a schema that defines a model's fields. 

1731 

1732 Args: 

1733 schema: The core schema. 

1734 

1735 Returns: 

1736 The generated JSON schema. 

1737 """ 

1738 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 

1739 (name, self.field_is_required(field, total=True), field) 

1740 for name, field in schema['fields'].items() 

1741 if self.field_is_present(field) 

1742 ] 

1743 if self.mode == 'serialization': 

1744 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 

1745 json_schema = self._named_required_fields_schema(named_required_fields) 

1746 extras_schema = schema.get('extras_schema', None) 

1747 if extras_schema is not None: 

1748 schema_to_update = self.resolve_ref_schema(json_schema) 

1749 schema_to_update['additionalProperties'] = self.generate_inner(extras_schema) 

1750 return json_schema 

1751 

1752 def field_is_present(self, field: CoreSchemaField) -> bool: 

1753 """Whether the field should be included in the generated JSON schema. 

1754 

1755 Args: 

1756 field: The schema for the field itself. 

1757 

1758 Returns: 

1759 `True` if the field should be included in the generated JSON schema, `False` otherwise. 

1760 """ 

1761 if self.mode == 'serialization': 

1762 # If you still want to include the field in the generated JSON schema, 

1763 # override this method and return True 

1764 return not field.get('serialization_exclude') 

1765 elif self.mode == 'validation': 

1766 return True 

1767 else: 

1768 assert_never(self.mode) 

1769 

1770 def field_is_required( 

1771 self, 

1772 field: core_schema.ModelField | core_schema.DataclassField | core_schema.TypedDictField, 

1773 total: bool, 

1774 ) -> bool: 

1775 """Whether the field should be marked as required in the generated JSON schema. 

1776 (Note that this is irrelevant if the field is not present in the JSON schema.). 

1777 

1778 Args: 

1779 field: The schema for the field itself. 

1780 total: Only applies to `TypedDictField`s. 

1781 Indicates if the `TypedDict` this field belongs to is total, in which case any fields that don't 

1782 explicitly specify `required=False` are required. 

1783 

1784 Returns: 

1785 `True` if the field should be marked as required in the generated JSON schema, `False` otherwise. 

1786 """ 

1787 if field['type'] == 'typed-dict-field': 

1788 required = field.get('required', total) 

1789 else: 

1790 required = field['schema']['type'] != 'default' 

1791 

1792 if self.mode == 'serialization': 

1793 has_exclude_if = field.get('serialization_exclude_if') is not None 

1794 if self._config.json_schema_serialization_defaults_required: 

1795 return not has_exclude_if 

1796 else: 

1797 return required and not has_exclude_if 

1798 else: 

1799 return required 

1800 

1801 def dataclass_args_schema(self, schema: core_schema.DataclassArgsSchema) -> JsonSchemaValue: 

1802 """Generates a JSON schema that matches a schema that defines a dataclass's constructor arguments. 

1803 

1804 Args: 

1805 schema: The core schema. 

1806 

1807 Returns: 

1808 The generated JSON schema. 

1809 """ 

1810 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 

1811 (field['name'], self.field_is_required(field, total=True), field) 

1812 for field in schema['fields'] 

1813 if self.field_is_present(field) 

1814 ] 

1815 if self.mode == 'serialization': 

1816 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 

1817 return self._named_required_fields_schema(named_required_fields) 

1818 

1819 def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaValue: 

1820 """Generates a JSON schema that matches a schema that defines a dataclass. 

1821 

1822 Args: 

1823 schema: The core schema. 

1824 

1825 Returns: 

1826 The generated JSON schema. 

1827 """ 

1828 

1829 cls = schema['cls'] 

1830 config = cast('ConfigDict', getattr(cls, '__pydantic_config__', {})) 

1831 

1832 with self._config_wrapper_stack.push(config): 

1833 json_schema = self.generate_inner(schema['schema']).copy() 

1834 

1835 self._update_class_schema(json_schema, cls, config) 

1836 

1837 return json_schema 

1838 

1839 def arguments_schema(self, schema: core_schema.ArgumentsSchema) -> JsonSchemaValue: 

1840 """Generates a JSON schema that matches a schema that defines a function's arguments. 

1841 

1842 Args: 

1843 schema: The core schema. 

1844 

1845 Returns: 

1846 The generated JSON schema. 

1847 """ 

1848 prefer_positional = schema.get('metadata', {}).get('pydantic_js_prefer_positional_arguments') 

1849 

1850 arguments = schema['arguments_schema'] 

1851 kw_only_arguments = [a for a in arguments if a.get('mode') == 'keyword_only'] 

1852 kw_or_p_arguments = [a for a in arguments if a.get('mode') in {'positional_or_keyword', None}] 

1853 p_only_arguments = [a for a in arguments if a.get('mode') == 'positional_only'] 

1854 var_args_schema = schema.get('var_args_schema') 

1855 var_kwargs_schema = schema.get('var_kwargs_schema') 

1856 

1857 if prefer_positional: 

1858 positional_possible = not kw_only_arguments and not var_kwargs_schema 

1859 if positional_possible: 

1860 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 

1861 

1862 keyword_possible = not p_only_arguments and not var_args_schema 

1863 if keyword_possible: 

1864 return self.kw_arguments_schema(kw_or_p_arguments + kw_only_arguments, var_kwargs_schema) 

1865 

1866 if not prefer_positional: 

1867 positional_possible = not kw_only_arguments and not var_kwargs_schema 

1868 if positional_possible: 

1869 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 

1870 

1871 raise PydanticInvalidForJsonSchema( 

1872 'Unable to generate JSON schema for arguments validator with positional-only and keyword-only arguments' 

1873 ) 

1874 

1875 def kw_arguments_schema( 

1876 self, arguments: list[core_schema.ArgumentsParameter], var_kwargs_schema: CoreSchema | None 

1877 ) -> JsonSchemaValue: 

1878 """Generates a JSON schema that matches a schema that defines a function's keyword arguments. 

1879 

1880 Args: 

1881 arguments: The core schema. 

1882 

1883 Returns: 

1884 The generated JSON schema. 

1885 """ 

1886 properties: dict[str, JsonSchemaValue] = {} 

1887 required: list[str] = [] 

1888 for argument in arguments: 

1889 name = self.get_argument_name(argument) 

1890 argument_schema = self.generate_inner(argument['schema']).copy() 

1891 if 'title' not in argument_schema and self.field_title_should_be_set(argument['schema']): 

1892 argument_schema['title'] = self.get_title_from_name(name) 

1893 properties[name] = argument_schema 

1894 

1895 if argument['schema']['type'] != 'default': 

1896 # This assumes that if the argument has a default value, 

1897 # the inner schema must be of type WithDefaultSchema. 

1898 # I believe this is true, but I am not 100% sure 

1899 required.append(name) 

1900 

1901 json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} 

1902 if required: 

1903 json_schema['required'] = required 

1904 

1905 if var_kwargs_schema: 

1906 additional_properties_schema = self.generate_inner(var_kwargs_schema) 

1907 if additional_properties_schema: 

1908 json_schema['additionalProperties'] = additional_properties_schema 

1909 else: 

1910 json_schema['additionalProperties'] = False 

1911 return json_schema 

1912 

1913 def p_arguments_schema( 

1914 self, arguments: list[core_schema.ArgumentsParameter], var_args_schema: CoreSchema | None 

1915 ) -> JsonSchemaValue: 

1916 """Generates a JSON schema that matches a schema that defines a function's positional arguments. 

1917 

1918 Args: 

1919 arguments: The core schema. 

1920 

1921 Returns: 

1922 The generated JSON schema. 

1923 """ 

1924 prefix_items: list[JsonSchemaValue] = [] 

1925 min_items = 0 

1926 

1927 for argument in arguments: 

1928 name = self.get_argument_name(argument) 

1929 

1930 argument_schema = self.generate_inner(argument['schema']).copy() 

1931 if 'title' not in argument_schema and self.field_title_should_be_set(argument['schema']): 

1932 argument_schema['title'] = self.get_title_from_name(name) 

1933 prefix_items.append(argument_schema) 

1934 

1935 if argument['schema']['type'] != 'default': 

1936 # This assumes that if the argument has a default value, 

1937 # the inner schema must be of type WithDefaultSchema. 

1938 # I believe this is true, but I am not 100% sure 

1939 min_items += 1 

1940 

1941 json_schema: JsonSchemaValue = {'type': 'array'} 

1942 if prefix_items: 

1943 json_schema['prefixItems'] = prefix_items 

1944 if min_items: 

1945 json_schema['minItems'] = min_items 

1946 

1947 if var_args_schema: 

1948 items_schema = self.generate_inner(var_args_schema) 

1949 if items_schema: 

1950 json_schema['items'] = items_schema 

1951 else: 

1952 json_schema['maxItems'] = len(prefix_items) 

1953 

1954 return json_schema 

1955 

1956 def get_argument_name(self, argument: core_schema.ArgumentsParameter | core_schema.ArgumentsV3Parameter) -> str: 

1957 """Retrieves the name of an argument. 

1958 

1959 Args: 

1960 argument: The core schema. 

1961 

1962 Returns: 

1963 The name of the argument. 

1964 """ 

1965 name = argument['name'] 

1966 if self.by_alias: 

1967 alias = argument.get('alias') 

1968 if isinstance(alias, str): 

1969 name = alias 

1970 else: 

1971 pass # might want to do something else? 

1972 return name 

1973 

1974 def arguments_v3_schema(self, schema: core_schema.ArgumentsV3Schema) -> JsonSchemaValue: 

1975 """Generates a JSON schema that matches a schema that defines a function's arguments. 

1976 

1977 Args: 

1978 schema: The core schema. 

1979 

1980 Returns: 

1981 The generated JSON schema. 

1982 """ 

1983 arguments = schema['arguments_schema'] 

1984 properties: dict[str, JsonSchemaValue] = {} 

1985 required: list[str] = [] 

1986 for argument in arguments: 

1987 mode = argument.get('mode', 'positional_or_keyword') 

1988 name = self.get_argument_name(argument) 

1989 argument_schema = self.generate_inner(argument['schema']).copy() 

1990 if mode == 'var_args': 

1991 argument_schema = {'type': 'array', 'items': argument_schema} 

1992 elif mode == 'var_kwargs_uniform': 

1993 argument_schema = {'type': 'object', 'additionalProperties': argument_schema} 

1994 

1995 argument_schema.setdefault('title', self.get_title_from_name(name)) 

1996 properties[name] = argument_schema 

1997 

1998 if ( 

1999 (mode == 'var_kwargs_unpacked_typed_dict' and 'required' in argument_schema) 

2000 or mode not in {'var_args', 'var_kwargs_uniform', 'var_kwargs_unpacked_typed_dict'} 

2001 and argument['schema']['type'] != 'default' 

2002 ): 

2003 # This assumes that if the argument has a default value, 

2004 # the inner schema must be of type WithDefaultSchema. 

2005 # I believe this is true, but I am not 100% sure 

2006 required.append(name) 

2007 

2008 json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} 

2009 if required: 

2010 json_schema['required'] = required 

2011 return json_schema 

2012 

2013 def call_schema(self, schema: core_schema.CallSchema) -> JsonSchemaValue: 

2014 """Generates a JSON schema that matches a schema that defines a function call. 

2015 

2016 Args: 

2017 schema: The core schema. 

2018 

2019 Returns: 

2020 The generated JSON schema. 

2021 """ 

2022 return self.generate_inner(schema['arguments_schema']) 

2023 

2024 def custom_error_schema(self, schema: core_schema.CustomErrorSchema) -> JsonSchemaValue: 

2025 """Generates a JSON schema that matches a schema that defines a custom error. 

2026 

2027 Args: 

2028 schema: The core schema. 

2029 

2030 Returns: 

2031 The generated JSON schema. 

2032 """ 

2033 return self.generate_inner(schema['schema']) 

2034 

2035 def json_schema(self, schema: core_schema.JsonSchema) -> JsonSchemaValue: 

2036 """Generates a JSON schema that matches a schema that defines a JSON object. 

2037 

2038 Args: 

2039 schema: The core schema. 

2040 

2041 Returns: 

2042 The generated JSON schema. 

2043 """ 

2044 content_core_schema = schema.get('schema') or core_schema.any_schema() 

2045 content_json_schema = self.generate_inner(content_core_schema) 

2046 if self.mode == 'validation': 

2047 return {'type': 'string', 'contentMediaType': 'application/json', 'contentSchema': content_json_schema} 

2048 else: 

2049 # self.mode == 'serialization' 

2050 return content_json_schema 

2051 

2052 def url_schema(self, schema: core_schema.UrlSchema) -> JsonSchemaValue: 

2053 """Generates a JSON schema that matches a schema that defines a URL. 

2054 

2055 Args: 

2056 schema: The core schema. 

2057 

2058 Returns: 

2059 The generated JSON schema. 

2060 """ 

2061 json_schema = {'type': 'string', 'format': 'uri', 'minLength': 1} 

2062 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 

2063 return json_schema 

2064 

2065 def multi_host_url_schema(self, schema: core_schema.MultiHostUrlSchema) -> JsonSchemaValue: 

2066 """Generates a JSON schema that matches a schema that defines a URL that can be used with multiple hosts. 

2067 

2068 Args: 

2069 schema: The core schema. 

2070 

2071 Returns: 

2072 The generated JSON schema. 

2073 """ 

2074 # Note: 'multi-host-uri' is a custom/pydantic-specific format, not part of the JSON Schema spec 

2075 json_schema = {'type': 'string', 'format': 'multi-host-uri', 'minLength': 1} 

2076 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 

2077 return json_schema 

2078 

2079 def uuid_schema(self, schema: core_schema.UuidSchema) -> JsonSchemaValue: 

2080 """Generates a JSON schema that matches a UUID. 

2081 

2082 Args: 

2083 schema: The core schema. 

2084 

2085 Returns: 

2086 The generated JSON schema. 

2087 """ 

2088 return {'type': 'string', 'format': 'uuid'} 

2089 

2090 def definitions_schema(self, schema: core_schema.DefinitionsSchema) -> JsonSchemaValue: 

2091 """Generates a JSON schema that matches a schema that defines a JSON object with definitions. 

2092 

2093 Args: 

2094 schema: The core schema. 

2095 

2096 Returns: 

2097 The generated JSON schema. 

2098 """ 

2099 for definition in schema['definitions']: 

2100 try: 

2101 self.generate_inner(definition) 

2102 except PydanticInvalidForJsonSchema as e: # noqa: PERF203 

2103 core_ref: CoreRef = CoreRef(definition['ref']) # type: ignore 

2104 self._core_defs_invalid_for_json_schema[self.get_defs_ref((core_ref, self.mode))] = e 

2105 continue 

2106 return self.generate_inner(schema['schema']) 

2107 

2108 def definition_ref_schema(self, schema: core_schema.DefinitionReferenceSchema) -> JsonSchemaValue: 

2109 """Generates a JSON schema that matches a schema that references a definition. 

2110 

2111 Args: 

2112 schema: The core schema. 

2113 

2114 Returns: 

2115 The generated JSON schema. 

2116 """ 

2117 core_ref = CoreRef(schema['schema_ref']) 

2118 _, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 

2119 return ref_json_schema 

2120 

2121 def ser_schema( 

2122 self, schema: core_schema.SerSchema | core_schema.IncExSeqSerSchema | core_schema.IncExDictSerSchema 

2123 ) -> JsonSchemaValue | None: 

2124 """Generates a JSON schema that matches a schema that defines a serialized object. 

2125 

2126 Args: 

2127 schema: The core schema. 

2128 

2129 Returns: 

2130 The generated JSON schema. 

2131 """ 

2132 schema_type = schema['type'] 

2133 if schema_type == 'function-plain' or schema_type == 'function-wrap': 

2134 # PlainSerializerFunctionSerSchema or WrapSerializerFunctionSerSchema 

2135 return_schema = schema.get('return_schema') 

2136 if return_schema is not None: 

2137 return self.generate_inner(return_schema) 

2138 elif schema_type == 'format' or schema_type == 'to-string': 

2139 # FormatSerSchema or ToStringSerSchema 

2140 return self.str_schema(core_schema.str_schema()) 

2141 elif schema['type'] == 'model': 

2142 # ModelSerSchema 

2143 return self.generate_inner(schema['schema']) 

2144 return None 

2145 

2146 def complex_schema(self, schema: core_schema.ComplexSchema) -> JsonSchemaValue: 

2147 """Generates a JSON schema that matches a complex number. 

2148 

2149 JSON has no standard way to represent complex numbers. Complex number is not a numeric 

2150 type. Here we represent complex number as strings following the rule defined by Python. 

2151 For instance, '1+2j' is an accepted complex string. Details can be found in 

2152 [Python's `complex` documentation][complex]. 

2153 

2154 Args: 

2155 schema: The core schema. 

2156 

2157 Returns: 

2158 The generated JSON schema. 

2159 """ 

2160 return {'type': 'string'} 

2161 

2162 # ### Utility methods 

2163 

2164 def get_title_from_name(self, name: str) -> str: 

2165 """Retrieves a title from a name. 

2166 

2167 Args: 

2168 name: The name to retrieve a title from. 

2169 

2170 Returns: 

2171 The title. 

2172 """ 

2173 return name.title().replace('_', ' ').strip() 

2174 

2175 def field_title_should_be_set(self, schema: CoreSchemaOrField) -> bool: 

2176 """Returns true if a field with the given schema should have a title set based on the field name. 

2177 

2178 Intuitively, we want this to return true for schemas that wouldn't otherwise provide their own title 

2179 (e.g., int, float, str), and false for those that would (e.g., BaseModel subclasses). 

2180 

2181 Args: 

2182 schema: The schema to check. 

2183 

2184 Returns: 

2185 `True` if the field should have a title set, `False` otherwise. 

2186 """ 

2187 if _core_utils.is_core_schema_field(schema): 

2188 if schema['type'] == 'computed-field': 

2189 field_schema = schema['return_schema'] 

2190 else: 

2191 field_schema = schema['schema'] 

2192 return self.field_title_should_be_set(field_schema) 

2193 

2194 elif _core_utils.is_core_schema(schema): 

2195 if schema.get('ref'): # things with refs, such as models and enums, should not have titles set 

2196 return False 

2197 if schema['type'] in {'default', 'nullable', 'definitions'}: 

2198 return self.field_title_should_be_set(schema['schema']) # type: ignore[typeddict-item] 

2199 if _core_utils.is_function_with_inner_schema(schema): 

2200 return self.field_title_should_be_set(schema['schema']) 

2201 if schema['type'] == 'definition-ref': 

2202 # Referenced schemas should not have titles set for the same reason 

2203 # schemas with refs should not 

2204 return False 

2205 return True # anything else should have title set 

2206 

2207 else: 

2208 raise PydanticInvalidForJsonSchema(f'Unexpected schema type: schema={schema}') # pragma: no cover 

2209 

2210 def normalize_name(self, name: str) -> str: 

2211 """Normalizes a name to be used as a key in a dictionary. 

2212 

2213 Args: 

2214 name: The name to normalize. 

2215 

2216 Returns: 

2217 The normalized name. 

2218 """ 

2219 return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name).replace('.', '__') 

2220 

2221 def get_defs_ref(self, core_mode_ref: CoreModeRef) -> DefsRef: 

2222 """Override this method to change the way that definitions keys are generated from a core reference. 

2223 

2224 Args: 

2225 core_mode_ref: The core reference. 

2226 

2227 Returns: 

2228 The definitions key. 

2229 """ 

2230 # Split the core ref into "components"; generic origins and arguments are each separate components 

2231 core_ref, mode = core_mode_ref 

2232 components = re.split(r'([\][,])', core_ref) 

2233 # Remove IDs from each component 

2234 components = [x.rsplit(':', 1)[0] for x in components] 

2235 core_ref_no_id = ''.join(components) 

2236 # Remove everything before the last period from each "component" 

2237 components = [re.sub(r'(?:[^.[\]]+\.)+((?:[^.[\]]+))', r'\1', x) for x in components] 

2238 short_ref = ''.join(components) 

2239 

2240 mode_title = _MODE_TITLE_MAPPING[mode] 

2241 

2242 # It is important that the generated defs_ref values be such that at least one choice will not 

2243 # be generated for any other core_ref. Currently, this should be the case because we include 

2244 # the id of the source type in the core_ref 

2245 name = DefsRef(self.normalize_name(short_ref)) 

2246 name_mode = DefsRef(self.normalize_name(short_ref) + f'-{mode_title}') 

2247 module_qualname = DefsRef(self.normalize_name(core_ref_no_id)) 

2248 module_qualname_mode = DefsRef(f'{module_qualname}-{mode_title}') 

2249 module_qualname_id = DefsRef(self.normalize_name(core_ref)) 

2250 occurrence_index = self._collision_index.get(module_qualname_id) 

2251 if occurrence_index is None: 

2252 self._collision_counter[module_qualname] += 1 

2253 occurrence_index = self._collision_index[module_qualname_id] = self._collision_counter[module_qualname] 

2254 

2255 module_qualname_occurrence = DefsRef(f'{module_qualname}__{occurrence_index}') 

2256 module_qualname_occurrence_mode = DefsRef(f'{module_qualname_mode}__{occurrence_index}') 

2257 

2258 self._prioritized_defsref_choices[module_qualname_occurrence_mode] = [ 

2259 name, 

2260 name_mode, 

2261 module_qualname, 

2262 module_qualname_mode, 

2263 module_qualname_occurrence, 

2264 module_qualname_occurrence_mode, 

2265 ] 

2266 

2267 return module_qualname_occurrence_mode 

2268 

2269 def get_cache_defs_ref_schema(self, core_ref: CoreRef) -> tuple[DefsRef, JsonSchemaValue]: 

2270 """This method wraps the get_defs_ref method with some cache-lookup/population logic, 

2271 and returns both the produced defs_ref and the JSON schema that will refer to the right definition. 

2272 

2273 Args: 

2274 core_ref: The core reference to get the definitions reference for. 

2275 

2276 Returns: 

2277 A tuple of the definitions reference and the JSON schema that will refer to it. 

2278 """ 

2279 core_mode_ref = (core_ref, self.mode) 

2280 maybe_defs_ref = self.core_to_defs_refs.get(core_mode_ref) 

2281 if maybe_defs_ref is not None: 

2282 json_ref = self.core_to_json_refs[core_mode_ref] 

2283 return maybe_defs_ref, {'$ref': json_ref} 

2284 

2285 defs_ref = self.get_defs_ref(core_mode_ref) 

2286 

2287 # populate the ref translation mappings 

2288 self.core_to_defs_refs[core_mode_ref] = defs_ref 

2289 self.defs_to_core_refs[defs_ref] = core_mode_ref 

2290 

2291 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 

2292 self.core_to_json_refs[core_mode_ref] = json_ref 

2293 self.json_to_defs_refs[json_ref] = defs_ref 

2294 ref_json_schema = {'$ref': json_ref} 

2295 return defs_ref, ref_json_schema 

2296 

2297 def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 

2298 """Remove any sibling keys that are redundant with the referenced schema. 

2299 

2300 Args: 

2301 json_schema: The schema to remove redundant sibling keys from. 

2302 

2303 Returns: 

2304 The schema with redundant sibling keys removed. 

2305 """ 

2306 if '$ref' in json_schema: 

2307 # prevent modifications to the input; this copy may be safe to drop if there is significant overhead 

2308 json_schema = json_schema.copy() 

2309 

2310 referenced_json_schema = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) 

2311 if referenced_json_schema is None: 

2312 # This can happen when building schemas for models with not-yet-defined references. 

2313 # It may be a good idea to do a recursive pass at the end of the generation to remove 

2314 # any redundant override keys. 

2315 return json_schema 

2316 for k, v in list(json_schema.items()): 

2317 if k == '$ref': 

2318 continue 

2319 if k in referenced_json_schema and referenced_json_schema[k] == v: 

2320 del json_schema[k] # redundant key 

2321 

2322 return json_schema 

2323 

2324 def get_schema_from_definitions(self, json_ref: JsonRef) -> JsonSchemaValue | None: 

2325 try: 

2326 def_ref = self.json_to_defs_refs[json_ref] 

2327 if def_ref in self._core_defs_invalid_for_json_schema: 

2328 raise self._core_defs_invalid_for_json_schema[def_ref] 

2329 return self.definitions.get(def_ref, None) 

2330 except KeyError: 

2331 if json_ref.startswith(('http://', 'https://')): 

2332 return None 

2333 raise 

2334 

2335 def encode_default(self, dft: Any) -> Any: 

2336 """Encode a default value to a JSON-serializable value. 

2337 

2338 This is used to encode default values for fields in the generated JSON schema. 

2339 

2340 Args: 

2341 dft: The default value to encode. 

2342 

2343 Returns: 

2344 The encoded default value. 

2345 """ 

2346 from .type_adapter import TypeAdapter, _type_has_config 

2347 

2348 config = self._config 

2349 try: 

2350 default = ( 

2351 dft 

2352 if _type_has_config(type(dft)) 

2353 else TypeAdapter(type(dft), config=config.config_dict).dump_python( 

2354 dft, by_alias=self.by_alias, mode='json' 

2355 ) 

2356 ) 

2357 except PydanticSchemaGenerationError: 

2358 raise pydantic_core.PydanticSerializationError(f'Unable to encode default value {dft}') 

2359 

2360 return pydantic_core.to_jsonable_python( 

2361 default, timedelta_mode=config.ser_json_timedelta, bytes_mode=config.ser_json_bytes, by_alias=self.by_alias 

2362 ) 

2363 

2364 def update_with_validations( 

2365 self, json_schema: JsonSchemaValue, core_schema: CoreSchema, mapping: dict[str, str] 

2366 ) -> None: 

2367 """Update the json_schema with the corresponding validations specified in the core_schema, 

2368 using the provided mapping to translate keys in core_schema to the appropriate keys for a JSON schema. 

2369 

2370 Args: 

2371 json_schema: The JSON schema to update. 

2372 core_schema: The core schema to get the validations from. 

2373 mapping: A mapping from core_schema attribute names to the corresponding JSON schema attribute names. 

2374 """ 

2375 for core_key, json_schema_key in mapping.items(): 

2376 if core_key in core_schema: 

2377 json_schema[json_schema_key] = core_schema[core_key] 

2378 

2379 class ValidationsMapping: 

2380 """This class just contains mappings from core_schema attribute names to the corresponding 

2381 JSON schema attribute names. While I suspect it is unlikely to be necessary, you can in 

2382 principle override this class in a subclass of GenerateJsonSchema (by inheriting from 

2383 GenerateJsonSchema.ValidationsMapping) to change these mappings. 

2384 """ 

2385 

2386 numeric = { 

2387 'multiple_of': 'multipleOf', 

2388 'le': 'maximum', 

2389 'ge': 'minimum', 

2390 'lt': 'exclusiveMaximum', 

2391 'gt': 'exclusiveMinimum', 

2392 } 

2393 bytes = { 

2394 'min_length': 'minLength', 

2395 'max_length': 'maxLength', 

2396 } 

2397 string = { 

2398 'min_length': 'minLength', 

2399 'max_length': 'maxLength', 

2400 'pattern': 'pattern', 

2401 } 

2402 array = { 

2403 'min_length': 'minItems', 

2404 'max_length': 'maxItems', 

2405 } 

2406 object = { 

2407 'min_length': 'minProperties', 

2408 'max_length': 'maxProperties', 

2409 } 

2410 

2411 def get_flattened_anyof(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue: 

2412 members = [] 

2413 for schema in schemas: 

2414 if len(schema) == 1 and 'anyOf' in schema: 

2415 members.extend(schema['anyOf']) 

2416 else: 

2417 members.append(schema) 

2418 members = _deduplicate_schemas(members) 

2419 if len(members) == 1: 

2420 return members[0] 

2421 return {'anyOf': members} 

2422 

2423 def get_json_ref_counts(self, json_schema: JsonSchemaValue) -> dict[JsonRef, int]: 

2424 """Get all values corresponding to the key '$ref' anywhere in the json_schema.""" 

2425 json_refs: dict[JsonRef, int] = Counter() 

2426 

2427 def _add_json_refs(schema: Any) -> None: 

2428 if isinstance(schema, dict): 

2429 if '$ref' in schema: 

2430 json_ref = JsonRef(schema['$ref']) 

2431 if not isinstance(json_ref, str): 

2432 return # in this case, '$ref' might have been the name of a property 

2433 already_visited = json_ref in json_refs 

2434 json_refs[json_ref] += 1 

2435 if already_visited: 

2436 return # prevent recursion on a definition that was already visited 

2437 try: 

2438 defs_ref = self.json_to_defs_refs[json_ref] 

2439 if defs_ref in self._core_defs_invalid_for_json_schema: 

2440 raise self._core_defs_invalid_for_json_schema[defs_ref] 

2441 _add_json_refs(self.definitions[defs_ref]) 

2442 except KeyError: 

2443 if not json_ref.startswith(('http://', 'https://')): 

2444 raise 

2445 

2446 for k, v in schema.items(): 

2447 if k == 'examples' and isinstance(v, list): 

2448 # Skip examples that may contain arbitrary values and references 

2449 # (see the comment in `_get_all_json_refs` for more details). 

2450 continue 

2451 _add_json_refs(v) 

2452 elif isinstance(schema, list): 

2453 for v in schema: 

2454 _add_json_refs(v) 

2455 

2456 _add_json_refs(json_schema) 

2457 return json_refs 

2458 

2459 def handle_invalid_for_json_schema(self, schema: CoreSchemaOrField, error_info: str) -> JsonSchemaValue: 

2460 raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}') 

2461 

2462 def emit_warning(self, kind: JsonSchemaWarningKind, detail: str) -> None: 

2463 """This method simply emits PydanticJsonSchemaWarnings based on handling in the `warning_message` method.""" 

2464 message = self.render_warning_message(kind, detail) 

2465 if message is not None: 

2466 warnings.warn(message, PydanticJsonSchemaWarning) 

2467 

2468 def render_warning_message(self, kind: JsonSchemaWarningKind, detail: str) -> str | None: 

2469 """This method is responsible for ignoring warnings as desired, and for formatting the warning messages. 

2470 

2471 You can override the value of `ignored_warning_kinds` in a subclass of GenerateJsonSchema 

2472 to modify what warnings are generated. If you want more control, you can override this method; 

2473 just return None in situations where you don't want warnings to be emitted. 

2474 

2475 Args: 

2476 kind: The kind of warning to render. It can be one of the following: 

2477 

2478 - 'skipped-choice': A choice field was skipped because it had no valid choices. 

2479 - 'non-serializable-default': A default value was skipped because it was not JSON-serializable. 

2480 detail: A string with additional details about the warning. 

2481 

2482 Returns: 

2483 The formatted warning message, or `None` if no warning should be emitted. 

2484 """ 

2485 if kind in self.ignored_warning_kinds: 

2486 return None 

2487 return f'{detail} [{kind}]' 

2488 

2489 def _build_definitions_remapping(self) -> _DefinitionsRemapping: 

2490 defs_to_json: dict[DefsRef, JsonRef] = {} 

2491 for defs_refs in self._prioritized_defsref_choices.values(): 

2492 for defs_ref in defs_refs: 

2493 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 

2494 defs_to_json[defs_ref] = json_ref 

2495 

2496 return _DefinitionsRemapping.from_prioritized_choices( 

2497 self._prioritized_defsref_choices, defs_to_json, self.definitions 

2498 ) 

2499 

2500 def _garbage_collect_definitions(self, schema: JsonSchemaValue) -> None: 

2501 visited_defs_refs: set[DefsRef] = set() 

2502 unvisited_json_refs = _get_all_json_refs(schema) 

2503 while unvisited_json_refs: 

2504 next_json_ref = unvisited_json_refs.pop() 

2505 try: 

2506 next_defs_ref = self.json_to_defs_refs[next_json_ref] 

2507 if next_defs_ref in visited_defs_refs: 

2508 continue 

2509 visited_defs_refs.add(next_defs_ref) 

2510 unvisited_json_refs.update(_get_all_json_refs(self.definitions[next_defs_ref])) 

2511 except KeyError: 

2512 if not next_json_ref.startswith(('http://', 'https://')): 

2513 raise 

2514 

2515 self.definitions = {k: v for k, v in self.definitions.items() if k in visited_defs_refs} 

2516 

2517 

2518# ##### Start JSON Schema Generation Functions ##### 

2519 

2520 

2521def model_json_schema( 

2522 cls: type[BaseModel] | type[PydanticDataclass], 

2523 by_alias: bool = True, 

2524 ref_template: str = DEFAULT_REF_TEMPLATE, 

2525 union_format: Literal['any_of', 'primitive_type_array'] = 'any_of', 

2526 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, 

2527 mode: JsonSchemaMode = 'validation', 

2528) -> dict[str, Any]: 

2529 """Utility function to generate a JSON Schema for a model. 

2530 

2531 Args: 

2532 cls: The model class to generate a JSON Schema for. 

2533 by_alias: If `True` (the default), fields will be serialized according to their alias. 

2534 If `False`, fields will be serialized according to their attribute name. 

2535 ref_template: The template to use for generating JSON Schema references. 

2536 union_format: The format to use when combining schemas from unions together. Can be one of: 

2537 

2538 - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf) 

2539 keyword to combine schemas (the default). 

2540 - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type) 

2541 keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive 

2542 type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to 

2543 `any_of`. 

2544 schema_generator: The class to use for generating the JSON Schema. 

2545 mode: The mode to use for generating the JSON Schema. It can be one of the following: 

2546 

2547 - 'validation': Generate a JSON Schema for validating data. 

2548 - 'serialization': Generate a JSON Schema for serializing data. 

2549 

2550 Returns: 

2551 The generated JSON Schema. 

2552 """ 

2553 from .main import BaseModel 

2554 

2555 schema_generator_instance = schema_generator( 

2556 by_alias=by_alias, ref_template=ref_template, union_format=union_format 

2557 ) 

2558 

2559 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 

2560 cls.__pydantic_core_schema__.rebuild() 

2561 

2562 if cls is BaseModel: 

2563 raise AttributeError('model_json_schema() must be called on a subclass of BaseModel, not BaseModel itself.') 

2564 

2565 assert not isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it' 

2566 return schema_generator_instance.generate(cls.__pydantic_core_schema__, mode=mode) 

2567 

2568 

2569def models_json_schema( 

2570 models: Sequence[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode]], 

2571 *, 

2572 by_alias: bool = True, 

2573 title: str | None = None, 

2574 description: str | None = None, 

2575 ref_template: str = DEFAULT_REF_TEMPLATE, 

2576 union_format: Literal['any_of', 'primitive_type_array'] = 'any_of', 

2577 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, 

2578) -> tuple[dict[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]: 

2579 """Utility function to generate a JSON Schema for multiple models. 

2580 

2581 Args: 

2582 models: A sequence of tuples of the form (model, mode). 

2583 by_alias: Whether field aliases should be used as keys in the generated JSON Schema. 

2584 title: The title of the generated JSON Schema. 

2585 description: The description of the generated JSON Schema. 

2586 ref_template: The reference template to use for generating JSON Schema references. 

2587 union_format: The format to use when combining schemas from unions together. Can be one of: 

2588 

2589 - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf) 

2590 keyword to combine schemas (the default). 

2591 - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type) 

2592 keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive 

2593 type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to 

2594 `any_of`. 

2595 schema_generator: The schema generator to use for generating the JSON Schema. 

2596 

2597 Returns: 

2598 A tuple where: 

2599 - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and 

2600 whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have 

2601 JsonRef references to definitions that are defined in the second returned element.) 

2602 - The second element is a JSON schema containing all definitions referenced in the first returned 

2603 element, along with the optional title and description keys. 

2604 """ 

2605 for cls, _ in models: 

2606 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 

2607 cls.__pydantic_core_schema__.rebuild() 

2608 

2609 instance = schema_generator(by_alias=by_alias, ref_template=ref_template, union_format=union_format) 

2610 inputs: list[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode, CoreSchema]] = [ 

2611 (m, mode, m.__pydantic_core_schema__) for m, mode in models 

2612 ] 

2613 json_schemas_map, definitions = instance.generate_definitions(inputs) 

2614 

2615 json_schema: dict[str, Any] = {} 

2616 if definitions: 

2617 json_schema['$defs'] = definitions 

2618 if title: 

2619 json_schema['title'] = title 

2620 if description: 

2621 json_schema['description'] = description 

2622 

2623 return json_schemas_map, json_schema 

2624 

2625 

2626# ##### End JSON Schema Generation Functions ##### 

2627 

2628 

2629_HashableJsonValue: TypeAlias = Union[ 

2630 int, float, str, bool, None, tuple['_HashableJsonValue', ...], tuple[tuple[str, '_HashableJsonValue'], ...] 

2631] 

2632 

2633 

2634def _deduplicate_schemas(schemas: Iterable[JsonDict]) -> list[JsonDict]: 

2635 return list({_make_json_hashable(schema): schema for schema in schemas}.values()) 

2636 

2637 

2638def _make_json_hashable(value: JsonValue) -> _HashableJsonValue: 

2639 if isinstance(value, dict): 

2640 return tuple(sorted((k, _make_json_hashable(v)) for k, v in value.items())) 

2641 elif isinstance(value, list): 

2642 return tuple(_make_json_hashable(v) for v in value) 

2643 else: 

2644 return value 

2645 

2646 

2647@dataclasses.dataclass(**_internal_dataclass.slots_true) 

2648class WithJsonSchema: 

2649 """!!! abstract "Usage Documentation" 

2650 [`WithJsonSchema` Annotation](../concepts/json_schema.md#withjsonschema-annotation) 

2651 

2652 An annotation used to override the JSON Schema for a type. 

2653 

2654 This is useful when you want to set a JSON Schema for a type that don't produce any JSON Schemas by default 

2655 (e.g. [`Callable`][collections.abc.Callable]). 

2656 

2657 If `mode` is set this will only apply to that schema generation mode, allowing you to set different JSON Schemas for validation and serialization. 

2658 

2659 !!! note 

2660 If the `WithJsonSchema` annotation is coupled with the [`Field()`][pydantic.Field] function, the behavior overriding will vary depending on the location: 

2661 

2662 * If the [`Annotated`][typing.Annotated] metadata is specified at the "top-level" field, `Field()` metadata arguments 

2663 (excluding [constraints](../concepts/fields.md#field-constraints)) such as `title` and `description` will be applied on 

2664 top of the `WithJsonSchema`, no matter the order: 

2665 

2666 ```python 

2667 from typing import Annotated 

2668 

2669 from pydantic import BaseModel, Field, WithJsonSchema 

2670 

2671 class Model(BaseModel): 

2672 field: Annotated[ 

2673 int, 

2674 Field(title='My Field'), 

2675 WithJsonSchema({'type': 'integer', 'extra': 'data'}), 

2676 ] 

2677 

2678 Model.model_json_schema()['properties']['field'] 

2679 #> {'type': 'integer', 'extra': 'data', 'title': 'My Field'} 

2680 ``` 

2681 

2682 * If the [`Annotated`][typing.Annotated] metadata is specified on a specific inner type, `WithJsonSchema` will unconditionally 

2683 override the JSON Schema: 

2684 

2685 ```python 

2686 from typing import Annotated 

2687 

2688 from pydantic import BaseModel, Field, WithJsonSchema 

2689 

2690 class Model(BaseModel): 

2691 field: list[ 

2692 Annotated[ 

2693 int, 

2694 Field(title='My Field'), 

2695 WithJsonSchema({'type': 'integer', 'extra': 'data'}), 

2696 ] 

2697 ] 

2698 

2699 Model.model_json_schema()['properties']['field'] 

2700 #> {'items': {'extra': 'data', 'type': 'integer'}, 'title': 'Field', 'type': 'array'} 

2701 ``` 

2702 

2703 See also the documentation about [the annotated pattern](../concepts/fields.md#the-annotated-pattern). 

2704 """ 

2705 

2706 json_schema: JsonSchemaValue | None 

2707 mode: Literal['validation', 'serialization'] | None = None 

2708 

2709 def __get_pydantic_json_schema__( 

2710 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler 

2711 ) -> JsonSchemaValue: 

2712 if self.mode is not None and self.mode != handler.mode: 

2713 return handler(core_schema) 

2714 if self.json_schema is None: 

2715 # This exception is handled in pydantic.json_schema.GenerateJsonSchema._named_required_fields_schema 

2716 raise PydanticOmit 

2717 else: 

2718 return self.json_schema.copy() 

2719 

2720 def __hash__(self) -> int: 

2721 return hash(type(self.mode)) 

2722 

2723 

2724class Examples: 

2725 """Add examples to a JSON schema. 

2726 

2727 If the JSON Schema already contains examples, the provided examples 

2728 will be appended. 

2729 

2730 If `mode` is set this will only apply to that schema generation mode, 

2731 allowing you to add different examples for validation and serialization. 

2732 """ 

2733 

2734 @overload 

2735 @deprecated('Using a dict for `examples` is deprecated since v2.9 and will be removed in v3.0. Use a list instead.') 

2736 def __init__( 

2737 self, examples: dict[str, Any], mode: Literal['validation', 'serialization'] | None = None 

2738 ) -> None: ... 

2739 

2740 @overload 

2741 def __init__(self, examples: list[Any], mode: Literal['validation', 'serialization'] | None = None) -> None: ... 

2742 

2743 def __init__( 

2744 self, examples: dict[str, Any] | list[Any], mode: Literal['validation', 'serialization'] | None = None 

2745 ) -> None: 

2746 if isinstance(examples, dict): 

2747 warnings.warn( 

2748 'Using a dict for `examples` is deprecated, use a list instead.', 

2749 PydanticDeprecatedSince29, 

2750 stacklevel=2, 

2751 ) 

2752 self.examples = examples 

2753 self.mode = mode 

2754 

2755 def __get_pydantic_json_schema__( 

2756 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler 

2757 ) -> JsonSchemaValue: 

2758 mode = self.mode or handler.mode 

2759 json_schema = handler(core_schema) 

2760 if mode != handler.mode: 

2761 return json_schema 

2762 examples = json_schema.get('examples') 

2763 if examples is None: 

2764 json_schema['examples'] = to_jsonable_python(self.examples) 

2765 if isinstance(examples, dict): 

2766 if isinstance(self.examples, list): 

2767 warnings.warn( 

2768 'Updating existing JSON Schema examples of type dict with examples of type list. ' 

2769 'Only the existing examples values will be retained. Note that dict support for ' 

2770 'examples is deprecated and will be removed in v3.0.', 

2771 UserWarning, 

2772 ) 

2773 json_schema['examples'] = to_jsonable_python( 

2774 [ex for value in examples.values() for ex in value] + self.examples 

2775 ) 

2776 else: 

2777 json_schema['examples'] = to_jsonable_python({**examples, **self.examples}) 

2778 if isinstance(examples, list): 

2779 if isinstance(self.examples, list): 

2780 json_schema['examples'] = to_jsonable_python(examples + self.examples) 

2781 elif isinstance(self.examples, dict): 

2782 warnings.warn( 

2783 'Updating existing JSON Schema examples of type list with examples of type dict. ' 

2784 'Only the examples values will be retained. Note that dict support for ' 

2785 'examples is deprecated and will be removed in v3.0.', 

2786 UserWarning, 

2787 ) 

2788 json_schema['examples'] = to_jsonable_python( 

2789 examples + [ex for value in self.examples.values() for ex in value] 

2790 ) 

2791 

2792 return json_schema 

2793 

2794 def __hash__(self) -> int: 

2795 return hash(type(self.mode)) 

2796 

2797 

2798def _get_all_json_refs(item: Any) -> set[JsonRef]: 

2799 """Get all the definitions references from a JSON schema.""" 

2800 refs: set[JsonRef] = set() 

2801 stack = [item] 

2802 

2803 while stack: 

2804 current = stack.pop() 

2805 if isinstance(current, dict): 

2806 for key, value in current.items(): 

2807 if key == 'examples' and isinstance(value, list): 

2808 # Skip examples that may contain arbitrary values and references 

2809 # (e.g. `{"examples": [{"$ref": "..."}]}`). Note: checking for value 

2810 # of type list is necessary to avoid skipping valid portions of the schema, 

2811 # for instance when "examples" is used as a property key. A more robust solution 

2812 # could be found, but would require more advanced JSON Schema parsing logic. 

2813 continue 

2814 if key == '$ref' and isinstance(value, str): 

2815 refs.add(JsonRef(value)) 

2816 elif isinstance(value, dict): 

2817 stack.append(value) 

2818 elif isinstance(value, list): 

2819 stack.extend(value) 

2820 elif isinstance(current, list): 

2821 stack.extend(current) 

2822 

2823 return refs 

2824 

2825 

2826AnyType = TypeVar('AnyType') 

2827 

2828if TYPE_CHECKING: 

2829 SkipJsonSchema = Annotated[AnyType, ...] 

2830else: 

2831 

2832 @dataclasses.dataclass(**_internal_dataclass.slots_true) 

2833 class SkipJsonSchema: 

2834 """!!! abstract "Usage Documentation" 

2835 [`SkipJsonSchema` Annotation](../concepts/json_schema.md#skipjsonschema-annotation) 

2836 

2837 Add this as an annotation on a field to skip generating a JSON schema for that field. 

2838 

2839 Example: 

2840 ```python 

2841 from pprint import pprint 

2842 from typing import Union 

2843 

2844 from pydantic import BaseModel 

2845 from pydantic.json_schema import SkipJsonSchema 

2846 

2847 class Model(BaseModel): 

2848 a: Union[int, None] = None # (1)! 

2849 b: Union[int, SkipJsonSchema[None]] = None # (2)! 

2850 c: SkipJsonSchema[Union[int, None]] = None # (3)! 

2851 

2852 pprint(Model.model_json_schema()) 

2853 ''' 

2854 { 

2855 'properties': { 

2856 'a': { 

2857 'anyOf': [ 

2858 {'type': 'integer'}, 

2859 {'type': 'null'} 

2860 ], 

2861 'default': None, 

2862 'title': 'A' 

2863 }, 

2864 'b': { 

2865 'default': None, 

2866 'title': 'B', 

2867 'type': 'integer' 

2868 } 

2869 }, 

2870 'title': 'Model', 

2871 'type': 'object' 

2872 } 

2873 ''' 

2874 ``` 

2875 

2876 1. The integer and null types are both included in the schema for `a`. 

2877 2. The integer type is the only type included in the schema for `b`. 

2878 3. The entirety of the `c` field is omitted from the schema. 

2879 """ 

2880 

2881 def __class_getitem__(cls, item: AnyType) -> AnyType: 

2882 return Annotated[item, cls()] 

2883 

2884 def __get_pydantic_json_schema__( 

2885 self, core_schema: CoreSchema, handler: GetJsonSchemaHandler 

2886 ) -> JsonSchemaValue: 

2887 raise PydanticOmit 

2888 

2889 def __hash__(self) -> int: 

2890 return hash(type(self)) 

2891 

2892 

2893def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict: 

2894 if cls is not None: 

2895 try: 

2896 return _decorators.get_attribute_from_bases(cls, '__pydantic_config__') 

2897 except AttributeError: 

2898 pass 

2899 return {} 

2900 

2901 

2902def _get_ser_schema_for_default_value(schema: CoreSchema) -> core_schema.PlainSerializerFunctionSerSchema | None: 

2903 """Get a `'function-plain'` serialization schema that can be used to serialize a default value. 

2904 

2905 This takes into account having the serialization schema nested under validation schema(s). 

2906 """ 

2907 if ( 

2908 (ser_schema := schema.get('serialization')) 

2909 and ser_schema['type'] == 'function-plain' 

2910 and not ser_schema.get('info_arg') 

2911 ): 

2912 return ser_schema 

2913 if _core_utils.is_function_with_inner_schema(schema): 

2914 return _get_ser_schema_for_default_value(schema['schema'])