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

1156 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 

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

1641 json_schema.setdefault('title', config_title) 

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

1643 title = model_title_generator(cls) 

1644 if not isinstance(title, str): 

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

1646 json_schema.setdefault('title', title) 

1647 if 'title' not in json_schema: 

1648 json_schema['title'] = cls.__name__ 

1649 

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

1651 if cls is BaseModel: 

1652 docstring = None 

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

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

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

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

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

1658 # to happen. 

1659 doc = cls.__doc__ 

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

1661 else: 

1662 docstring = cls.__doc__ 

1663 

1664 if docstring: 

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

1666 

1667 extra = config.get('extra') 

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

1669 if extra == 'allow': 

1670 json_schema['additionalProperties'] = True 

1671 elif extra == 'forbid': 

1672 json_schema['additionalProperties'] = False 

1673 

1674 json_schema_extra = config.get('json_schema_extra') 

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

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

1677 if json_schema_extra and root_json_schema_extra: 

1678 raise ValueError( 

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

1680 ' field must not be set simultaneously' 

1681 ) 

1682 if root_json_schema_extra: 

1683 json_schema_extra = root_json_schema_extra 

1684 

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

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

1687 json_schema_extra = json_schema_extra.__get__(cls) 

1688 

1689 if isinstance(json_schema_extra, dict): 

1690 json_schema.update(json_schema_extra) 

1691 elif callable(json_schema_extra): 

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

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

1694 json_schema_extra(json_schema, cls) 

1695 else: 

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

1697 json_schema_extra(json_schema) 

1698 elif json_schema_extra is not None: 

1699 raise ValueError( 

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

1701 ) 

1702 

1703 if hasattr(cls, '__deprecated__'): 

1704 json_schema['deprecated'] = True 

1705 

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

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

1708 

1709 Args: 

1710 json_schema: The schema to resolve. 

1711 

1712 Returns: 

1713 The resolved schema. 

1714 

1715 Raises: 

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

1717 """ 

1718 while '$ref' in json_schema: 

1719 ref = json_schema['$ref'] 

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

1721 if schema_to_update is None: 

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

1723 json_schema = schema_to_update 

1724 return json_schema 

1725 

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

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

1728 

1729 Args: 

1730 schema: The core schema. 

1731 

1732 Returns: 

1733 The generated JSON schema. 

1734 """ 

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

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

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

1738 if self.field_is_present(field) 

1739 ] 

1740 if self.mode == 'serialization': 

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

1742 json_schema = self._named_required_fields_schema(named_required_fields) 

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

1744 if extras_schema is not None: 

1745 schema_to_update = self.resolve_ref_schema(json_schema) 

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

1747 return json_schema 

1748 

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

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

1751 

1752 Args: 

1753 field: The schema for the field itself. 

1754 

1755 Returns: 

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

1757 """ 

1758 if self.mode == 'serialization': 

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

1760 # override this method and return True 

1761 return not field.get('serialization_exclude') 

1762 elif self.mode == 'validation': 

1763 return True 

1764 else: 

1765 assert_never(self.mode) 

1766 

1767 def field_is_required( 

1768 self, 

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

1770 total: bool, 

1771 ) -> bool: 

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

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

1774 

1775 Args: 

1776 field: The schema for the field itself. 

1777 total: Only applies to `TypedDictField`s. 

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

1779 explicitly specify `required=False` are required. 

1780 

1781 Returns: 

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

1783 """ 

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

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

1786 else: 

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

1788 

1789 if self.mode == 'serialization': 

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

1791 if self._config.json_schema_serialization_defaults_required: 

1792 return not has_exclude_if 

1793 else: 

1794 return required and not has_exclude_if 

1795 else: 

1796 return required 

1797 

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

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

1800 

1801 Args: 

1802 schema: The core schema. 

1803 

1804 Returns: 

1805 The generated JSON schema. 

1806 """ 

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

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

1809 for field in schema['fields'] 

1810 if self.field_is_present(field) 

1811 ] 

1812 if self.mode == 'serialization': 

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

1814 return self._named_required_fields_schema(named_required_fields) 

1815 

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

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

1818 

1819 Args: 

1820 schema: The core schema. 

1821 

1822 Returns: 

1823 The generated JSON schema. 

1824 """ 

1825 

1826 cls = schema['cls'] 

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

1828 

1829 with self._config_wrapper_stack.push(config): 

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

1831 

1832 self._update_class_schema(json_schema, cls, config) 

1833 

1834 return json_schema 

1835 

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

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

1838 

1839 Args: 

1840 schema: The core schema. 

1841 

1842 Returns: 

1843 The generated JSON schema. 

1844 """ 

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

1846 

1847 arguments = schema['arguments_schema'] 

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

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

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

1851 var_args_schema = schema.get('var_args_schema') 

1852 var_kwargs_schema = schema.get('var_kwargs_schema') 

1853 

1854 if prefer_positional: 

1855 positional_possible = not kw_only_arguments and not var_kwargs_schema 

1856 if positional_possible: 

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

1858 

1859 keyword_possible = not p_only_arguments and not var_args_schema 

1860 if keyword_possible: 

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

1862 

1863 if not prefer_positional: 

1864 positional_possible = not kw_only_arguments and not var_kwargs_schema 

1865 if positional_possible: 

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

1867 

1868 raise PydanticInvalidForJsonSchema( 

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

1870 ) 

1871 

1872 def kw_arguments_schema( 

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

1874 ) -> JsonSchemaValue: 

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

1876 

1877 Args: 

1878 arguments: The core schema. 

1879 

1880 Returns: 

1881 The generated JSON schema. 

1882 """ 

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

1884 required: list[str] = [] 

1885 for argument in arguments: 

1886 name = self.get_argument_name(argument) 

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

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

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

1890 properties[name] = argument_schema 

1891 

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

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

1894 # the inner schema must be of type WithDefaultSchema. 

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

1896 required.append(name) 

1897 

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

1899 if required: 

1900 json_schema['required'] = required 

1901 

1902 if var_kwargs_schema: 

1903 additional_properties_schema = self.generate_inner(var_kwargs_schema) 

1904 if additional_properties_schema: 

1905 json_schema['additionalProperties'] = additional_properties_schema 

1906 else: 

1907 json_schema['additionalProperties'] = False 

1908 return json_schema 

1909 

1910 def p_arguments_schema( 

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

1912 ) -> JsonSchemaValue: 

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

1914 

1915 Args: 

1916 arguments: The core schema. 

1917 

1918 Returns: 

1919 The generated JSON schema. 

1920 """ 

1921 prefix_items: list[JsonSchemaValue] = [] 

1922 min_items = 0 

1923 

1924 for argument in arguments: 

1925 name = self.get_argument_name(argument) 

1926 

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

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

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

1930 prefix_items.append(argument_schema) 

1931 

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

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

1934 # the inner schema must be of type WithDefaultSchema. 

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

1936 min_items += 1 

1937 

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

1939 if prefix_items: 

1940 json_schema['prefixItems'] = prefix_items 

1941 if min_items: 

1942 json_schema['minItems'] = min_items 

1943 

1944 if var_args_schema: 

1945 items_schema = self.generate_inner(var_args_schema) 

1946 if items_schema: 

1947 json_schema['items'] = items_schema 

1948 else: 

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

1950 

1951 return json_schema 

1952 

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

1954 """Retrieves the name of an argument. 

1955 

1956 Args: 

1957 argument: The core schema. 

1958 

1959 Returns: 

1960 The name of the argument. 

1961 """ 

1962 name = argument['name'] 

1963 if self.by_alias: 

1964 alias = argument.get('alias') 

1965 if isinstance(alias, str): 

1966 name = alias 

1967 else: 

1968 pass # might want to do something else? 

1969 return name 

1970 

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

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

1973 

1974 Args: 

1975 schema: The core schema. 

1976 

1977 Returns: 

1978 The generated JSON schema. 

1979 """ 

1980 arguments = schema['arguments_schema'] 

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

1982 required: list[str] = [] 

1983 for argument in arguments: 

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

1985 name = self.get_argument_name(argument) 

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

1987 if mode == 'var_args': 

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

1989 elif mode == 'var_kwargs_uniform': 

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

1991 

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

1993 properties[name] = argument_schema 

1994 

1995 if ( 

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

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

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

1999 ): 

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

2001 # the inner schema must be of type WithDefaultSchema. 

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

2003 required.append(name) 

2004 

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

2006 if required: 

2007 json_schema['required'] = required 

2008 return json_schema 

2009 

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

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

2012 

2013 Args: 

2014 schema: The core schema. 

2015 

2016 Returns: 

2017 The generated JSON schema. 

2018 """ 

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

2020 

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

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

2023 

2024 Args: 

2025 schema: The core schema. 

2026 

2027 Returns: 

2028 The generated JSON schema. 

2029 """ 

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

2031 

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

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

2034 

2035 Args: 

2036 schema: The core schema. 

2037 

2038 Returns: 

2039 The generated JSON schema. 

2040 """ 

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

2042 content_json_schema = self.generate_inner(content_core_schema) 

2043 if self.mode == 'validation': 

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

2045 else: 

2046 # self.mode == 'serialization' 

2047 return content_json_schema 

2048 

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

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

2051 

2052 Args: 

2053 schema: The core schema. 

2054 

2055 Returns: 

2056 The generated JSON schema. 

2057 """ 

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

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

2060 return json_schema 

2061 

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

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

2064 

2065 Args: 

2066 schema: The core schema. 

2067 

2068 Returns: 

2069 The generated JSON schema. 

2070 """ 

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

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

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

2074 return json_schema 

2075 

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

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

2078 

2079 Args: 

2080 schema: The core schema. 

2081 

2082 Returns: 

2083 The generated JSON schema. 

2084 """ 

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

2086 

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

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

2089 

2090 Args: 

2091 schema: The core schema. 

2092 

2093 Returns: 

2094 The generated JSON schema. 

2095 """ 

2096 for definition in schema['definitions']: 

2097 try: 

2098 self.generate_inner(definition) 

2099 except PydanticInvalidForJsonSchema as e: # noqa: PERF203 

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

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

2102 continue 

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

2104 

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

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

2107 

2108 Args: 

2109 schema: The core schema. 

2110 

2111 Returns: 

2112 The generated JSON schema. 

2113 """ 

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

2115 _, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 

2116 return ref_json_schema 

2117 

2118 def ser_schema( 

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

2120 ) -> JsonSchemaValue | None: 

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

2122 

2123 Args: 

2124 schema: The core schema. 

2125 

2126 Returns: 

2127 The generated JSON schema. 

2128 """ 

2129 schema_type = schema['type'] 

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

2131 # PlainSerializerFunctionSerSchema or WrapSerializerFunctionSerSchema 

2132 return_schema = schema.get('return_schema') 

2133 if return_schema is not None: 

2134 return self.generate_inner(return_schema) 

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

2136 # FormatSerSchema or ToStringSerSchema 

2137 return self.str_schema(core_schema.str_schema()) 

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

2139 # ModelSerSchema 

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

2141 return None 

2142 

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

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

2145 

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

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

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

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

2150 

2151 Args: 

2152 schema: The core schema. 

2153 

2154 Returns: 

2155 The generated JSON schema. 

2156 """ 

2157 return {'type': 'string'} 

2158 

2159 # ### Utility methods 

2160 

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

2162 """Retrieves a title from a name. 

2163 

2164 Args: 

2165 name: The name to retrieve a title from. 

2166 

2167 Returns: 

2168 The title. 

2169 """ 

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

2171 

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

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

2174 

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

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

2177 

2178 Args: 

2179 schema: The schema to check. 

2180 

2181 Returns: 

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

2183 """ 

2184 if _core_utils.is_core_schema_field(schema): 

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

2186 field_schema = schema['return_schema'] 

2187 else: 

2188 field_schema = schema['schema'] 

2189 return self.field_title_should_be_set(field_schema) 

2190 

2191 elif _core_utils.is_core_schema(schema): 

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

2193 return False 

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

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

2196 if _core_utils.is_function_with_inner_schema(schema): 

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

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

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

2200 # schemas with refs should not 

2201 return False 

2202 return True # anything else should have title set 

2203 

2204 else: 

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

2206 

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

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

2209 

2210 Args: 

2211 name: The name to normalize. 

2212 

2213 Returns: 

2214 The normalized name. 

2215 """ 

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

2217 

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

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

2220 

2221 Args: 

2222 core_mode_ref: The core reference. 

2223 

2224 Returns: 

2225 The definitions key. 

2226 """ 

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

2228 core_ref, mode = core_mode_ref 

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

2230 # Remove IDs from each component 

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

2232 core_ref_no_id = ''.join(components) 

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

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

2235 short_ref = ''.join(components) 

2236 

2237 mode_title = _MODE_TITLE_MAPPING[mode] 

2238 

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

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

2241 # the id of the source type in the core_ref 

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

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

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

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

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

2247 occurrence_index = self._collision_index.get(module_qualname_id) 

2248 if occurrence_index is None: 

2249 self._collision_counter[module_qualname] += 1 

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

2251 

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

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

2254 

2255 self._prioritized_defsref_choices[module_qualname_occurrence_mode] = [ 

2256 name, 

2257 name_mode, 

2258 module_qualname, 

2259 module_qualname_mode, 

2260 module_qualname_occurrence, 

2261 module_qualname_occurrence_mode, 

2262 ] 

2263 

2264 return module_qualname_occurrence_mode 

2265 

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

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

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

2269 

2270 Args: 

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

2272 

2273 Returns: 

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

2275 """ 

2276 core_mode_ref = (core_ref, self.mode) 

2277 maybe_defs_ref = self.core_to_defs_refs.get(core_mode_ref) 

2278 if maybe_defs_ref is not None: 

2279 json_ref = self.core_to_json_refs[core_mode_ref] 

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

2281 

2282 defs_ref = self.get_defs_ref(core_mode_ref) 

2283 

2284 # populate the ref translation mappings 

2285 self.core_to_defs_refs[core_mode_ref] = defs_ref 

2286 self.defs_to_core_refs[defs_ref] = core_mode_ref 

2287 

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

2289 self.core_to_json_refs[core_mode_ref] = json_ref 

2290 self.json_to_defs_refs[json_ref] = defs_ref 

2291 ref_json_schema = {'$ref': json_ref} 

2292 return defs_ref, ref_json_schema 

2293 

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

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

2296 

2297 Args: 

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

2299 

2300 Returns: 

2301 The schema with redundant sibling keys removed. 

2302 """ 

2303 if '$ref' in json_schema: 

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

2305 json_schema = json_schema.copy() 

2306 

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

2308 if referenced_json_schema is None: 

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

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

2311 # any redundant override keys. 

2312 return json_schema 

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

2314 if k == '$ref': 

2315 continue 

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

2317 del json_schema[k] # redundant key 

2318 

2319 return json_schema 

2320 

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

2322 try: 

2323 def_ref = self.json_to_defs_refs[json_ref] 

2324 if def_ref in self._core_defs_invalid_for_json_schema: 

2325 raise self._core_defs_invalid_for_json_schema[def_ref] 

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

2327 except KeyError: 

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

2329 return None 

2330 raise 

2331 

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

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

2334 

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

2336 

2337 Args: 

2338 dft: The default value to encode. 

2339 

2340 Returns: 

2341 The encoded default value. 

2342 """ 

2343 from .type_adapter import TypeAdapter, _type_has_config 

2344 

2345 config = self._config 

2346 try: 

2347 default = ( 

2348 dft 

2349 if _type_has_config(type(dft)) 

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

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

2352 ) 

2353 ) 

2354 except PydanticSchemaGenerationError: 

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

2356 

2357 return pydantic_core.to_jsonable_python( 

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

2359 ) 

2360 

2361 def update_with_validations( 

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

2363 ) -> None: 

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

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

2366 

2367 Args: 

2368 json_schema: The JSON schema to update. 

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

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

2371 """ 

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

2373 if core_key in core_schema: 

2374 json_schema[json_schema_key] = core_schema[core_key] 

2375 

2376 class ValidationsMapping: 

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

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

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

2380 GenerateJsonSchema.ValidationsMapping) to change these mappings. 

2381 """ 

2382 

2383 numeric = { 

2384 'multiple_of': 'multipleOf', 

2385 'le': 'maximum', 

2386 'ge': 'minimum', 

2387 'lt': 'exclusiveMaximum', 

2388 'gt': 'exclusiveMinimum', 

2389 } 

2390 bytes = { 

2391 'min_length': 'minLength', 

2392 'max_length': 'maxLength', 

2393 } 

2394 string = { 

2395 'min_length': 'minLength', 

2396 'max_length': 'maxLength', 

2397 'pattern': 'pattern', 

2398 } 

2399 array = { 

2400 'min_length': 'minItems', 

2401 'max_length': 'maxItems', 

2402 } 

2403 object = { 

2404 'min_length': 'minProperties', 

2405 'max_length': 'maxProperties', 

2406 } 

2407 

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

2409 members = [] 

2410 for schema in schemas: 

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

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

2413 else: 

2414 members.append(schema) 

2415 members = _deduplicate_schemas(members) 

2416 if len(members) == 1: 

2417 return members[0] 

2418 return {'anyOf': members} 

2419 

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

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

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

2423 

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

2425 if isinstance(schema, dict): 

2426 if '$ref' in schema: 

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

2428 if not isinstance(json_ref, str): 

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

2430 already_visited = json_ref in json_refs 

2431 json_refs[json_ref] += 1 

2432 if already_visited: 

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

2434 try: 

2435 defs_ref = self.json_to_defs_refs[json_ref] 

2436 if defs_ref in self._core_defs_invalid_for_json_schema: 

2437 raise self._core_defs_invalid_for_json_schema[defs_ref] 

2438 _add_json_refs(self.definitions[defs_ref]) 

2439 except KeyError: 

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

2441 raise 

2442 

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

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

2445 # Skip examples that may contain arbitrary values and references 

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

2447 continue 

2448 _add_json_refs(v) 

2449 elif isinstance(schema, list): 

2450 for v in schema: 

2451 _add_json_refs(v) 

2452 

2453 _add_json_refs(json_schema) 

2454 return json_refs 

2455 

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

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

2458 

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

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

2461 message = self.render_warning_message(kind, detail) 

2462 if message is not None: 

2463 warnings.warn(message, PydanticJsonSchemaWarning) 

2464 

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

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

2467 

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

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

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

2471 

2472 Args: 

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

2474 

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

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

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

2478 

2479 Returns: 

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

2481 """ 

2482 if kind in self.ignored_warning_kinds: 

2483 return None 

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

2485 

2486 def _build_definitions_remapping(self) -> _DefinitionsRemapping: 

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

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

2489 for defs_ref in defs_refs: 

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

2491 defs_to_json[defs_ref] = json_ref 

2492 

2493 return _DefinitionsRemapping.from_prioritized_choices( 

2494 self._prioritized_defsref_choices, defs_to_json, self.definitions 

2495 ) 

2496 

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

2498 visited_defs_refs: set[DefsRef] = set() 

2499 unvisited_json_refs = _get_all_json_refs(schema) 

2500 while unvisited_json_refs: 

2501 next_json_ref = unvisited_json_refs.pop() 

2502 try: 

2503 next_defs_ref = self.json_to_defs_refs[next_json_ref] 

2504 if next_defs_ref in visited_defs_refs: 

2505 continue 

2506 visited_defs_refs.add(next_defs_ref) 

2507 unvisited_json_refs.update(_get_all_json_refs(self.definitions[next_defs_ref])) 

2508 except KeyError: 

2509 if not next_json_ref.startswith(('http://', 'https://')): 

2510 raise 

2511 

2512 self.definitions = {k: v for k, v in self.definitions.items() if k in visited_defs_refs} 

2513 

2514 

2515# ##### Start JSON Schema Generation Functions ##### 

2516 

2517 

2518def model_json_schema( 

2519 cls: type[BaseModel] | type[PydanticDataclass], 

2520 by_alias: bool = True, 

2521 ref_template: str = DEFAULT_REF_TEMPLATE, 

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

2523 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, 

2524 mode: JsonSchemaMode = 'validation', 

2525) -> dict[str, Any]: 

2526 """Utility function to generate a JSON Schema for a model. 

2527 

2528 Args: 

2529 cls: The model class to generate a JSON Schema for. 

2530 by_alias: If `True` (the default), fields will be serialized according to their alias. 

2531 If `False`, fields will be serialized according to their attribute name. 

2532 ref_template: The template to use for generating JSON Schema references. 

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

2534 

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

2536 keyword to combine schemas (the default). 

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

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

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

2540 `any_of`. 

2541 schema_generator: The class to use for generating the JSON Schema. 

2542 mode: The mode to use for generating the JSON Schema. It can be one of the following: 

2543 

2544 - 'validation': Generate a JSON Schema for validating data. 

2545 - 'serialization': Generate a JSON Schema for serializing data. 

2546 

2547 Returns: 

2548 The generated JSON Schema. 

2549 """ 

2550 from .main import BaseModel 

2551 

2552 schema_generator_instance = schema_generator( 

2553 by_alias=by_alias, ref_template=ref_template, union_format=union_format 

2554 ) 

2555 

2556 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 

2557 cls.__pydantic_core_schema__.rebuild() 

2558 

2559 if cls is BaseModel: 

2560 raise AttributeError('model_json_schema() must be called on a subclass of BaseModel, not BaseModel itself.') 

2561 

2562 assert not isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it' 

2563 return schema_generator_instance.generate(cls.__pydantic_core_schema__, mode=mode) 

2564 

2565 

2566def models_json_schema( 

2567 models: Sequence[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode]], 

2568 *, 

2569 by_alias: bool = True, 

2570 title: str | None = None, 

2571 description: str | None = None, 

2572 ref_template: str = DEFAULT_REF_TEMPLATE, 

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

2574 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, 

2575) -> tuple[dict[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]: 

2576 """Utility function to generate a JSON Schema for multiple models. 

2577 

2578 Args: 

2579 models: A sequence of tuples of the form (model, mode). 

2580 by_alias: Whether field aliases should be used as keys in the generated JSON Schema. 

2581 title: The title of the generated JSON Schema. 

2582 description: The description of the generated JSON Schema. 

2583 ref_template: The reference template to use for generating JSON Schema references. 

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

2585 

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

2587 keyword to combine schemas (the default). 

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

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

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

2591 `any_of`. 

2592 schema_generator: The schema generator to use for generating the JSON Schema. 

2593 

2594 Returns: 

2595 A tuple where: 

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

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

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

2599 - The second element is a JSON schema containing all definitions referenced in the first returned 

2600 element, along with the optional title and description keys. 

2601 """ 

2602 for cls, _ in models: 

2603 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 

2604 cls.__pydantic_core_schema__.rebuild() 

2605 

2606 instance = schema_generator(by_alias=by_alias, ref_template=ref_template, union_format=union_format) 

2607 inputs: list[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode, CoreSchema]] = [ 

2608 (m, mode, m.__pydantic_core_schema__) for m, mode in models 

2609 ] 

2610 json_schemas_map, definitions = instance.generate_definitions(inputs) 

2611 

2612 json_schema: dict[str, Any] = {} 

2613 if definitions: 

2614 json_schema['$defs'] = definitions 

2615 if title: 

2616 json_schema['title'] = title 

2617 if description: 

2618 json_schema['description'] = description 

2619 

2620 return json_schemas_map, json_schema 

2621 

2622 

2623# ##### End JSON Schema Generation Functions ##### 

2624 

2625 

2626_HashableJsonValue: TypeAlias = Union[ 

2627 int, float, str, bool, None, tuple['_HashableJsonValue', ...], tuple[tuple[str, '_HashableJsonValue'], ...] 

2628] 

2629 

2630 

2631def _deduplicate_schemas(schemas: Iterable[JsonDict]) -> list[JsonDict]: 

2632 return list({_make_json_hashable(schema): schema for schema in schemas}.values()) 

2633 

2634 

2635def _make_json_hashable(value: JsonValue) -> _HashableJsonValue: 

2636 if isinstance(value, dict): 

2637 return tuple(sorted((k, _make_json_hashable(v)) for k, v in value.items())) 

2638 elif isinstance(value, list): 

2639 return tuple(_make_json_hashable(v) for v in value) 

2640 else: 

2641 return value 

2642 

2643 

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

2645class WithJsonSchema: 

2646 """!!! abstract "Usage Documentation" 

2647 [`WithJsonSchema` Annotation](../concepts/json_schema.md#withjsonschema-annotation) 

2648 

2649 An annotation used to override the JSON Schema for a type. 

2650 

2651 This is useful when you want to set a JSON Schema for a type that don't produce any JSON Schemas by default 

2652 (e.g. [`Callable`][collections.abc.Callable]). 

2653 

2654 If `mode` is set this will only apply to that schema generation mode, allowing you to set different JSON Schemas for validation and serialization. 

2655 

2656 !!! note 

2657 If the `WithJsonSchema` annotation is coupled with the [`Field()`][pydantic.Field] function, the behavior overriding will vary depending on the location: 

2658 

2659 * If the [`Annotated`][typing.Annotated] metadata is specified at the "top-level" field, `Field()` metadata arguments 

2660 (excluding [constraints](../concepts/fields.md#field-constraints)) such as `title` and `description` will be applied on 

2661 top of the `WithJsonSchema`, no matter the order: 

2662 

2663 ```python 

2664 from typing import Annotated 

2665 

2666 from pydantic import BaseModel, Field, WithJsonSchema 

2667 

2668 class Model(BaseModel): 

2669 field: Annotated[ 

2670 int, 

2671 Field(title='My Field'), 

2672 WithJsonSchema({'type': 'integer', 'extra': 'data'}), 

2673 ] 

2674 

2675 Model.model_json_schema()['properties']['field'] 

2676 #> {'type': 'integer', 'extra': 'data', 'title': 'My Field'} 

2677 ``` 

2678 

2679 * If the [`Annotated`][typing.Annotated] metadata is specified on a specific inner type, `WithJsonSchema` will unconditionally 

2680 override the JSON Schema: 

2681 

2682 ```python 

2683 from typing import Annotated 

2684 

2685 from pydantic import BaseModel, Field, WithJsonSchema 

2686 

2687 class Model(BaseModel): 

2688 field: list[ 

2689 Annotated[ 

2690 int, 

2691 Field(title='My Field'), 

2692 WithJsonSchema({'type': 'integer', 'extra': 'data'}), 

2693 ] 

2694 ] 

2695 

2696 Model.model_json_schema()['properties']['field'] 

2697 #> {'items': {'extra': 'data', 'type': 'integer'}, 'title': 'Field', 'type': 'array'} 

2698 ``` 

2699 

2700 See also the documentation about [the annotated pattern](../concepts/fields.md#the-annotated-pattern). 

2701 """ 

2702 

2703 json_schema: JsonSchemaValue | None 

2704 mode: Literal['validation', 'serialization'] | None = None 

2705 

2706 def __get_pydantic_json_schema__( 

2707 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler 

2708 ) -> JsonSchemaValue: 

2709 if self.mode is not None and self.mode != handler.mode: 

2710 return handler(core_schema) 

2711 if self.json_schema is None: 

2712 # This exception is handled in pydantic.json_schema.GenerateJsonSchema._named_required_fields_schema 

2713 raise PydanticOmit 

2714 else: 

2715 return self.json_schema.copy() 

2716 

2717 def __hash__(self) -> int: 

2718 return hash(type(self.mode)) 

2719 

2720 

2721class Examples: 

2722 """Add examples to a JSON schema. 

2723 

2724 If the JSON Schema already contains examples, the provided examples 

2725 will be appended. 

2726 

2727 If `mode` is set this will only apply to that schema generation mode, 

2728 allowing you to add different examples for validation and serialization. 

2729 """ 

2730 

2731 @overload 

2732 @deprecated('Using a dict for `examples` is deprecated since v2.9 and will be removed in v3.0. Use a list instead.') 

2733 def __init__( 

2734 self, examples: dict[str, Any], mode: Literal['validation', 'serialization'] | None = None 

2735 ) -> None: ... 

2736 

2737 @overload 

2738 def __init__(self, examples: list[Any], mode: Literal['validation', 'serialization'] | None = None) -> None: ... 

2739 

2740 def __init__( 

2741 self, examples: dict[str, Any] | list[Any], mode: Literal['validation', 'serialization'] | None = None 

2742 ) -> None: 

2743 if isinstance(examples, dict): 

2744 warnings.warn( 

2745 'Using a dict for `examples` is deprecated, use a list instead.', 

2746 PydanticDeprecatedSince29, 

2747 stacklevel=2, 

2748 ) 

2749 self.examples = examples 

2750 self.mode = mode 

2751 

2752 def __get_pydantic_json_schema__( 

2753 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler 

2754 ) -> JsonSchemaValue: 

2755 mode = self.mode or handler.mode 

2756 json_schema = handler(core_schema) 

2757 if mode != handler.mode: 

2758 return json_schema 

2759 examples = json_schema.get('examples') 

2760 if examples is None: 

2761 json_schema['examples'] = to_jsonable_python(self.examples) 

2762 if isinstance(examples, dict): 

2763 if isinstance(self.examples, list): 

2764 warnings.warn( 

2765 'Updating existing JSON Schema examples of type dict with examples of type list. ' 

2766 'Only the existing examples values will be retained. Note that dict support for ' 

2767 'examples is deprecated and will be removed in v3.0.', 

2768 UserWarning, 

2769 ) 

2770 json_schema['examples'] = to_jsonable_python( 

2771 [ex for value in examples.values() for ex in value] + self.examples 

2772 ) 

2773 else: 

2774 json_schema['examples'] = to_jsonable_python({**examples, **self.examples}) 

2775 if isinstance(examples, list): 

2776 if isinstance(self.examples, list): 

2777 json_schema['examples'] = to_jsonable_python(examples + self.examples) 

2778 elif isinstance(self.examples, dict): 

2779 warnings.warn( 

2780 'Updating existing JSON Schema examples of type list with examples of type dict. ' 

2781 'Only the examples values will be retained. Note that dict support for ' 

2782 'examples is deprecated and will be removed in v3.0.', 

2783 UserWarning, 

2784 ) 

2785 json_schema['examples'] = to_jsonable_python( 

2786 examples + [ex for value in self.examples.values() for ex in value] 

2787 ) 

2788 

2789 return json_schema 

2790 

2791 def __hash__(self) -> int: 

2792 return hash(type(self.mode)) 

2793 

2794 

2795def _get_all_json_refs(item: Any) -> set[JsonRef]: 

2796 """Get all the definitions references from a JSON schema.""" 

2797 refs: set[JsonRef] = set() 

2798 stack = [item] 

2799 

2800 while stack: 

2801 current = stack.pop() 

2802 if isinstance(current, dict): 

2803 for key, value in current.items(): 

2804 if key == 'examples' and isinstance(value, list): 

2805 # Skip examples that may contain arbitrary values and references 

2806 # (e.g. `{"examples": [{"$ref": "..."}]}`). Note: checking for value 

2807 # of type list is necessary to avoid skipping valid portions of the schema, 

2808 # for instance when "examples" is used as a property key. A more robust solution 

2809 # could be found, but would require more advanced JSON Schema parsing logic. 

2810 continue 

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

2812 refs.add(JsonRef(value)) 

2813 elif isinstance(value, dict): 

2814 stack.append(value) 

2815 elif isinstance(value, list): 

2816 stack.extend(value) 

2817 elif isinstance(current, list): 

2818 stack.extend(current) 

2819 

2820 return refs 

2821 

2822 

2823AnyType = TypeVar('AnyType') 

2824 

2825if TYPE_CHECKING: 

2826 SkipJsonSchema = Annotated[AnyType, ...] 

2827else: 

2828 

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

2830 class SkipJsonSchema: 

2831 """!!! abstract "Usage Documentation" 

2832 [`SkipJsonSchema` Annotation](../concepts/json_schema.md#skipjsonschema-annotation) 

2833 

2834 Add this as an annotation on a field to skip generating a JSON schema for that field. 

2835 

2836 Example: 

2837 ```python 

2838 from pprint import pprint 

2839 from typing import Union 

2840 

2841 from pydantic import BaseModel 

2842 from pydantic.json_schema import SkipJsonSchema 

2843 

2844 class Model(BaseModel): 

2845 a: Union[int, None] = None # (1)! 

2846 b: Union[int, SkipJsonSchema[None]] = None # (2)! 

2847 c: SkipJsonSchema[Union[int, None]] = None # (3)! 

2848 

2849 pprint(Model.model_json_schema()) 

2850 ''' 

2851 { 

2852 'properties': { 

2853 'a': { 

2854 'anyOf': [ 

2855 {'type': 'integer'}, 

2856 {'type': 'null'} 

2857 ], 

2858 'default': None, 

2859 'title': 'A' 

2860 }, 

2861 'b': { 

2862 'default': None, 

2863 'title': 'B', 

2864 'type': 'integer' 

2865 } 

2866 }, 

2867 'title': 'Model', 

2868 'type': 'object' 

2869 } 

2870 ''' 

2871 ``` 

2872 

2873 1. The integer and null types are both included in the schema for `a`. 

2874 2. The integer type is the only type included in the schema for `b`. 

2875 3. The entirety of the `c` field is omitted from the schema. 

2876 """ 

2877 

2878 def __class_getitem__(cls, item: AnyType) -> AnyType: 

2879 return Annotated[item, cls()] 

2880 

2881 def __get_pydantic_json_schema__( 

2882 self, core_schema: CoreSchema, handler: GetJsonSchemaHandler 

2883 ) -> JsonSchemaValue: 

2884 raise PydanticOmit 

2885 

2886 def __hash__(self) -> int: 

2887 return hash(type(self)) 

2888 

2889 

2890def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict: 

2891 if cls is not None: 

2892 try: 

2893 return _decorators.get_attribute_from_bases(cls, '__pydantic_config__') 

2894 except AttributeError: 

2895 pass 

2896 return {} 

2897 

2898 

2899def _get_ser_schema_for_default_value(schema: CoreSchema) -> core_schema.PlainSerializerFunctionSerSchema | None: 

2900 """Get a `'function-plain'` serialization schema that can be used to serialize a default value. 

2901 

2902 This takes into account having the serialization schema nested under validation schema(s). 

2903 """ 

2904 if ( 

2905 (ser_schema := schema.get('serialization')) 

2906 and ser_schema['type'] == 'function-plain' 

2907 and not ser_schema.get('info_arg') 

2908 ): 

2909 return ser_schema 

2910 if _core_utils.is_function_with_inner_schema(schema): 

2911 return _get_ser_schema_for_default_value(schema['schema'])