Coverage for pydantic/json_schema.py: 94.32%
1065 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-13 19:35 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-13 19:35 +0000
1"""!!! abstract "Usage Documentation"
2 [JSON Schema](../concepts/json_schema.md)
4The `json_schema` module contains classes and functions to allow the way [JSON Schema](https://json-schema.org/)
5is generated to be customized.
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"""
12from __future__ import annotations as _annotations 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
14import dataclasses 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
15import inspect 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
16import math 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
17import os 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
18import re 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
19import warnings 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
20from collections import Counter, defaultdict 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
21from collections.abc import Hashable, Iterable, Sequence 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
22from copy import deepcopy 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
23from enum import Enum 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
24from re import Pattern 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
25from typing import ( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
26 TYPE_CHECKING,
27 Annotated,
28 Any,
29 Callable,
30 Literal,
31 NewType,
32 TypeVar,
33 Union,
34 cast,
35 overload,
36)
38import pydantic_core 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
39from pydantic_core import CoreSchema, PydanticOmit, core_schema, to_jsonable_python 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
40from pydantic_core.core_schema import ComputedField 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
41from typing_extensions import TypeAlias, assert_never, deprecated, final 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
43from pydantic.warnings import PydanticDeprecatedSince26, PydanticDeprecatedSince29 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
45from ._internal import ( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
46 _config,
47 _core_metadata,
48 _core_utils,
49 _decorators,
50 _internal_dataclass,
51 _mock_val_ser,
52 _schema_generation_shared,
53 _typing_extra,
54)
55from .annotated_handlers import GetJsonSchemaHandler 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
56from .config import JsonDict, JsonValue 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
57from .errors import PydanticInvalidForJsonSchema, PydanticSchemaGenerationError, PydanticUserError 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
59if TYPE_CHECKING: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
60 from . import ConfigDict
61 from ._internal._core_utils import CoreSchemaField, CoreSchemaOrField
62 from ._internal._dataclasses import PydanticDataclass
63 from ._internal._schema_generation_shared import GetJsonSchemaFunction
64 from .main import BaseModel
67CoreSchemaOrFieldType = Literal[core_schema.CoreSchemaType, core_schema.CoreSchemaFieldType] 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
68""" 1akgwblcmhnoxypqrsKLGHIJdtizeufv
69A type alias for defined schema types that represents a union of
70`core_schema.CoreSchemaType` and
71`core_schema.CoreSchemaFieldType`.
72"""
74JsonSchemaValue = dict[str, Any] 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
75""" 1akgwblcmhnoxypqrsKLGHIJdtizeufv
76A type alias for a JSON schema value. This is a dictionary of string keys to arbitrary JSON values.
77"""
79JsonSchemaMode = Literal['validation', 'serialization'] 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
80""" 1akgwblcmhnoxypqrsKLGHIJdtizeufv
81A type alias that represents the mode of a JSON schema; either 'validation' or 'serialization'.
83For some types, the inputs to validation differ from the outputs of serialization. For example,
84computed fields will only be present when serializing, and should not be provided when
85validating. This flag provides a way to indicate whether you want the JSON schema required
86for validation inputs, or that will be matched by serialization outputs.
87"""
89_MODE_TITLE_MAPPING: dict[JsonSchemaMode, str] = {'validation': 'Input', 'serialization': 'Output'} 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
92JsonSchemaWarningKind = Literal['skipped-choice', 'non-serializable-default', 'skipped-discriminator'] 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
93""" 1akgwblcmhnoxypqrsKLGHIJdtizeufv
94A type alias representing the kinds of warnings that can be emitted during JSON schema generation.
96See [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message]
97for more details.
98"""
101class PydanticJsonSchemaWarning(UserWarning): 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
102 """This class is used to emit warnings produced during JSON schema generation.
103 See the [`GenerateJsonSchema.emit_warning`][pydantic.json_schema.GenerateJsonSchema.emit_warning] and
104 [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message]
105 methods for more details; these can be overridden to control warning behavior.
106 """
109# ##### JSON Schema Generation #####
110DEFAULT_REF_TEMPLATE = '#/$defs/{model}' 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
111"""The default format string used to generate reference names.""" 1akgwblcmhnoxypqrsKLGHIJdtizeufv
113# There are three types of references relevant to building JSON schemas:
114# 1. core_schema "ref" values; these are not exposed as part of the JSON schema
115# * these might look like the fully qualified path of a model, its id, or something similar
116CoreRef = NewType('CoreRef', str) 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
117# 2. keys of the "definitions" object that will eventually go into the JSON schema
118# * by default, these look like "MyModel", though may change in the presence of collisions
119# * eventually, we may want to make it easier to modify the way these names are generated
120DefsRef = NewType('DefsRef', str) 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
121# 3. the values corresponding to the "$ref" key in the schema
122# * By default, these look like "#/$defs/MyModel", as in {"$ref": "#/$defs/MyModel"}
123JsonRef = NewType('JsonRef', str) 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
125CoreModeRef = tuple[CoreRef, JsonSchemaMode] 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
126JsonSchemaKeyT = TypeVar('JsonSchemaKeyT', bound=Hashable) 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
129@dataclasses.dataclass(**_internal_dataclass.slots_true) 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
130class _DefinitionsRemapping: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
131 defs_remapping: dict[DefsRef, DefsRef] 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
132 json_remapping: dict[JsonRef, JsonRef] 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
134 @staticmethod 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
135 def from_prioritized_choices( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
136 prioritized_choices: dict[DefsRef, list[DefsRef]],
137 defs_to_json: dict[DefsRef, JsonRef],
138 definitions: dict[DefsRef, JsonSchemaValue],
139 ) -> _DefinitionsRemapping:
140 """
141 This function should produce a remapping that replaces complex DefsRef with the simpler ones from the
142 prioritized_choices such that applying the name remapping would result in an equivalent JSON schema.
143 """
144 # We need to iteratively simplify the definitions until we reach a fixed point.
145 # The reason for this is that outer definitions may reference inner definitions that get simplified
146 # into an equivalent reference, and the outer definitions won't be equivalent until we've simplified
147 # the inner definitions.
148 copied_definitions = deepcopy(definitions) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
149 definitions_schema = {'$defs': copied_definitions} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
150 for _iter in range(100): # prevent an infinite loop in the case of a bug, 100 iterations should be enough 150 ↛ 179line 150 didn't jump to line 179 because the loop on line 150 didn't complete1jBakgwblcmChDEnoxypqrsAFdtizeufv
151 # For every possible remapped DefsRef, collect all schemas that that DefsRef might be used for:
152 schemas_for_alternatives: dict[DefsRef, list[JsonSchemaValue]] = defaultdict(list) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
153 for defs_ref in copied_definitions: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
154 alternatives = prioritized_choices[defs_ref] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
155 for alternative in alternatives: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
156 schemas_for_alternatives[alternative].append(copied_definitions[defs_ref]) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
158 # Deduplicate the schemas for each alternative; the idea is that we only want to remap to a new DefsRef
159 # if it introduces no ambiguity, i.e., there is only one distinct schema for that DefsRef.
160 for defs_ref in schemas_for_alternatives: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
161 schemas_for_alternatives[defs_ref] = _deduplicate_schemas(schemas_for_alternatives[defs_ref]) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
163 # Build the remapping
164 defs_remapping: dict[DefsRef, DefsRef] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
165 json_remapping: dict[JsonRef, JsonRef] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
166 for original_defs_ref in definitions: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
167 alternatives = prioritized_choices[original_defs_ref] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
168 # Pick the first alternative that has only one schema, since that means there is no collision
169 remapped_defs_ref = next(x for x in alternatives if len(schemas_for_alternatives[x]) == 1) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
170 defs_remapping[original_defs_ref] = remapped_defs_ref 1jBakgwblcmChDEnoxypqrsAFdtizeufv
171 json_remapping[defs_to_json[original_defs_ref]] = defs_to_json[remapped_defs_ref] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
172 remapping = _DefinitionsRemapping(defs_remapping, json_remapping) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
173 new_definitions_schema = remapping.remap_json_schema({'$defs': copied_definitions}) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
174 if definitions_schema == new_definitions_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
175 # We've reached the fixed point
176 return remapping 1jBakgwblcmChDEnoxypqrsAFdtizeufv
177 definitions_schema = new_definitions_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
179 raise PydanticInvalidForJsonSchema('Failed to simplify the JSON schema definitions')
181 def remap_defs_ref(self, ref: DefsRef) -> DefsRef: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
182 return self.defs_remapping.get(ref, ref) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
184 def remap_json_ref(self, ref: JsonRef) -> JsonRef: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
185 return self.json_remapping.get(ref, ref) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
187 def remap_json_schema(self, schema: Any) -> Any: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
188 """
189 Recursively update the JSON schema replacing all $refs
190 """
191 if isinstance(schema, str): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
192 # Note: this may not really be a JsonRef; we rely on having no collisions between JsonRefs and other strings
193 return self.remap_json_ref(JsonRef(schema)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
194 elif isinstance(schema, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
195 return [self.remap_json_schema(item) for item in schema] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
196 elif isinstance(schema, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
197 for key, value in schema.items(): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
198 if key == '$ref' and isinstance(value, str): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
199 schema['$ref'] = self.remap_json_ref(JsonRef(value)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
200 elif key == '$defs': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
201 schema['$defs'] = { 1jBakgwblcmChDEnoxypqrsAFdtizeufv
202 self.remap_defs_ref(DefsRef(key)): self.remap_json_schema(value)
203 for key, value in schema['$defs'].items()
204 }
205 else:
206 schema[key] = self.remap_json_schema(value) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
207 return schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
210class GenerateJsonSchema: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
211 """!!! abstract "Usage Documentation"
212 [Customizing the JSON Schema Generation Process](../concepts/json_schema.md#customizing-the-json-schema-generation-process)
214 A class for generating JSON schemas.
216 This class generates JSON schemas based on configured parameters. The default schema dialect
217 is [https://json-schema.org/draft/2020-12/schema](https://json-schema.org/draft/2020-12/schema).
218 The class uses `by_alias` to configure how fields with
219 multiple names are handled and `ref_template` to format reference names.
221 Attributes:
222 schema_dialect: The JSON schema dialect used to generate the schema. See
223 [Declaring a Dialect](https://json-schema.org/understanding-json-schema/reference/schema.html#id4)
224 in the JSON Schema documentation for more information about dialects.
225 ignored_warning_kinds: Warnings to ignore when generating the schema. `self.render_warning_message` will
226 do nothing if its argument `kind` is in `ignored_warning_kinds`;
227 this value can be modified on subclasses to easily control which warnings are emitted.
228 by_alias: Whether to use field aliases when generating the schema.
229 ref_template: The format string used when generating reference names.
230 core_to_json_refs: A mapping of core refs to JSON refs.
231 core_to_defs_refs: A mapping of core refs to definition refs.
232 defs_to_core_refs: A mapping of definition refs to core refs.
233 json_to_defs_refs: A mapping of JSON refs to definition refs.
234 definitions: Definitions in the schema.
236 Args:
237 by_alias: Whether to use field aliases in the generated schemas.
238 ref_template: The format string to use when generating reference names.
240 Raises:
241 JsonSchemaError: If the instance of the class is inadvertently reused after generating a schema.
242 """
244 schema_dialect = 'https://json-schema.org/draft/2020-12/schema' 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
246 # `self.render_warning_message` will do nothing if its argument `kind` is in `ignored_warning_kinds`;
247 # this value can be modified on subclasses to easily control which warnings are emitted
248 ignored_warning_kinds: set[JsonSchemaWarningKind] = {'skipped-choice'} 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
250 def __init__(self, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE): 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
251 self.by_alias = by_alias 1jBakgwblcmChDEnoxypqrsAFdtizeufv
252 self.ref_template = ref_template 1jBakgwblcmChDEnoxypqrsAFdtizeufv
254 self.core_to_json_refs: dict[CoreModeRef, JsonRef] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
255 self.core_to_defs_refs: dict[CoreModeRef, DefsRef] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
256 self.defs_to_core_refs: dict[DefsRef, CoreModeRef] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
257 self.json_to_defs_refs: dict[JsonRef, DefsRef] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
259 self.definitions: dict[DefsRef, JsonSchemaValue] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
260 self._config_wrapper_stack = _config.ConfigWrapperStack(_config.ConfigWrapper({})) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
262 self._mode: JsonSchemaMode = 'validation' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
264 # The following includes a mapping of a fully-unique defs ref choice to a list of preferred
265 # alternatives, which are generally simpler, such as only including the class name.
266 # At the end of schema generation, we use these to produce a JSON schema with more human-readable
267 # definitions, which would also work better in a generated OpenAPI client, etc.
268 self._prioritized_defsref_choices: dict[DefsRef, list[DefsRef]] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
269 self._collision_counter: dict[str, int] = defaultdict(int) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
270 self._collision_index: dict[str, int] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
272 self._schema_type_to_method = self.build_schema_type_to_method() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
274 # When we encounter definitions we need to try to build them immediately
275 # so that they are available schemas that reference them
276 # But it's possible that CoreSchema was never going to be used
277 # (e.g. because the CoreSchema that references short circuits is JSON schema generation without needing
278 # the reference) so instead of failing altogether if we can't build a definition we
279 # store the error raised and re-throw it if we end up needing that def
280 self._core_defs_invalid_for_json_schema: dict[DefsRef, PydanticInvalidForJsonSchema] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
282 # This changes to True after generating a schema, to prevent issues caused by accidental reuse
283 # of a single instance of a schema generator
284 self._used = False 1jBakgwblcmChDEnoxypqrsAFdtizeufv
286 @property 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
287 def _config(self) -> _config.ConfigWrapper: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
288 return self._config_wrapper_stack.tail 1jBakgwblcmChDEnoxypqrsAFdtizeufv
290 @property 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
291 def mode(self) -> JsonSchemaMode: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
292 if self._config.json_schema_mode_override is not None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
293 return self._config.json_schema_mode_override 1jBakgwblcmChDEnoxypqrsAFdtizeufv
294 else:
295 return self._mode 1jBakgwblcmChDEnoxypqrsAFdtizeufv
297 def build_schema_type_to_method( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
298 self,
299 ) -> dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]]:
300 """Builds a dictionary mapping fields to methods for generating JSON schemas.
302 Returns:
303 A dictionary containing the mapping of `CoreSchemaOrFieldType` to a handler method.
305 Raises:
306 TypeError: If no method has been defined for generating a JSON schema for a given pydantic core schema type.
307 """
308 mapping: dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
309 core_schema_types: list[CoreSchemaOrFieldType] = _typing_extra.literal_values( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
310 CoreSchemaOrFieldType # type: ignore
311 )
312 for key in core_schema_types: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
313 method_name = f'{key.replace("-", "_")}_schema' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
314 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
315 mapping[key] = getattr(self, method_name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
316 except AttributeError as e: # pragma: no cover
317 if os.getenv('PYDANTIC_PRIVATE_ALLOW_UNHANDLED_SCHEMA_TYPES'):
318 continue
319 raise TypeError(
320 f'No method for generating JsonSchema for core_schema.type={key!r} '
321 f'(expected: {type(self).__name__}.{method_name})'
322 ) from e
323 return mapping 1jBakgwblcmChDEnoxypqrsAFdtizeufv
325 def generate_definitions( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
326 self, inputs: Sequence[tuple[JsonSchemaKeyT, JsonSchemaMode, core_schema.CoreSchema]]
327 ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], dict[DefsRef, JsonSchemaValue]]:
328 """Generates JSON schema definitions from a list of core schemas, pairing the generated definitions with a
329 mapping that links the input keys to the definition references.
331 Args:
332 inputs: A sequence of tuples, where:
334 - The first element is a JSON schema key type.
335 - The second element is the JSON mode: either 'validation' or 'serialization'.
336 - The third element is a core schema.
338 Returns:
339 A tuple where:
341 - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
342 whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
343 JsonRef references to definitions that are defined in the second returned element.)
344 - The second element is a dictionary whose keys are definition references for the JSON schemas
345 from the first returned element, and whose values are the actual JSON schema definitions.
347 Raises:
348 PydanticUserError: Raised if the JSON schema generator has already been used to generate a JSON schema.
349 """
350 if self._used: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
351 raise PydanticUserError( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
352 'This JSON schema generator has already been used to generate a JSON schema. '
353 f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.',
354 code='json-schema-already-used',
355 )
357 for _, mode, schema in inputs: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
358 self._mode = mode 1jBakgwblcmChDEnoxypqrsAFdtizeufv
359 self.generate_inner(schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
361 definitions_remapping = self._build_definitions_remapping() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
363 json_schemas_map: dict[tuple[JsonSchemaKeyT, JsonSchemaMode], DefsRef] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
364 for key, mode, schema in inputs: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
365 self._mode = mode 1jBakgwblcmChDEnoxypqrsAFdtizeufv
366 json_schema = self.generate_inner(schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
367 json_schemas_map[(key, mode)] = definitions_remapping.remap_json_schema(json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
369 json_schema = {'$defs': self.definitions} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
370 json_schema = definitions_remapping.remap_json_schema(json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
371 self._used = True 1jBakgwblcmChDEnoxypqrsAFdtizeufv
372 return json_schemas_map, self.sort(json_schema['$defs']) # type: ignore 1jBakgwblcmChDEnoxypqrsAFdtizeufv
374 def generate(self, schema: CoreSchema, mode: JsonSchemaMode = 'validation') -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
375 """Generates a JSON schema for a specified schema in a specified mode.
377 Args:
378 schema: A Pydantic model.
379 mode: The mode in which to generate the schema. Defaults to 'validation'.
381 Returns:
382 A JSON schema representing the specified schema.
384 Raises:
385 PydanticUserError: If the JSON schema generator has already been used to generate a JSON schema.
386 """
387 self._mode = mode 1jBakgwblcmChDEnoxypqrsAFdtizeufv
388 if self._used: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
389 raise PydanticUserError( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
390 'This JSON schema generator has already been used to generate a JSON schema. '
391 f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.',
392 code='json-schema-already-used',
393 )
395 json_schema: JsonSchemaValue = self.generate_inner(schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
396 json_ref_counts = self.get_json_ref_counts(json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
398 ref = cast(JsonRef, json_schema.get('$ref')) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
399 while ref is not None: # may need to unpack multiple levels 1jBakgwblcmChDEnoxypqrsAFdtizeufv
400 ref_json_schema = self.get_schema_from_definitions(ref) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
401 if json_ref_counts[ref] == 1 and ref_json_schema is not None and len(json_schema) == 1: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
402 # "Unpack" the ref since this is the only reference and there are no sibling keys
403 json_schema = ref_json_schema.copy() # copy to prevent recursive dict reference 1jBakgwblcmChDEnoxypqrsAFdtizeufv
404 json_ref_counts[ref] -= 1 1jBakgwblcmChDEnoxypqrsAFdtizeufv
405 ref = cast(JsonRef, json_schema.get('$ref')) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
406 ref = None 1jBakgwblcmChDEnoxypqrsAFdtizeufv
408 self._garbage_collect_definitions(json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
409 definitions_remapping = self._build_definitions_remapping() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
411 if self.definitions: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
412 json_schema['$defs'] = self.definitions 1jBakgwblcmChDEnoxypqrsAFdtizeufv
414 json_schema = definitions_remapping.remap_json_schema(json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
416 # For now, we will not set the $schema key. However, if desired, this can be easily added by overriding
417 # this method and adding the following line after a call to super().generate(schema):
418 # json_schema['$schema'] = self.schema_dialect
420 self._used = True 1jBakgwblcmChDEnoxypqrsAFdtizeufv
421 return self.sort(json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
423 def generate_inner(self, schema: CoreSchemaOrField) -> JsonSchemaValue: # noqa: C901 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
424 """Generates a JSON schema for a given core schema.
426 Args:
427 schema: The given core schema.
429 Returns:
430 The generated JSON schema.
432 TODO: the nested function definitions here seem like bad practice, I'd like to unpack these
433 in a future PR. It'd be great if we could shorten the call stack a bit for JSON schema generation,
434 and I think there's potential for that here.
435 """
436 # If a schema with the same CoreRef has been handled, just return a reference to it
437 # Note that this assumes that it will _never_ be the case that the same CoreRef is used
438 # on types that should have different JSON schemas
439 if 'ref' in schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
440 core_ref = CoreRef(schema['ref']) # type: ignore[typeddict-item] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
441 core_mode_ref = (core_ref, self.mode) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
442 if core_mode_ref in self.core_to_defs_refs and self.core_to_defs_refs[core_mode_ref] in self.definitions: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
443 return {'$ref': self.core_to_json_refs[core_mode_ref]} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
445 def populate_defs(core_schema: CoreSchema, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
446 if 'ref' in core_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
447 core_ref = CoreRef(core_schema['ref']) # type: ignore[typeddict-item] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
448 defs_ref, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
449 json_ref = JsonRef(ref_json_schema['$ref']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
450 # Replace the schema if it's not a reference to itself
451 # What we want to avoid is having the def be just a ref to itself
452 # which is what would happen if we blindly assigned any
453 if json_schema.get('$ref', None) != json_ref: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
454 self.definitions[defs_ref] = json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
455 self._core_defs_invalid_for_json_schema.pop(defs_ref, None) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
456 json_schema = ref_json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
457 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
459 def handler_func(schema_or_field: CoreSchemaOrField) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
460 """Generate a JSON schema based on the input schema.
462 Args:
463 schema_or_field: The core schema to generate a JSON schema from.
465 Returns:
466 The generated JSON schema.
468 Raises:
469 TypeError: If an unexpected schema type is encountered.
470 """
471 # Generate the core-schema-type-specific bits of the schema generation:
472 json_schema: JsonSchemaValue | None = None 1jBakgwblcmChDEnoxypqrsAFdtizeufv
473 if self.mode == 'serialization' and 'serialization' in schema_or_field: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
474 # In this case, we skip the JSON Schema generation of the schema
475 # and use the `'serialization'` schema instead (canonical example:
476 # `Annotated[int, PlainSerializer(str)]`).
477 ser_schema = schema_or_field['serialization'] # type: ignore 1jBakgwblcmChDEnoxypqrsAFdtizeufv
478 json_schema = self.ser_schema(ser_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
480 # It might be that the 'serialization'` is skipped depending on `when_used`.
481 # This is only relevant for `nullable` schemas though, so we special case here.
482 if ( 1jBCh
483 json_schema is not None
484 and ser_schema.get('when_used') in ('unless-none', 'json-unless-none')
485 and schema_or_field['type'] == 'nullable'
486 ):
487 json_schema = self.get_flattened_anyof([{'type': 'null'}, json_schema]) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
488 if json_schema is None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
489 if _core_utils.is_core_schema(schema_or_field) or _core_utils.is_core_schema_field(schema_or_field): 489 ↛ 493line 489 didn't jump to line 493 because the condition on line 489 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
490 generate_for_schema_type = self._schema_type_to_method[schema_or_field['type']] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
491 json_schema = generate_for_schema_type(schema_or_field) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
492 else:
493 raise TypeError(f'Unexpected schema type: schema={schema_or_field}')
494 if _core_utils.is_core_schema(schema_or_field): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
495 json_schema = populate_defs(schema_or_field, json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
496 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
498 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, handler_func) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
500 metadata = cast(_core_metadata.CoreMetadata, schema.get('metadata', {})) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
502 # TODO: I dislike that we have to wrap these basic dict updates in callables, is there any way around this?
504 if js_updates := metadata.get('pydantic_js_updates'): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
506 def js_updates_handler_func( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
507 schema_or_field: CoreSchemaOrField,
508 current_handler: GetJsonSchemaHandler = current_handler,
509 ) -> JsonSchemaValue:
510 json_schema = {**current_handler(schema_or_field), **js_updates} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
511 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
513 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_updates_handler_func) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
515 if js_extra := metadata.get('pydantic_js_extra'): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
517 def js_extra_handler_func( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
518 schema_or_field: CoreSchemaOrField,
519 current_handler: GetJsonSchemaHandler = current_handler,
520 ) -> JsonSchemaValue:
521 json_schema = current_handler(schema_or_field) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
522 if isinstance(js_extra, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
523 json_schema.update(to_jsonable_python(js_extra)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
524 elif callable(js_extra): 524 ↛ 527line 524 didn't jump to line 527 because the condition on line 524 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
525 # similar to typing issue in _update_class_schema when we're working with callable js extra
526 js_extra(json_schema) # type: ignore 1jBakgwblcmChDEnoxypqrsAFdtizeufv
527 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
529 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_extra_handler_func) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
531 for js_modify_function in metadata.get('pydantic_js_functions', ()): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
533 def new_handler_func( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
534 schema_or_field: CoreSchemaOrField,
535 current_handler: GetJsonSchemaHandler = current_handler,
536 js_modify_function: GetJsonSchemaFunction = js_modify_function,
537 ) -> JsonSchemaValue:
538 json_schema = js_modify_function(schema_or_field, current_handler) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
539 if _core_utils.is_core_schema(schema_or_field): 539 ↛ 541line 539 didn't jump to line 541 because the condition on line 539 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
540 json_schema = populate_defs(schema_or_field, json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
541 original_schema = current_handler.resolve_ref_schema(json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
542 ref = json_schema.pop('$ref', None) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
543 if ref and json_schema: 543 ↛ 544line 543 didn't jump to line 544 because the condition on line 543 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
544 original_schema.update(json_schema)
545 return original_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
547 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
549 for js_modify_function in metadata.get('pydantic_js_annotation_functions', ()): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
551 def new_handler_func( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
552 schema_or_field: CoreSchemaOrField,
553 current_handler: GetJsonSchemaHandler = current_handler,
554 js_modify_function: GetJsonSchemaFunction = js_modify_function,
555 ) -> JsonSchemaValue:
556 json_schema = js_modify_function(schema_or_field, current_handler) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
557 if _core_utils.is_core_schema(schema_or_field): 557 ↛ 559line 557 didn't jump to line 559 because the condition on line 557 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
558 json_schema = populate_defs(schema_or_field, json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
559 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
561 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
563 json_schema = current_handler(schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
564 if _core_utils.is_core_schema(schema): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
565 json_schema = populate_defs(schema, json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
566 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
568 def sort(self, value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
569 """Override this method to customize the sorting of the JSON schema (e.g., don't sort at all, sort all keys unconditionally, etc.)
571 By default, alphabetically sort the keys in the JSON schema, skipping the 'properties' and 'default' keys to preserve field definition order.
572 This sort is recursive, so it will sort all nested dictionaries as well.
573 """
574 sorted_dict: dict[str, JsonSchemaValue] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
575 keys = value.keys() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
576 if parent_key not in ('properties', 'default'): 576 ↛ 578line 576 didn't jump to line 578 because the condition on line 576 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
577 keys = sorted(keys) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
578 for key in keys: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
579 sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
580 return sorted_dict 1jBakgwblcmChDEnoxypqrsAFdtizeufv
582 def _sort_recursive(self, value: Any, parent_key: str | None = None) -> Any: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
583 """Recursively sort a JSON schema value."""
584 if isinstance(value, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
585 sorted_dict: dict[str, JsonSchemaValue] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
586 keys = value.keys() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
587 if parent_key not in ('properties', 'default'): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
588 keys = sorted(keys) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
589 for key in keys: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
590 sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
591 return sorted_dict 1jBakgwblcmChDEnoxypqrsAFdtizeufv
592 elif isinstance(value, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
593 sorted_list: list[JsonSchemaValue] = [] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
594 for item in value: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
595 sorted_list.append(self._sort_recursive(item, parent_key)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
596 return sorted_list 1jBakgwblcmChDEnoxypqrsAFdtizeufv
597 else:
598 return value 1jBakgwblcmChDEnoxypqrsAFdtizeufv
600 # ### Schema generation methods
602 def invalid_schema(self, schema: core_schema.InvalidSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
603 """Placeholder - should never be called."""
605 raise RuntimeError('Cannot generate schema for invalid_schema. This is a bug! Please report it.')
607 def any_schema(self, schema: core_schema.AnySchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
608 """Generates a JSON schema that matches any value.
610 Args:
611 schema: The core schema.
613 Returns:
614 The generated JSON schema.
615 """
616 return {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
618 def none_schema(self, schema: core_schema.NoneSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
619 """Generates a JSON schema that matches `None`.
621 Args:
622 schema: The core schema.
624 Returns:
625 The generated JSON schema.
626 """
627 return {'type': 'null'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
629 def bool_schema(self, schema: core_schema.BoolSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
630 """Generates a JSON schema that matches a bool value.
632 Args:
633 schema: The core schema.
635 Returns:
636 The generated JSON schema.
637 """
638 return {'type': 'boolean'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
640 def int_schema(self, schema: core_schema.IntSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
641 """Generates a JSON schema that matches an int value.
643 Args:
644 schema: The core schema.
646 Returns:
647 The generated JSON schema.
648 """
649 json_schema: dict[str, Any] = {'type': 'integer'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
650 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
651 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
652 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
654 def float_schema(self, schema: core_schema.FloatSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
655 """Generates a JSON schema that matches a float value.
657 Args:
658 schema: The core schema.
660 Returns:
661 The generated JSON schema.
662 """
663 json_schema: dict[str, Any] = {'type': 'number'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
664 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
665 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
666 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
668 def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
669 """Generates a JSON schema that matches a decimal value.
671 Args:
672 schema: The core schema.
674 Returns:
675 The generated JSON schema.
676 """
677 json_schema = self.str_schema(core_schema.str_schema()) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
678 if self.mode == 'validation': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
679 multiple_of = schema.get('multiple_of') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
680 le = schema.get('le') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
681 ge = schema.get('ge') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
682 lt = schema.get('lt') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
683 gt = schema.get('gt') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
684 json_schema = { 1jBakgwblcmChDEnoxypqrsAFdtizeufv
685 'anyOf': [
686 self.float_schema(
687 core_schema.float_schema(
688 allow_inf_nan=schema.get('allow_inf_nan'),
689 multiple_of=None if multiple_of is None else float(multiple_of),
690 le=None if le is None else float(le),
691 ge=None if ge is None else float(ge),
692 lt=None if lt is None else float(lt),
693 gt=None if gt is None else float(gt),
694 )
695 ),
696 json_schema,
697 ],
698 }
699 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
701 def str_schema(self, schema: core_schema.StringSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
702 """Generates a JSON schema that matches a string value.
704 Args:
705 schema: The core schema.
707 Returns:
708 The generated JSON schema.
709 """
710 json_schema = {'type': 'string'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
711 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
712 if isinstance(json_schema.get('pattern'), Pattern): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
713 # TODO: should we add regex flags to the pattern?
714 json_schema['pattern'] = json_schema.get('pattern').pattern # type: ignore 1jBakgwblcmChDEnoxypqrsAFdtizeufv
715 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
717 def bytes_schema(self, schema: core_schema.BytesSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
718 """Generates a JSON schema that matches a bytes value.
720 Args:
721 schema: The core schema.
723 Returns:
724 The generated JSON schema.
725 """
726 json_schema = {'type': 'string', 'format': 'base64url' if self._config.ser_json_bytes == 'base64' else 'binary'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
727 self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
728 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
730 def date_schema(self, schema: core_schema.DateSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
731 """Generates a JSON schema that matches a date value.
733 Args:
734 schema: The core schema.
736 Returns:
737 The generated JSON schema.
738 """
739 return {'type': 'string', 'format': 'date'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
741 def time_schema(self, schema: core_schema.TimeSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
742 """Generates a JSON schema that matches a time value.
744 Args:
745 schema: The core schema.
747 Returns:
748 The generated JSON schema.
749 """
750 return {'type': 'string', 'format': 'time'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
752 def datetime_schema(self, schema: core_schema.DatetimeSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
753 """Generates a JSON schema that matches a datetime value.
755 Args:
756 schema: The core schema.
758 Returns:
759 The generated JSON schema.
760 """
761 return {'type': 'string', 'format': 'date-time'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
763 def timedelta_schema(self, schema: core_schema.TimedeltaSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
764 """Generates a JSON schema that matches a timedelta value.
766 Args:
767 schema: The core schema.
769 Returns:
770 The generated JSON schema.
771 """
772 if self._config.ser_json_timedelta == 'float': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
773 return {'type': 'number'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
774 return {'type': 'string', 'format': 'duration'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
776 def literal_schema(self, schema: core_schema.LiteralSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
777 """Generates a JSON schema that matches a literal value.
779 Args:
780 schema: The core schema.
782 Returns:
783 The generated JSON schema.
784 """
785 expected = [to_jsonable_python(v.value if isinstance(v, Enum) else v) for v in schema['expected']] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
787 result: dict[str, Any] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
788 if len(expected) == 1: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
789 result['const'] = expected[0] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
790 else:
791 result['enum'] = expected 1jBakgwblcmChDEnoxypqrsAFdtizeufv
793 types = {type(e) for e in expected} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
794 if types == {str}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
795 result['type'] = 'string' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
796 elif types == {int}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
797 result['type'] = 'integer' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
798 elif types == {float}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
799 result['type'] = 'number' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
800 elif types == {bool}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
801 result['type'] = 'boolean' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
802 elif types == {list}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
803 result['type'] = 'array' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
804 elif types == {type(None)}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
805 result['type'] = 'null' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
806 return result 1jBakgwblcmChDEnoxypqrsAFdtizeufv
808 def enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
809 """Generates a JSON schema that matches an Enum value.
811 Args:
812 schema: The core schema.
814 Returns:
815 The generated JSON schema.
816 """
817 enum_type = schema['cls'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
818 description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
819 if ( 1jBCh
820 description == 'An enumeration.'
821 ): # This is the default value provided by enum.EnumMeta.__new__; don't use it
822 description = None 1jBakChDEnoAFdt
823 result: dict[str, Any] = {'title': enum_type.__name__, 'description': description} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
824 result = {k: v for k, v in result.items() if v is not None} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
826 expected = [to_jsonable_python(v.value) for v in schema['members']] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
828 result['enum'] = expected 1jBakgwblcmChDEnoxypqrsAFdtizeufv
830 types = {type(e) for e in expected} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
831 if isinstance(enum_type, str) or types == {str}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
832 result['type'] = 'string' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
833 elif isinstance(enum_type, int) or types == {int}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
834 result['type'] = 'integer' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
835 elif isinstance(enum_type, float) or types == {float}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
836 result['type'] = 'number' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
837 elif types == {bool}: 837 ↛ 838line 837 didn't jump to line 838 because the condition on line 837 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
838 result['type'] = 'boolean'
839 elif types == {list}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
840 result['type'] = 'array' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
842 return result 1jBakgwblcmChDEnoxypqrsAFdtizeufv
844 def is_instance_schema(self, schema: core_schema.IsInstanceSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
845 """Handles JSON schema generation for a core schema that checks if a value is an instance of a class.
847 Unless overridden in a subclass, this raises an error.
849 Args:
850 schema: The core schema.
852 Returns:
853 The generated JSON schema.
854 """
855 return self.handle_invalid_for_json_schema(schema, f'core_schema.IsInstanceSchema ({schema["cls"]})') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
857 def is_subclass_schema(self, schema: core_schema.IsSubclassSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
858 """Handles JSON schema generation for a core schema that checks if a value is a subclass of a class.
860 For backwards compatibility with v1, this does not raise an error, but can be overridden to change this.
862 Args:
863 schema: The core schema.
865 Returns:
866 The generated JSON schema.
867 """
868 # Note: This is for compatibility with V1; you can override if you want different behavior.
869 return {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
871 def callable_schema(self, schema: core_schema.CallableSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
872 """Generates a JSON schema that matches a callable value.
874 Unless overridden in a subclass, this raises an error.
876 Args:
877 schema: The core schema.
879 Returns:
880 The generated JSON schema.
881 """
882 return self.handle_invalid_for_json_schema(schema, 'core_schema.CallableSchema') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
884 def list_schema(self, schema: core_schema.ListSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
885 """Returns a schema that matches a list schema.
887 Args:
888 schema: The core schema.
890 Returns:
891 The generated JSON schema.
892 """
893 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
894 json_schema = {'type': 'array', 'items': items_schema} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
895 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
896 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
898 @deprecated('`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', category=None) 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
899 @final 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
900 def tuple_positional_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
901 """Replaced by `tuple_schema`."""
902 warnings.warn(
903 '`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.',
904 PydanticDeprecatedSince26,
905 stacklevel=2,
906 )
907 return self.tuple_schema(schema)
909 @deprecated('`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', category=None) 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
910 @final 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
911 def tuple_variable_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
912 """Replaced by `tuple_schema`."""
913 warnings.warn(
914 '`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.',
915 PydanticDeprecatedSince26,
916 stacklevel=2,
917 )
918 return self.tuple_schema(schema)
920 def tuple_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
921 """Generates a JSON schema that matches a tuple schema e.g. `tuple[int,
922 str, bool]` or `tuple[int, ...]`.
924 Args:
925 schema: The core schema.
927 Returns:
928 The generated JSON schema.
929 """
930 json_schema: JsonSchemaValue = {'type': 'array'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
931 if 'variadic_item_index' in schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
932 variadic_item_index = schema['variadic_item_index'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
933 if variadic_item_index > 0: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
934 json_schema['minItems'] = variadic_item_index 1jBakgwblcmChDEnoxypqrsAFdtizeufv
935 json_schema['prefixItems'] = [ 1jBakgwblcmChDEnoxypqrsAFdtizeufv
936 self.generate_inner(item) for item in schema['items_schema'][:variadic_item_index]
937 ]
938 if variadic_item_index + 1 == len(schema['items_schema']): 938 ↛ 945line 938 didn't jump to line 945 because the condition on line 938 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
939 # if the variadic item is the last item, then represent it faithfully
940 json_schema['items'] = self.generate_inner(schema['items_schema'][variadic_item_index]) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
941 else:
942 # otherwise, 'items' represents the schema for the variadic
943 # item plus the suffix, so just allow anything for simplicity
944 # for now
945 json_schema['items'] = True
946 else:
947 prefixItems = [self.generate_inner(item) for item in schema['items_schema']] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
948 if prefixItems: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
949 json_schema['prefixItems'] = prefixItems 1jBakgwblcmChDEnoxypqrsAFdtizeufv
950 json_schema['minItems'] = len(prefixItems) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
951 json_schema['maxItems'] = len(prefixItems) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
952 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
953 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
955 def set_schema(self, schema: core_schema.SetSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
956 """Generates a JSON schema that matches a set schema.
958 Args:
959 schema: The core schema.
961 Returns:
962 The generated JSON schema.
963 """
964 return self._common_set_schema(schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
966 def frozenset_schema(self, schema: core_schema.FrozenSetSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
967 """Generates a JSON schema that matches a frozenset schema.
969 Args:
970 schema: The core schema.
972 Returns:
973 The generated JSON schema.
974 """
975 return self._common_set_schema(schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
977 def _common_set_schema(self, schema: core_schema.SetSchema | core_schema.FrozenSetSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
978 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
979 json_schema = {'type': 'array', 'uniqueItems': True, 'items': items_schema} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
980 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
981 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
983 def generator_schema(self, schema: core_schema.GeneratorSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
984 """Returns a JSON schema that represents the provided GeneratorSchema.
986 Args:
987 schema: The schema.
989 Returns:
990 The generated JSON schema.
991 """
992 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
993 json_schema = {'type': 'array', 'items': items_schema} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
994 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
995 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
997 def dict_schema(self, schema: core_schema.DictSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
998 """Generates a JSON schema that matches a dict schema.
1000 Args:
1001 schema: The core schema.
1003 Returns:
1004 The generated JSON schema.
1005 """
1006 json_schema: JsonSchemaValue = {'type': 'object'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1008 keys_schema = self.generate_inner(schema['keys_schema']).copy() if 'keys_schema' in schema else {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1009 if '$ref' not in keys_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1010 keys_pattern = keys_schema.pop('pattern', None) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1011 # Don't give a title to patternProperties/propertyNames:
1012 keys_schema.pop('title', None) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1013 else:
1014 # Here, we assume that if the keys schema is a definition reference,
1015 # it can't be a simple string core schema (and thus no pattern can exist).
1016 # However, this is only in practice (in theory, a definition reference core
1017 # schema could be generated for a simple string schema).
1018 # Note that we avoid calling `self.resolve_ref_schema`, as it might not exist yet.
1019 keys_pattern = None 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1021 values_schema = self.generate_inner(schema['values_schema']).copy() if 'values_schema' in schema else {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1022 # don't give a title to additionalProperties:
1023 values_schema.pop('title', None) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1025 if values_schema or keys_pattern is not None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1026 if keys_pattern is None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1027 json_schema['additionalProperties'] = values_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1028 else:
1029 json_schema['patternProperties'] = {keys_pattern: values_schema} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1030 else: # for `dict[str, Any]`, we allow any key and any value, since `str` is the default key type
1031 json_schema['additionalProperties'] = True 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1033 if ( 1jBCh
1034 # The len check indicates that constraints are probably present:
1035 (keys_schema.get('type') == 'string' and len(keys_schema) > 1)
1036 # If this is a definition reference schema, it most likely has constraints:
1037 or '$ref' in keys_schema
1038 ):
1039 keys_schema.pop('type', None) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1040 json_schema['propertyNames'] = keys_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1042 self.update_with_validations(json_schema, schema, self.ValidationsMapping.object) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1043 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1045 def function_before_schema(self, schema: core_schema.BeforeValidatorFunctionSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1046 """Generates a JSON schema that matches a function-before schema.
1048 Args:
1049 schema: The core schema.
1051 Returns:
1052 The generated JSON schema.
1053 """
1054 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1055 return self.generate_inner(input_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1057 return self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1059 def function_after_schema(self, schema: core_schema.AfterValidatorFunctionSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1060 """Generates a JSON schema that matches a function-after schema.
1062 Args:
1063 schema: The core schema.
1065 Returns:
1066 The generated JSON schema.
1067 """
1068 return self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1070 def function_plain_schema(self, schema: core_schema.PlainValidatorFunctionSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1071 """Generates a JSON schema that matches a function-plain schema.
1073 Args:
1074 schema: The core schema.
1076 Returns:
1077 The generated JSON schema.
1078 """
1079 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 1079 ↛ 1082line 1079 didn't jump to line 1082 because the condition on line 1079 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1080 return self.generate_inner(input_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1082 return self.handle_invalid_for_json_schema(
1083 schema, f'core_schema.PlainValidatorFunctionSchema ({schema["function"]})'
1084 )
1086 def function_wrap_schema(self, schema: core_schema.WrapValidatorFunctionSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1087 """Generates a JSON schema that matches a function-wrap schema.
1089 Args:
1090 schema: The core schema.
1092 Returns:
1093 The generated JSON schema.
1094 """
1095 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1096 return self.generate_inner(input_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1098 return self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1100 def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1101 """Generates a JSON schema that matches a schema with a default value.
1103 Args:
1104 schema: The core schema.
1106 Returns:
1107 The generated JSON schema.
1108 """
1109 json_schema = self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1111 if 'default' not in schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1112 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1113 default = schema['default'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1114 # Note: if you want to include the value returned by the default_factory,
1115 # override this method and replace the code above with:
1116 # if 'default' in schema:
1117 # default = schema['default']
1118 # elif 'default_factory' in schema:
1119 # default = schema['default_factory']()
1120 # else:
1121 # return json_schema
1123 # we reflect the application of custom plain, no-info serializers to defaults for
1124 # JSON Schemas viewed in serialization mode:
1125 # TODO: improvements along with https://github.com/pydantic/pydantic/issues/8208
1126 if ( 1jBChDEAF
1127 self.mode == 'serialization'
1128 and (ser_schema := schema['schema'].get('serialization'))
1129 and (ser_func := ser_schema.get('function'))
1130 and ser_schema.get('type') == 'function-plain'
1131 and not ser_schema.get('info_arg')
1132 and not (default is None and ser_schema.get('when_used') in ('unless-none', 'json-unless-none'))
1133 ):
1134 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1135 default = ser_func(default) # type: ignore 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1136 except Exception: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1137 # It might be that the provided default needs to be validated (read: parsed) first
1138 # (assuming `validate_default` is enabled). However, we can't perform
1139 # such validation during JSON Schema generation so we don't support
1140 # this pattern for now.
1141 # (One example is when using `foo: ByteSize = '1MB'`, which validates and
1142 # serializes as an int. In this case, `ser_func` is `int` and `int('1MB')` fails).
1143 self.emit_warning( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1144 'non-serializable-default',
1145 f'Unable to serialize value {default!r} with the plain serializer; excluding default from JSON schema',
1146 )
1147 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1149 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1150 encoded_default = self.encode_default(default) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1151 except pydantic_core.PydanticSerializationError: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1152 self.emit_warning( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1153 'non-serializable-default',
1154 f'Default value {default} is not JSON serializable; excluding default from JSON schema',
1155 )
1156 # Return the inner schema, as though there was no default
1157 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1159 json_schema['default'] = encoded_default 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1160 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1162 def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1163 """Generates a JSON schema that matches a schema that allows null values.
1165 Args:
1166 schema: The core schema.
1168 Returns:
1169 The generated JSON schema.
1170 """
1171 null_schema = {'type': 'null'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1172 inner_json_schema = self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1174 if inner_json_schema == null_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1175 return null_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1176 else:
1177 # Thanks to the equality check against `null_schema` above, I think 'oneOf' would also be valid here;
1178 # I'll use 'anyOf' for now, but it could be changed it if it would work better with some external tooling
1179 return self.get_flattened_anyof([inner_json_schema, null_schema]) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1181 def union_schema(self, schema: core_schema.UnionSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1182 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas.
1184 Args:
1185 schema: The core schema.
1187 Returns:
1188 The generated JSON schema.
1189 """
1190 generated: list[JsonSchemaValue] = [] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1192 choices = schema['choices'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1193 for choice in choices: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1194 # choice will be a tuple if an explicit label was provided
1195 choice_schema = choice[0] if isinstance(choice, tuple) else choice 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1196 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1197 generated.append(self.generate_inner(choice_schema)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1198 except PydanticOmit: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1199 continue 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1200 except PydanticInvalidForJsonSchema as exc: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1201 self.emit_warning('skipped-choice', exc.message) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1202 if len(generated) == 1: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1203 return generated[0] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1204 return self.get_flattened_anyof(generated) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1206 def tagged_union_schema(self, schema: core_schema.TaggedUnionSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1207 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas, where
1208 the schemas are tagged with a discriminator field that indicates which schema should be used to validate
1209 the value.
1211 Args:
1212 schema: The core schema.
1214 Returns:
1215 The generated JSON schema.
1216 """
1217 generated: dict[str, JsonSchemaValue] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1218 for k, v in schema['choices'].items(): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1219 if isinstance(k, Enum): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1220 k = k.value 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1221 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1222 # Use str(k) since keys must be strings for json; while not technically correct,
1223 # it's the closest that can be represented in valid JSON
1224 generated[str(k)] = self.generate_inner(v).copy() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1225 except PydanticOmit:
1226 continue
1227 except PydanticInvalidForJsonSchema as exc:
1228 self.emit_warning('skipped-choice', exc.message)
1230 one_of_choices = _deduplicate_schemas(generated.values()) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1231 json_schema: JsonSchemaValue = {'oneOf': one_of_choices} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1233 # This reflects the v1 behavior; TODO: we should make it possible to exclude OpenAPI stuff from the JSON schema
1234 openapi_discriminator = self._extract_discriminator(schema, one_of_choices) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1235 if openapi_discriminator is not None: 1235 ↛ 1241line 1235 didn't jump to line 1241 because the condition on line 1235 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1236 json_schema['discriminator'] = { 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1237 'propertyName': openapi_discriminator,
1238 'mapping': {k: v.get('$ref', v) for k, v in generated.items()},
1239 }
1241 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1243 def _extract_discriminator( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1244 self, schema: core_schema.TaggedUnionSchema, one_of_choices: list[JsonDict]
1245 ) -> str | None:
1246 """Extract a compatible OpenAPI discriminator from the schema and one_of choices that end up in the final
1247 schema."""
1248 openapi_discriminator: str | None = None 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1250 if isinstance(schema['discriminator'], str): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1251 return schema['discriminator'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1253 if isinstance(schema['discriminator'], list): 1253 ↛ 1286line 1253 didn't jump to line 1286 because the condition on line 1253 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1254 # If the discriminator is a single item list containing a string, that is equivalent to the string case
1255 if len(schema['discriminator']) == 1 and isinstance(schema['discriminator'][0], str): 1255 ↛ 1256line 1255 didn't jump to line 1256 because the condition on line 1255 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1256 return schema['discriminator'][0]
1257 # When an alias is used that is different from the field name, the discriminator will be a list of single
1258 # str lists, one for the attribute and one for the actual alias. The logic here will work even if there is
1259 # more than one possible attribute, and looks for whether a single alias choice is present as a documented
1260 # property on all choices. If so, that property will be used as the OpenAPI discriminator.
1261 for alias_path in schema['discriminator']: 1261 ↛ 1286line 1261 didn't jump to line 1286 because the loop on line 1261 didn't complete1jBakgwblcmChDEnoxypqrsAFdtizeufv
1262 if not isinstance(alias_path, list): 1262 ↛ 1263line 1262 didn't jump to line 1263 because the condition on line 1262 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1263 break # this means that the discriminator is not a list of alias paths
1264 if len(alias_path) != 1: 1264 ↛ 1265line 1264 didn't jump to line 1265 because the condition on line 1264 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1265 continue # this means that the "alias" does not represent a single field
1266 alias = alias_path[0] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1267 if not isinstance(alias, str): 1267 ↛ 1268line 1267 didn't jump to line 1268 because the condition on line 1267 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1268 continue # this means that the "alias" does not represent a field
1269 alias_is_present_on_all_choices = True 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1270 for choice in one_of_choices: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1271 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1272 choice = self.resolve_ref_schema(choice) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1273 except RuntimeError as exc:
1274 # TODO: fixme - this is a workaround for the fact that we can't always resolve refs
1275 # for tagged union choices at this point in the schema gen process, we might need to do
1276 # another pass at the end like we do for core schemas
1277 self.emit_warning('skipped-discriminator', str(exc))
1278 choice = {}
1279 properties = choice.get('properties', {}) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1280 if not isinstance(properties, dict) or alias not in properties: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1281 alias_is_present_on_all_choices = False 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1282 break 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1283 if alias_is_present_on_all_choices: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1284 openapi_discriminator = alias 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1285 break 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1286 return openapi_discriminator 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1288 def chain_schema(self, schema: core_schema.ChainSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1289 """Generates a JSON schema that matches a core_schema.ChainSchema.
1291 When generating a schema for validation, we return the validation JSON schema for the first step in the chain.
1292 For serialization, we return the serialization JSON schema for the last step in the chain.
1294 Args:
1295 schema: The core schema.
1297 Returns:
1298 The generated JSON schema.
1299 """
1300 step_index = 0 if self.mode == 'validation' else -1 # use first step for validation, last for serialization 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1301 return self.generate_inner(schema['steps'][step_index]) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1303 def lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1304 """Generates a JSON schema that matches a schema that allows values matching either the lax schema or the
1305 strict schema.
1307 Args:
1308 schema: The core schema.
1310 Returns:
1311 The generated JSON schema.
1312 """
1313 # TODO: Need to read the default value off of model config or whatever
1314 use_strict = schema.get('strict', False) # TODO: replace this default False 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1315 # If your JSON schema fails to generate it is probably
1316 # because one of the following two branches failed.
1317 if use_strict: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1318 return self.generate_inner(schema['strict_schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1319 else:
1320 return self.generate_inner(schema['lax_schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1322 def json_or_python_schema(self, schema: core_schema.JsonOrPythonSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1323 """Generates a JSON schema that matches a schema that allows values matching either the JSON schema or the
1324 Python schema.
1326 The JSON schema is used instead of the Python schema. If you want to use the Python schema, you should override
1327 this method.
1329 Args:
1330 schema: The core schema.
1332 Returns:
1333 The generated JSON schema.
1334 """
1335 return self.generate_inner(schema['json_schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1337 def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1338 """Generates a JSON schema that matches a schema that defines a typed dict.
1340 Args:
1341 schema: The core schema.
1343 Returns:
1344 The generated JSON schema.
1345 """
1346 total = schema.get('total', True) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1347 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1348 (name, self.field_is_required(field, total), field)
1349 for name, field in schema['fields'].items()
1350 if self.field_is_present(field)
1351 ]
1352 if self.mode == 'serialization': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1353 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1354 cls = schema.get('cls') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1355 config = _get_typed_dict_config(cls) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1356 with self._config_wrapper_stack.push(config): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1357 json_schema = self._named_required_fields_schema(named_required_fields) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1359 if cls is not None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1360 self._update_class_schema(json_schema, cls, config) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1361 else:
1362 extra = config.get('extra') 1jagbcAdief
1363 if extra == 'forbid': 1363 ↛ 1364line 1363 didn't jump to line 1364 because the condition on line 1363 was never true1jagbcAdief
1364 json_schema['additionalProperties'] = False
1365 elif extra == 'allow': 1365 ↛ 1366line 1365 didn't jump to line 1366 because the condition on line 1365 was never true1jagbcAdief
1366 json_schema['additionalProperties'] = True
1368 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1370 @staticmethod 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1371 def _name_required_computed_fields( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1372 computed_fields: list[ComputedField],
1373 ) -> list[tuple[str, bool, core_schema.ComputedField]]:
1374 return [(field['property_name'], True, field) for field in computed_fields] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1376 def _named_required_fields_schema( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1377 self, named_required_fields: Sequence[tuple[str, bool, CoreSchemaField]]
1378 ) -> JsonSchemaValue:
1379 properties: dict[str, JsonSchemaValue] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1380 required_fields: list[str] = [] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1381 for name, required, field in named_required_fields: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1382 if self.by_alias: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1383 name = self._get_alias_name(field, name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1384 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1385 field_json_schema = self.generate_inner(field).copy() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1386 except PydanticOmit: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1387 continue 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1388 if 'title' not in field_json_schema and self.field_title_should_be_set(field): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1389 title = self.get_title_from_name(name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1390 field_json_schema['title'] = title 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1391 field_json_schema = self.handle_ref_overrides(field_json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1392 properties[name] = field_json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1393 if required: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1394 required_fields.append(name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1396 json_schema = {'type': 'object', 'properties': properties} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1397 if required_fields: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1398 json_schema['required'] = required_fields 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1399 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1401 def _get_alias_name(self, field: CoreSchemaField, name: str) -> str: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1402 if field['type'] == 'computed-field': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1403 alias: Any = field.get('alias', name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1404 elif self.mode == 'validation': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1405 alias = field.get('validation_alias', name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1406 else:
1407 alias = field.get('serialization_alias', name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1408 if isinstance(alias, str): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1409 name = alias 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1410 elif isinstance(alias, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1411 alias = cast('list[str] | str', alias) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1412 for path in alias: 1412 ↛ 1420line 1412 didn't jump to line 1420 because the loop on line 1412 didn't complete1jBakgwblcmChDEnoxypqrsAFdtizeufv
1413 if isinstance(path, list) and len(path) == 1 and isinstance(path[0], str): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1414 # Use the first valid single-item string path; the code that constructs the alias array
1415 # should ensure the first such item is what belongs in the JSON schema
1416 name = path[0] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1417 break 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1418 else:
1419 assert_never(alias)
1420 return name 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1422 def typed_dict_field_schema(self, schema: core_schema.TypedDictField) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1423 """Generates a JSON schema that matches a schema that defines a typed dict field.
1425 Args:
1426 schema: The core schema.
1428 Returns:
1429 The generated JSON schema.
1430 """
1431 return self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1433 def dataclass_field_schema(self, schema: core_schema.DataclassField) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1434 """Generates a JSON schema that matches a schema that defines a dataclass field.
1436 Args:
1437 schema: The core schema.
1439 Returns:
1440 The generated JSON schema.
1441 """
1442 return self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1444 def model_field_schema(self, schema: core_schema.ModelField) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1445 """Generates a JSON schema that matches a schema that defines a model field.
1447 Args:
1448 schema: The core schema.
1450 Returns:
1451 The generated JSON schema.
1452 """
1453 return self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1455 def computed_field_schema(self, schema: core_schema.ComputedField) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1456 """Generates a JSON schema that matches a schema that defines a computed field.
1458 Args:
1459 schema: The core schema.
1461 Returns:
1462 The generated JSON schema.
1463 """
1464 return self.generate_inner(schema['return_schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1466 def model_schema(self, schema: core_schema.ModelSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1467 """Generates a JSON schema that matches a schema that defines a model.
1469 Args:
1470 schema: The core schema.
1472 Returns:
1473 The generated JSON schema.
1474 """
1475 # We do not use schema['model'].model_json_schema() here
1476 # because it could lead to inconsistent refs handling, etc.
1477 cls = cast('type[BaseModel]', schema['cls']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1478 config = cls.model_config 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1480 with self._config_wrapper_stack.push(config): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1481 json_schema = self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1483 self._update_class_schema(json_schema, cls, config) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1485 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1487 def _update_class_schema(self, json_schema: JsonSchemaValue, cls: type[Any], config: ConfigDict) -> None: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1488 """Update json_schema with the following, extracted from `config` and `cls`:
1490 * title
1491 * description
1492 * additional properties
1493 * json_schema_extra
1494 * deprecated
1496 Done in place, hence there's no return value as the original json_schema is mutated.
1497 No ref resolving is involved here, as that's not appropriate for simple updates.
1498 """
1499 from .main import BaseModel 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1500 from .root_model import RootModel 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1502 if (config_title := config.get('title')) is not None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1503 json_schema.setdefault('title', config_title) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1504 elif model_title_generator := config.get('model_title_generator'): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1505 title = model_title_generator(cls) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1506 if not isinstance(title, str): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1507 raise TypeError(f'model_title_generator {model_title_generator} must return str, not {title.__class__}') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1508 json_schema.setdefault('title', title) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1509 if 'title' not in json_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1510 json_schema['title'] = cls.__name__ 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1512 # BaseModel and dataclasses; don't use cls.__doc__ as it will contain the verbose class signature by default
1513 docstring = None if cls is BaseModel or dataclasses.is_dataclass(cls) else cls.__doc__ 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1515 if docstring: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1516 json_schema.setdefault('description', inspect.cleandoc(docstring)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1517 elif issubclass(cls, RootModel) and (root_description := cls.__pydantic_fields__['root'].description): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1518 json_schema.setdefault('description', root_description) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1520 extra = config.get('extra') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1521 if 'additionalProperties' not in json_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1522 if extra == 'allow': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1523 json_schema['additionalProperties'] = True 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1524 elif extra == 'forbid': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1525 json_schema['additionalProperties'] = False 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1527 json_schema_extra = config.get('json_schema_extra') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1528 if issubclass(cls, BaseModel) and cls.__pydantic_root_model__: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1529 root_json_schema_extra = cls.model_fields['root'].json_schema_extra 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1530 if json_schema_extra and root_json_schema_extra: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1531 raise ValueError( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1532 '"model_config[\'json_schema_extra\']" and "Field.json_schema_extra" on "RootModel.root"'
1533 ' field must not be set simultaneously'
1534 )
1535 if root_json_schema_extra: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1536 json_schema_extra = root_json_schema_extra 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1538 if isinstance(json_schema_extra, (staticmethod, classmethod)): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1539 # In older versions of python, this is necessary to ensure staticmethod/classmethods are callable
1540 json_schema_extra = json_schema_extra.__get__(cls) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1542 if isinstance(json_schema_extra, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1543 json_schema.update(json_schema_extra) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1544 elif callable(json_schema_extra): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1545 # FIXME: why are there type ignores here? We support two signatures for json_schema_extra callables...
1546 if len(inspect.signature(json_schema_extra).parameters) > 1: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1547 json_schema_extra(json_schema, cls) # type: ignore 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1548 else:
1549 json_schema_extra(json_schema) # type: ignore 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1550 elif json_schema_extra is not None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1551 raise ValueError( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1552 f"model_config['json_schema_extra']={json_schema_extra} should be a dict, callable, or None"
1553 )
1555 if hasattr(cls, '__deprecated__'): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1556 json_schema['deprecated'] = True 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1558 def resolve_ref_schema(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1559 """Resolve a JsonSchemaValue to the non-ref schema if it is a $ref schema.
1561 Args:
1562 json_schema: The schema to resolve.
1564 Returns:
1565 The resolved schema.
1567 Raises:
1568 RuntimeError: If the schema reference can't be found in definitions.
1569 """
1570 while '$ref' in json_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1571 ref = json_schema['$ref'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1572 schema_to_update = self.get_schema_from_definitions(JsonRef(ref)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1573 if schema_to_update is None: 1573 ↛ 1574line 1573 didn't jump to line 1574 because the condition on line 1573 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1574 raise RuntimeError(f'Cannot update undefined schema for $ref={ref}')
1575 json_schema = schema_to_update 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1576 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1578 def model_fields_schema(self, schema: core_schema.ModelFieldsSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1579 """Generates a JSON schema that matches a schema that defines a model's fields.
1581 Args:
1582 schema: The core schema.
1584 Returns:
1585 The generated JSON schema.
1586 """
1587 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1588 (name, self.field_is_required(field, total=True), field)
1589 for name, field in schema['fields'].items()
1590 if self.field_is_present(field)
1591 ]
1592 if self.mode == 'serialization': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1593 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1594 json_schema = self._named_required_fields_schema(named_required_fields) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1595 extras_schema = schema.get('extras_schema', None) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1596 if extras_schema is not None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1597 schema_to_update = self.resolve_ref_schema(json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1598 schema_to_update['additionalProperties'] = self.generate_inner(extras_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1599 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1601 def field_is_present(self, field: CoreSchemaField) -> bool: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1602 """Whether the field should be included in the generated JSON schema.
1604 Args:
1605 field: The schema for the field itself.
1607 Returns:
1608 `True` if the field should be included in the generated JSON schema, `False` otherwise.
1609 """
1610 if self.mode == 'serialization': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1611 # If you still want to include the field in the generated JSON schema,
1612 # override this method and return True
1613 return not field.get('serialization_exclude') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1614 elif self.mode == 'validation': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1615 return True 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1616 else:
1617 assert_never(self.mode)
1619 def field_is_required( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1620 self,
1621 field: core_schema.ModelField | core_schema.DataclassField | core_schema.TypedDictField,
1622 total: bool,
1623 ) -> bool:
1624 """Whether the field should be marked as required in the generated JSON schema.
1625 (Note that this is irrelevant if the field is not present in the JSON schema.).
1627 Args:
1628 field: The schema for the field itself.
1629 total: Only applies to `TypedDictField`s.
1630 Indicates if the `TypedDict` this field belongs to is total, in which case any fields that don't
1631 explicitly specify `required=False` are required.
1633 Returns:
1634 `True` if the field should be marked as required in the generated JSON schema, `False` otherwise.
1635 """
1636 if self.mode == 'serialization' and self._config.json_schema_serialization_defaults_required: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1637 return not field.get('serialization_exclude') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1638 else:
1639 if field['type'] == 'typed-dict-field': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1640 return field.get('required', total) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1641 else:
1642 return field['schema']['type'] != 'default' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1644 def dataclass_args_schema(self, schema: core_schema.DataclassArgsSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1645 """Generates a JSON schema that matches a schema that defines a dataclass's constructor arguments.
1647 Args:
1648 schema: The core schema.
1650 Returns:
1651 The generated JSON schema.
1652 """
1653 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1654 (field['name'], self.field_is_required(field, total=True), field)
1655 for field in schema['fields']
1656 if self.field_is_present(field)
1657 ]
1658 if self.mode == 'serialization': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1659 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1660 return self._named_required_fields_schema(named_required_fields) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1662 def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1663 """Generates a JSON schema that matches a schema that defines a dataclass.
1665 Args:
1666 schema: The core schema.
1668 Returns:
1669 The generated JSON schema.
1670 """
1671 from ._internal._dataclasses import is_builtin_dataclass 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1673 cls = schema['cls'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1674 config: ConfigDict = getattr(cls, '__pydantic_config__', cast('ConfigDict', {})) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1676 with self._config_wrapper_stack.push(config): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1677 json_schema = self.generate_inner(schema['schema']).copy() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1679 self._update_class_schema(json_schema, cls, config) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1681 # Dataclass-specific handling of description
1682 if is_builtin_dataclass(cls): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1683 # vanilla dataclass; don't use cls.__doc__ as it will contain the class signature by default
1684 description = None 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1685 else:
1686 description = None if cls.__doc__ is None else inspect.cleandoc(cls.__doc__) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1687 if description: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1688 json_schema['description'] = description 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1690 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1692 def arguments_schema(self, schema: core_schema.ArgumentsSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1693 """Generates a JSON schema that matches a schema that defines a function's arguments.
1695 Args:
1696 schema: The core schema.
1698 Returns:
1699 The generated JSON schema.
1700 """
1701 prefer_positional = schema.get('metadata', {}).get('pydantic_js_prefer_positional_arguments') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1703 arguments = schema['arguments_schema'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1704 kw_only_arguments = [a for a in arguments if a.get('mode') == 'keyword_only'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1705 kw_or_p_arguments = [a for a in arguments if a.get('mode') in {'positional_or_keyword', None}] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1706 p_only_arguments = [a for a in arguments if a.get('mode') == 'positional_only'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1707 var_args_schema = schema.get('var_args_schema') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1708 var_kwargs_schema = schema.get('var_kwargs_schema') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1710 if prefer_positional: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1711 positional_possible = not kw_only_arguments and not var_kwargs_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1712 if positional_possible: 1712 ↛ 1715line 1712 didn't jump to line 1715 because the condition on line 1712 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1713 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1715 keyword_possible = not p_only_arguments and not var_args_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1716 if keyword_possible: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1717 return self.kw_arguments_schema(kw_or_p_arguments + kw_only_arguments, var_kwargs_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1719 if not prefer_positional: 1719 ↛ 1724line 1719 didn't jump to line 1724 because the condition on line 1719 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1720 positional_possible = not kw_only_arguments and not var_kwargs_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1721 if positional_possible: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1722 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1724 raise PydanticInvalidForJsonSchema( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1725 'Unable to generate JSON schema for arguments validator with positional-only and keyword-only arguments'
1726 )
1728 def kw_arguments_schema( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1729 self, arguments: list[core_schema.ArgumentsParameter], var_kwargs_schema: CoreSchema | None
1730 ) -> JsonSchemaValue:
1731 """Generates a JSON schema that matches a schema that defines a function's keyword arguments.
1733 Args:
1734 arguments: The core schema.
1736 Returns:
1737 The generated JSON schema.
1738 """
1739 properties: dict[str, JsonSchemaValue] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1740 required: list[str] = [] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1741 for argument in arguments: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1742 name = self.get_argument_name(argument) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1743 argument_schema = self.generate_inner(argument['schema']).copy() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1744 argument_schema['title'] = self.get_title_from_name(name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1745 properties[name] = argument_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1747 if argument['schema']['type'] != 'default': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1748 # This assumes that if the argument has a default value,
1749 # the inner schema must be of type WithDefaultSchema.
1750 # I believe this is true, but I am not 100% sure
1751 required.append(name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1753 json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1754 if required: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1755 json_schema['required'] = required 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1757 if var_kwargs_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1758 additional_properties_schema = self.generate_inner(var_kwargs_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1759 if additional_properties_schema: 1759 ↛ 1763line 1759 didn't jump to line 1763 because the condition on line 1759 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1760 json_schema['additionalProperties'] = additional_properties_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1761 else:
1762 json_schema['additionalProperties'] = False 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1763 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1765 def p_arguments_schema( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1766 self, arguments: list[core_schema.ArgumentsParameter], var_args_schema: CoreSchema | None
1767 ) -> JsonSchemaValue:
1768 """Generates a JSON schema that matches a schema that defines a function's positional arguments.
1770 Args:
1771 arguments: The core schema.
1773 Returns:
1774 The generated JSON schema.
1775 """
1776 prefix_items: list[JsonSchemaValue] = [] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1777 min_items = 0 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1779 for argument in arguments: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1780 name = self.get_argument_name(argument) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1782 argument_schema = self.generate_inner(argument['schema']).copy() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1783 argument_schema['title'] = self.get_title_from_name(name) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1784 prefix_items.append(argument_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1786 if argument['schema']['type'] != 'default': 1786 ↛ 1779line 1786 didn't jump to line 1779 because the condition on line 1786 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1787 # This assumes that if the argument has a default value,
1788 # the inner schema must be of type WithDefaultSchema.
1789 # I believe this is true, but I am not 100% sure
1790 min_items += 1 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1792 json_schema: JsonSchemaValue = {'type': 'array'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1793 if prefix_items: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1794 json_schema['prefixItems'] = prefix_items 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1795 if min_items: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1796 json_schema['minItems'] = min_items 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1798 if var_args_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1799 items_schema = self.generate_inner(var_args_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1800 if items_schema: 1800 ↛ 1805line 1800 didn't jump to line 1805 because the condition on line 1800 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1801 json_schema['items'] = items_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1802 else:
1803 json_schema['maxItems'] = len(prefix_items) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1805 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1807 def get_argument_name(self, argument: core_schema.ArgumentsParameter) -> str: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1808 """Retrieves the name of an argument.
1810 Args:
1811 argument: The core schema.
1813 Returns:
1814 The name of the argument.
1815 """
1816 name = argument['name'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1817 if self.by_alias: 1817 ↛ 1823line 1817 didn't jump to line 1823 because the condition on line 1817 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1818 alias = argument.get('alias') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1819 if isinstance(alias, str): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1820 name = alias 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1821 else:
1822 pass # might want to do something else? 1akgwblcmhnoxypqrsdtizeufv
1823 return name 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1825 def call_schema(self, schema: core_schema.CallSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1826 """Generates a JSON schema that matches a schema that defines a function call.
1828 Args:
1829 schema: The core schema.
1831 Returns:
1832 The generated JSON schema.
1833 """
1834 return self.generate_inner(schema['arguments_schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1836 def custom_error_schema(self, schema: core_schema.CustomErrorSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1837 """Generates a JSON schema that matches a schema that defines a custom error.
1839 Args:
1840 schema: The core schema.
1842 Returns:
1843 The generated JSON schema.
1844 """
1845 return self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1847 def json_schema(self, schema: core_schema.JsonSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1848 """Generates a JSON schema that matches a schema that defines a JSON object.
1850 Args:
1851 schema: The core schema.
1853 Returns:
1854 The generated JSON schema.
1855 """
1856 content_core_schema = schema.get('schema') or core_schema.any_schema() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1857 content_json_schema = self.generate_inner(content_core_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1858 if self.mode == 'validation': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1859 return {'type': 'string', 'contentMediaType': 'application/json', 'contentSchema': content_json_schema} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1860 else:
1861 # self.mode == 'serialization'
1862 return content_json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1864 def url_schema(self, schema: core_schema.UrlSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1865 """Generates a JSON schema that matches a schema that defines a URL.
1867 Args:
1868 schema: The core schema.
1870 Returns:
1871 The generated JSON schema.
1872 """
1873 json_schema = {'type': 'string', 'format': 'uri', 'minLength': 1} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1874 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1875 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1877 def multi_host_url_schema(self, schema: core_schema.MultiHostUrlSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1878 """Generates a JSON schema that matches a schema that defines a URL that can be used with multiple hosts.
1880 Args:
1881 schema: The core schema.
1883 Returns:
1884 The generated JSON schema.
1885 """
1886 # Note: 'multi-host-uri' is a custom/pydantic-specific format, not part of the JSON Schema spec
1887 json_schema = {'type': 'string', 'format': 'multi-host-uri', 'minLength': 1} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1888 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1889 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1891 def uuid_schema(self, schema: core_schema.UuidSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1892 """Generates a JSON schema that matches a UUID.
1894 Args:
1895 schema: The core schema.
1897 Returns:
1898 The generated JSON schema.
1899 """
1900 return {'type': 'string', 'format': 'uuid'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1902 def definitions_schema(self, schema: core_schema.DefinitionsSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1903 """Generates a JSON schema that matches a schema that defines a JSON object with definitions.
1905 Args:
1906 schema: The core schema.
1908 Returns:
1909 The generated JSON schema.
1910 """
1911 for definition in schema['definitions']: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1912 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1913 self.generate_inner(definition) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1914 except PydanticInvalidForJsonSchema as e:
1915 core_ref: CoreRef = CoreRef(definition['ref']) # type: ignore
1916 self._core_defs_invalid_for_json_schema[self.get_defs_ref((core_ref, self.mode))] = e
1917 continue
1918 return self.generate_inner(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1920 def definition_ref_schema(self, schema: core_schema.DefinitionReferenceSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1921 """Generates a JSON schema that matches a schema that references a definition.
1923 Args:
1924 schema: The core schema.
1926 Returns:
1927 The generated JSON schema.
1928 """
1929 core_ref = CoreRef(schema['schema_ref']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1930 _, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1931 return ref_json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1933 def ser_schema( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1934 self, schema: core_schema.SerSchema | core_schema.IncExSeqSerSchema | core_schema.IncExDictSerSchema
1935 ) -> JsonSchemaValue | None:
1936 """Generates a JSON schema that matches a schema that defines a serialized object.
1938 Args:
1939 schema: The core schema.
1941 Returns:
1942 The generated JSON schema.
1943 """
1944 schema_type = schema['type'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1945 if schema_type == 'function-plain' or schema_type == 'function-wrap': 1945 ↛ 1950line 1945 didn't jump to line 1950 because the condition on line 1945 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1946 # PlainSerializerFunctionSerSchema or WrapSerializerFunctionSerSchema
1947 return_schema = schema.get('return_schema') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1948 if return_schema is not None: 1948 ↛ 1956line 1948 didn't jump to line 1956 because the condition on line 1948 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
1949 return self.generate_inner(return_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1950 elif schema_type == 'format' or schema_type == 'to-string':
1951 # FormatSerSchema or ToStringSerSchema
1952 return self.str_schema(core_schema.str_schema())
1953 elif schema['type'] == 'model':
1954 # ModelSerSchema
1955 return self.generate_inner(schema['schema'])
1956 return None
1958 def complex_schema(self, schema: core_schema.ComplexSchema) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1959 """Generates a JSON schema that matches a complex number.
1961 JSON has no standard way to represent complex numbers. Complex number is not a numeric
1962 type. Here we represent complex number as strings following the rule defined by Python.
1963 For instance, '1+2j' is an accepted complex string. Details can be found in
1964 [Python's `complex` documentation][complex].
1966 Args:
1967 schema: The core schema.
1969 Returns:
1970 The generated JSON schema.
1971 """
1972 return {'type': 'string'} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1974 # ### Utility methods
1976 def get_title_from_name(self, name: str) -> str: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1977 """Retrieves a title from a name.
1979 Args:
1980 name: The name to retrieve a title from.
1982 Returns:
1983 The title.
1984 """
1985 return name.title().replace('_', ' ').strip() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
1987 def field_title_should_be_set(self, schema: CoreSchemaOrField) -> bool: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
1988 """Returns true if a field with the given schema should have a title set based on the field name.
1990 Intuitively, we want this to return true for schemas that wouldn't otherwise provide their own title
1991 (e.g., int, float, str), and false for those that would (e.g., BaseModel subclasses).
1993 Args:
1994 schema: The schema to check.
1996 Returns:
1997 `True` if the field should have a title set, `False` otherwise.
1998 """
1999 if _core_utils.is_core_schema_field(schema): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2000 if schema['type'] == 'computed-field': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2001 field_schema = schema['return_schema'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2002 else:
2003 field_schema = schema['schema'] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2004 return self.field_title_should_be_set(field_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2006 elif _core_utils.is_core_schema(schema): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2007 if schema.get('ref'): # things with refs, such as models and enums, should not have titles set 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2008 return False 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2009 if schema['type'] in {'default', 'nullable', 'definitions'}: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2010 return self.field_title_should_be_set(schema['schema']) # type: ignore[typeddict-item] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2011 if _core_utils.is_function_with_inner_schema(schema): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2012 return self.field_title_should_be_set(schema['schema']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2013 if schema['type'] == 'definition-ref': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2014 # Referenced schemas should not have titles set for the same reason
2015 # schemas with refs should not
2016 return False 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2017 return True # anything else should have title set 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2019 else:
2020 raise PydanticInvalidForJsonSchema(f'Unexpected schema type: schema={schema}') # pragma: no cover
2022 def normalize_name(self, name: str) -> str: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2023 """Normalizes a name to be used as a key in a dictionary.
2025 Args:
2026 name: The name to normalize.
2028 Returns:
2029 The normalized name.
2030 """
2031 return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name).replace('.', '__') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2033 def get_defs_ref(self, core_mode_ref: CoreModeRef) -> DefsRef: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2034 """Override this method to change the way that definitions keys are generated from a core reference.
2036 Args:
2037 core_mode_ref: The core reference.
2039 Returns:
2040 The definitions key.
2041 """
2042 # Split the core ref into "components"; generic origins and arguments are each separate components
2043 core_ref, mode = core_mode_ref 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2044 components = re.split(r'([\][,])', core_ref) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2045 # Remove IDs from each component
2046 components = [x.rsplit(':', 1)[0] for x in components] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2047 core_ref_no_id = ''.join(components) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2048 # Remove everything before the last period from each "component"
2049 components = [re.sub(r'(?:[^.[\]]+\.)+((?:[^.[\]]+))', r'\1', x) for x in components] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2050 short_ref = ''.join(components) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2052 mode_title = _MODE_TITLE_MAPPING[mode] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2054 # It is important that the generated defs_ref values be such that at least one choice will not
2055 # be generated for any other core_ref. Currently, this should be the case because we include
2056 # the id of the source type in the core_ref
2057 name = DefsRef(self.normalize_name(short_ref)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2058 name_mode = DefsRef(self.normalize_name(short_ref) + f'-{mode_title}') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2059 module_qualname = DefsRef(self.normalize_name(core_ref_no_id)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2060 module_qualname_mode = DefsRef(f'{module_qualname}-{mode_title}') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2061 module_qualname_id = DefsRef(self.normalize_name(core_ref)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2062 occurrence_index = self._collision_index.get(module_qualname_id) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2063 if occurrence_index is None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2064 self._collision_counter[module_qualname] += 1 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2065 occurrence_index = self._collision_index[module_qualname_id] = self._collision_counter[module_qualname] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2067 module_qualname_occurrence = DefsRef(f'{module_qualname}__{occurrence_index}') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2068 module_qualname_occurrence_mode = DefsRef(f'{module_qualname_mode}__{occurrence_index}') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2070 self._prioritized_defsref_choices[module_qualname_occurrence_mode] = [ 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2071 name,
2072 name_mode,
2073 module_qualname,
2074 module_qualname_mode,
2075 module_qualname_occurrence,
2076 module_qualname_occurrence_mode,
2077 ]
2079 return module_qualname_occurrence_mode 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2081 def get_cache_defs_ref_schema(self, core_ref: CoreRef) -> tuple[DefsRef, JsonSchemaValue]: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2082 """This method wraps the get_defs_ref method with some cache-lookup/population logic,
2083 and returns both the produced defs_ref and the JSON schema that will refer to the right definition.
2085 Args:
2086 core_ref: The core reference to get the definitions reference for.
2088 Returns:
2089 A tuple of the definitions reference and the JSON schema that will refer to it.
2090 """
2091 core_mode_ref = (core_ref, self.mode) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2092 maybe_defs_ref = self.core_to_defs_refs.get(core_mode_ref) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2093 if maybe_defs_ref is not None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2094 json_ref = self.core_to_json_refs[core_mode_ref] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2095 return maybe_defs_ref, {'$ref': json_ref} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2097 defs_ref = self.get_defs_ref(core_mode_ref) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2099 # populate the ref translation mappings
2100 self.core_to_defs_refs[core_mode_ref] = defs_ref 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2101 self.defs_to_core_refs[defs_ref] = core_mode_ref 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2103 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2104 self.core_to_json_refs[core_mode_ref] = json_ref 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2105 self.json_to_defs_refs[json_ref] = defs_ref 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2106 ref_json_schema = {'$ref': json_ref} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2107 return defs_ref, ref_json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2109 def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2110 """Remove any sibling keys that are redundant with the referenced schema.
2112 Args:
2113 json_schema: The schema to remove redundant sibling keys from.
2115 Returns:
2116 The schema with redundant sibling keys removed.
2117 """
2118 if '$ref' in json_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2119 # prevent modifications to the input; this copy may be safe to drop if there is significant overhead
2120 json_schema = json_schema.copy() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2122 referenced_json_schema = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2123 if referenced_json_schema is None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2124 # This can happen when building schemas for models with not-yet-defined references.
2125 # It may be a good idea to do a recursive pass at the end of the generation to remove
2126 # any redundant override keys.
2127 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2128 for k, v in list(json_schema.items()): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2129 if k == '$ref': 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2130 continue 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2131 if k in referenced_json_schema and referenced_json_schema[k] == v: 2131 ↛ 2132line 2131 didn't jump to line 2132 because the condition on line 2131 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2132 del json_schema[k] # redundant key
2134 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2136 def get_schema_from_definitions(self, json_ref: JsonRef) -> JsonSchemaValue | None: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2137 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2138 def_ref = self.json_to_defs_refs[json_ref] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2139 if def_ref in self._core_defs_invalid_for_json_schema: 2139 ↛ 2140line 2139 didn't jump to line 2140 because the condition on line 2139 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2140 raise self._core_defs_invalid_for_json_schema[def_ref]
2141 return self.definitions.get(def_ref, None) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2142 except KeyError: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2143 if json_ref.startswith(('http://', 'https://')): 2143 ↛ 2145line 2143 didn't jump to line 2145 because the condition on line 2143 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2144 return None 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2145 raise
2147 def encode_default(self, dft: Any) -> Any: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2148 """Encode a default value to a JSON-serializable value.
2150 This is used to encode default values for fields in the generated JSON schema.
2152 Args:
2153 dft: The default value to encode.
2155 Returns:
2156 The encoded default value.
2157 """
2158 from .type_adapter import TypeAdapter, _type_has_config 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2160 config = self._config 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2161 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2162 default = ( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2163 dft
2164 if _type_has_config(type(dft))
2165 else TypeAdapter(type(dft), config=config.config_dict).dump_python(dft, mode='json')
2166 )
2167 except PydanticSchemaGenerationError: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2168 raise pydantic_core.PydanticSerializationError(f'Unable to encode default value {dft}') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2170 return pydantic_core.to_jsonable_python( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2171 default,
2172 timedelta_mode=config.ser_json_timedelta,
2173 bytes_mode=config.ser_json_bytes,
2174 )
2176 def update_with_validations( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2177 self, json_schema: JsonSchemaValue, core_schema: CoreSchema, mapping: dict[str, str]
2178 ) -> None:
2179 """Update the json_schema with the corresponding validations specified in the core_schema,
2180 using the provided mapping to translate keys in core_schema to the appropriate keys for a JSON schema.
2182 Args:
2183 json_schema: The JSON schema to update.
2184 core_schema: The core schema to get the validations from.
2185 mapping: A mapping from core_schema attribute names to the corresponding JSON schema attribute names.
2186 """
2187 for core_key, json_schema_key in mapping.items(): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2188 if core_key in core_schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2189 json_schema[json_schema_key] = core_schema[core_key] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2191 class ValidationsMapping: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2192 """This class just contains mappings from core_schema attribute names to the corresponding
2193 JSON schema attribute names. While I suspect it is unlikely to be necessary, you can in
2194 principle override this class in a subclass of GenerateJsonSchema (by inheriting from
2195 GenerateJsonSchema.ValidationsMapping) to change these mappings.
2196 """
2198 numeric = { 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2199 'multiple_of': 'multipleOf',
2200 'le': 'maximum',
2201 'ge': 'minimum',
2202 'lt': 'exclusiveMaximum',
2203 'gt': 'exclusiveMinimum',
2204 }
2205 bytes = { 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2206 'min_length': 'minLength',
2207 'max_length': 'maxLength',
2208 }
2209 string = { 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2210 'min_length': 'minLength',
2211 'max_length': 'maxLength',
2212 'pattern': 'pattern',
2213 }
2214 array = { 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2215 'min_length': 'minItems',
2216 'max_length': 'maxItems',
2217 }
2218 object = { 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2219 'min_length': 'minProperties',
2220 'max_length': 'maxProperties',
2221 }
2223 def get_flattened_anyof(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2224 members = [] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2225 for schema in schemas: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2226 if len(schema) == 1 and 'anyOf' in schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2227 members.extend(schema['anyOf']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2228 else:
2229 members.append(schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2230 members = _deduplicate_schemas(members) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2231 if len(members) == 1: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2232 return members[0] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2233 return {'anyOf': members} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2235 def get_json_ref_counts(self, json_schema: JsonSchemaValue) -> dict[JsonRef, int]: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2236 """Get all values corresponding to the key '$ref' anywhere in the json_schema."""
2237 json_refs: dict[JsonRef, int] = Counter() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2239 def _add_json_refs(schema: Any) -> None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2240 if isinstance(schema, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2241 if '$ref' in schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2242 json_ref = JsonRef(schema['$ref']) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2243 if not isinstance(json_ref, str): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2244 return # in this case, '$ref' might have been the name of a property 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2245 already_visited = json_ref in json_refs 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2246 json_refs[json_ref] += 1 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2247 if already_visited: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2248 return # prevent recursion on a definition that was already visited 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2249 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2250 defs_ref = self.json_to_defs_refs[json_ref] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2251 if defs_ref in self._core_defs_invalid_for_json_schema: 2251 ↛ 2252line 2251 didn't jump to line 2252 because the condition on line 2251 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2252 raise self._core_defs_invalid_for_json_schema[defs_ref]
2253 _add_json_refs(self.definitions[defs_ref]) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2254 except KeyError: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2255 if not json_ref.startswith(('http://', 'https://')): 2255 ↛ 2256line 2255 didn't jump to line 2256 because the condition on line 2255 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2256 raise
2258 for k, v in schema.items(): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2259 if k == 'examples' and isinstance(v, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2260 # Skip examples that may contain arbitrary values and references
2261 # (see the comment in `_get_all_json_refs` for more details).
2262 continue 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2263 _add_json_refs(v) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2264 elif isinstance(schema, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2265 for v in schema: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2266 _add_json_refs(v) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2268 _add_json_refs(json_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2269 return json_refs 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2271 def handle_invalid_for_json_schema(self, schema: CoreSchemaOrField, error_info: str) -> JsonSchemaValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2272 raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2274 def emit_warning(self, kind: JsonSchemaWarningKind, detail: str) -> None: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2275 """This method simply emits PydanticJsonSchemaWarnings based on handling in the `warning_message` method."""
2276 message = self.render_warning_message(kind, detail) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2277 if message is not None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2278 warnings.warn(message, PydanticJsonSchemaWarning) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2280 def render_warning_message(self, kind: JsonSchemaWarningKind, detail: str) -> str | None: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2281 """This method is responsible for ignoring warnings as desired, and for formatting the warning messages.
2283 You can override the value of `ignored_warning_kinds` in a subclass of GenerateJsonSchema
2284 to modify what warnings are generated. If you want more control, you can override this method;
2285 just return None in situations where you don't want warnings to be emitted.
2287 Args:
2288 kind: The kind of warning to render. It can be one of the following:
2290 - 'skipped-choice': A choice field was skipped because it had no valid choices.
2291 - 'non-serializable-default': A default value was skipped because it was not JSON-serializable.
2292 detail: A string with additional details about the warning.
2294 Returns:
2295 The formatted warning message, or `None` if no warning should be emitted.
2296 """
2297 if kind in self.ignored_warning_kinds: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2298 return None 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2299 return f'{detail} [{kind}]' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2301 def _build_definitions_remapping(self) -> _DefinitionsRemapping: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2302 defs_to_json: dict[DefsRef, JsonRef] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2303 for defs_refs in self._prioritized_defsref_choices.values(): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2304 for defs_ref in defs_refs: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2305 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2306 defs_to_json[defs_ref] = json_ref 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2308 return _DefinitionsRemapping.from_prioritized_choices( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2309 self._prioritized_defsref_choices, defs_to_json, self.definitions
2310 )
2312 def _garbage_collect_definitions(self, schema: JsonSchemaValue) -> None: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2313 visited_defs_refs: set[DefsRef] = set() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2314 unvisited_json_refs = _get_all_json_refs(schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2315 while unvisited_json_refs: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2316 next_json_ref = unvisited_json_refs.pop() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2317 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2318 next_defs_ref = self.json_to_defs_refs[next_json_ref] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2319 if next_defs_ref in visited_defs_refs: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2320 continue 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2321 visited_defs_refs.add(next_defs_ref) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2322 unvisited_json_refs.update(_get_all_json_refs(self.definitions[next_defs_ref])) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2323 except KeyError: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2324 if not next_json_ref.startswith(('http://', 'https://')): 2324 ↛ 2325line 2324 didn't jump to line 2325 because the condition on line 2324 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2325 raise
2327 self.definitions = {k: v for k, v in self.definitions.items() if k in visited_defs_refs} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2330# ##### Start JSON Schema Generation Functions #####
2333def model_json_schema( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2334 cls: type[BaseModel] | type[PydanticDataclass],
2335 by_alias: bool = True,
2336 ref_template: str = DEFAULT_REF_TEMPLATE,
2337 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
2338 mode: JsonSchemaMode = 'validation',
2339) -> dict[str, Any]:
2340 """Utility function to generate a JSON Schema for a model.
2342 Args:
2343 cls: The model class to generate a JSON Schema for.
2344 by_alias: If `True` (the default), fields will be serialized according to their alias.
2345 If `False`, fields will be serialized according to their attribute name.
2346 ref_template: The template to use for generating JSON Schema references.
2347 schema_generator: The class to use for generating the JSON Schema.
2348 mode: The mode to use for generating the JSON Schema. It can be one of the following:
2350 - 'validation': Generate a JSON Schema for validating data.
2351 - 'serialization': Generate a JSON Schema for serializing data.
2353 Returns:
2354 The generated JSON Schema.
2355 """
2356 from .main import BaseModel 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2358 schema_generator_instance = schema_generator(by_alias=by_alias, ref_template=ref_template) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2360 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2361 cls.__pydantic_core_schema__.rebuild() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2363 if cls is BaseModel: 2363 ↛ 2364line 2363 didn't jump to line 2364 because the condition on line 2363 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2364 raise AttributeError('model_json_schema() must be called on a subclass of BaseModel, not BaseModel itself.')
2366 assert not isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it' 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2367 return schema_generator_instance.generate(cls.__pydantic_core_schema__, mode=mode) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2370def models_json_schema( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2371 models: Sequence[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode]],
2372 *,
2373 by_alias: bool = True,
2374 title: str | None = None,
2375 description: str | None = None,
2376 ref_template: str = DEFAULT_REF_TEMPLATE,
2377 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
2378) -> tuple[dict[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]:
2379 """Utility function to generate a JSON Schema for multiple models.
2381 Args:
2382 models: A sequence of tuples of the form (model, mode).
2383 by_alias: Whether field aliases should be used as keys in the generated JSON Schema.
2384 title: The title of the generated JSON Schema.
2385 description: The description of the generated JSON Schema.
2386 ref_template: The reference template to use for generating JSON Schema references.
2387 schema_generator: The schema generator to use for generating the JSON Schema.
2389 Returns:
2390 A tuple where:
2391 - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
2392 whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
2393 JsonRef references to definitions that are defined in the second returned element.)
2394 - The second element is a JSON schema containing all definitions referenced in the first returned
2395 element, along with the optional title and description keys.
2396 """
2397 for cls, _ in models: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2398 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 2398 ↛ 2399line 2398 didn't jump to line 2399 because the condition on line 2398 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2399 cls.__pydantic_core_schema__.rebuild()
2401 instance = schema_generator(by_alias=by_alias, ref_template=ref_template) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2402 inputs: list[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode, CoreSchema]] = [ 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2403 (m, mode, m.__pydantic_core_schema__) for m, mode in models
2404 ]
2405 json_schemas_map, definitions = instance.generate_definitions(inputs) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2407 json_schema: dict[str, Any] = {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2408 if definitions: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2409 json_schema['$defs'] = definitions 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2410 if title: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2411 json_schema['title'] = title 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2412 if description: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2413 json_schema['description'] = description 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2415 return json_schemas_map, json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2418# ##### End JSON Schema Generation Functions #####
2421_HashableJsonValue: TypeAlias = Union[ 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2422 int, float, str, bool, None, tuple['_HashableJsonValue', ...], tuple[tuple[str, '_HashableJsonValue'], ...]
2423]
2426def _deduplicate_schemas(schemas: Iterable[JsonDict]) -> list[JsonDict]: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2427 return list({_make_json_hashable(schema): schema for schema in schemas}.values()) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2430def _make_json_hashable(value: JsonValue) -> _HashableJsonValue: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2431 if isinstance(value, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2432 return tuple(sorted((k, _make_json_hashable(v)) for k, v in value.items())) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2433 elif isinstance(value, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2434 return tuple(_make_json_hashable(v) for v in value) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2435 else:
2436 return value 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2439@dataclasses.dataclass(**_internal_dataclass.slots_true) 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2440class WithJsonSchema: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2441 """!!! abstract "Usage Documentation"
2442 [`WithJsonSchema` Annotation](../concepts/json_schema.md#withjsonschema-annotation)
2444 Add this as an annotation on a field to override the (base) JSON schema that would be generated for that field.
2445 This provides a way to set a JSON schema for types that would otherwise raise errors when producing a JSON schema,
2446 such as Callable, or types that have an is-instance core schema, without needing to go so far as creating a
2447 custom subclass of pydantic.json_schema.GenerateJsonSchema.
2448 Note that any _modifications_ to the schema that would normally be made (such as setting the title for model fields)
2449 will still be performed.
2451 If `mode` is set this will only apply to that schema generation mode, allowing you
2452 to set different json schemas for validation and serialization.
2453 """
2455 json_schema: JsonSchemaValue | None 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2456 mode: Literal['validation', 'serialization'] | None = None 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2458 def __get_pydantic_json_schema__( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2459 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2460 ) -> JsonSchemaValue:
2461 mode = self.mode or handler.mode 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2462 if mode != handler.mode: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2463 return handler(core_schema) 1jagbcAdief
2464 if self.json_schema is None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2465 # This exception is handled in pydantic.json_schema.GenerateJsonSchema._named_required_fields_schema
2466 raise PydanticOmit 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2467 else:
2468 return self.json_schema.copy() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2470 def __hash__(self) -> int: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2471 return hash(type(self.mode)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2474class Examples: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2475 """Add examples to a JSON schema.
2477 If the JSON Schema already contains examples, the provided examples
2478 will be appended.
2480 If `mode` is set this will only apply to that schema generation mode,
2481 allowing you to add different examples for validation and serialization.
2482 """
2484 @overload 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2485 @deprecated('Using a dict for `examples` is deprecated since v2.9 and will be removed in v3.0. Use a list instead.') 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2486 def __init__( 1jBakgwblcmDEnoxypqrsMKLGHIJAFdtizeufv
2487 self, examples: dict[str, Any], mode: Literal['validation', 'serialization'] | None = None 1akgwblcmChnoxypqrsKLGHIJdtizeufv
2488 ) -> None: ... 1blcmChpqrsGHIJeufv
2490 @overload 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2491 def __init__(self, examples: list[Any], mode: Literal['validation', 'serialization'] | None = None) -> None: ... 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2493 def __init__( 1jBakgwblcmDEnoxypqrsMKLGHIJAFdtizeufv
2494 self, examples: dict[str, Any] | list[Any], mode: Literal['validation', 'serialization'] | None = None
2495 ) -> None:
2496 if isinstance(examples, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2497 warnings.warn( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2498 'Using a dict for `examples` is deprecated, use a list instead.',
2499 PydanticDeprecatedSince29,
2500 stacklevel=2,
2501 )
2502 self.examples = examples 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2503 self.mode = mode 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2505 def __get_pydantic_json_schema__( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2506 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2507 ) -> JsonSchemaValue:
2508 mode = self.mode or handler.mode 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2509 json_schema = handler(core_schema) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2510 if mode != handler.mode: 2510 ↛ 2511line 2510 didn't jump to line 2511 because the condition on line 2510 was never true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2511 return json_schema
2512 examples = json_schema.get('examples') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2513 if examples is None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2514 json_schema['examples'] = to_jsonable_python(self.examples) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2515 if isinstance(examples, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2516 if isinstance(self.examples, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2517 warnings.warn( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2518 'Updating existing JSON Schema examples of type dict with examples of type list. '
2519 'Only the existing examples values will be retained. Note that dict support for '
2520 'examples is deprecated and will be removed in v3.0.',
2521 UserWarning,
2522 )
2523 json_schema['examples'] = to_jsonable_python( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2524 [ex for value in examples.values() for ex in value] + self.examples
2525 )
2526 else:
2527 json_schema['examples'] = to_jsonable_python({**examples, **self.examples}) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2528 if isinstance(examples, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2529 if isinstance(self.examples, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2530 json_schema['examples'] = to_jsonable_python(examples + self.examples) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2531 elif isinstance(self.examples, dict): 2531 ↛ 2542line 2531 didn't jump to line 2542 because the condition on line 2531 was always true1jBakgwblcmChDEnoxypqrsAFdtizeufv
2532 warnings.warn( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2533 'Updating existing JSON Schema examples of type list with examples of type dict. '
2534 'Only the examples values will be retained. Note that dict support for '
2535 'examples is deprecated and will be removed in v3.0.',
2536 UserWarning,
2537 )
2538 json_schema['examples'] = to_jsonable_python( 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2539 examples + [ex for value in self.examples.values() for ex in value]
2540 )
2542 return json_schema 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2544 def __hash__(self) -> int: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2545 return hash(type(self.mode)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2548def _get_all_json_refs(item: Any) -> set[JsonRef]: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2549 """Get all the definitions references from a JSON schema."""
2550 refs: set[JsonRef] = set() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2551 stack = [item] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2553 while stack: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2554 current = stack.pop() 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2555 if isinstance(current, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2556 for key, value in current.items(): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2557 if key == 'examples' and isinstance(value, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2558 # Skip examples that may contain arbitrary values and references
2559 # (e.g. `{"examples": [{"$ref": "..."}]}`). Note: checking for value
2560 # of type list is necessary to avoid skipping valid portions of the schema,
2561 # for instance when "examples" is used as a property key. A more robust solution
2562 # could be found, but would require more advanced JSON Schema parsing logic.
2563 continue 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2564 if key == '$ref' and isinstance(value, str): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2565 refs.add(JsonRef(value)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2566 elif isinstance(value, dict): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2567 stack.append(value) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2568 elif isinstance(value, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2569 stack.extend(value) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2570 elif isinstance(current, list): 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2571 stack.extend(current) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2573 return refs 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2576AnyType = TypeVar('AnyType') 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2578if TYPE_CHECKING: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2579 SkipJsonSchema = Annotated[AnyType, ...]
2580else:
2582 @dataclasses.dataclass(**_internal_dataclass.slots_true) 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2583 class SkipJsonSchema: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2584 """!!! abstract "Usage Documentation"
2585 [`SkipJsonSchema` Annotation](../concepts/json_schema.md#skipjsonschema-annotation)
2587 Add this as an annotation on a field to skip generating a JSON schema for that field.
2589 Example:
2590 ```python
2591 from pprint import pprint
2592 from typing import Union
2594 from pydantic import BaseModel
2595 from pydantic.json_schema import SkipJsonSchema
2597 class Model(BaseModel):
2598 a: Union[int, None] = None # (1)!
2599 b: Union[int, SkipJsonSchema[None]] = None # (2)!
2600 c: SkipJsonSchema[Union[int, None]] = None # (3)!
2602 pprint(Model.model_json_schema())
2603 '''
2604 {
2605 'properties': {
2606 'a': {
2607 'anyOf': [
2608 {'type': 'integer'},
2609 {'type': 'null'}
2610 ],
2611 'default': None,
2612 'title': 'A'
2613 },
2614 'b': {
2615 'default': None,
2616 'title': 'B',
2617 'type': 'integer'
2618 }
2619 },
2620 'title': 'Model',
2621 'type': 'object'
2622 }
2623 '''
2624 ```
2626 1. The integer and null types are both included in the schema for `a`.
2627 2. The integer type is the only type included in the schema for `b`.
2628 3. The entirety of the `c` field is omitted from the schema.
2629 """
2631 def __class_getitem__(cls, item: AnyType) -> AnyType: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2632 return Annotated[item, cls()] 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2634 def __get_pydantic_json_schema__( 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2635 self, core_schema: CoreSchema, handler: GetJsonSchemaHandler
2636 ) -> JsonSchemaValue:
2637 raise PydanticOmit 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2639 def __hash__(self) -> int: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2640 return hash(type(self)) 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2643def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict: 1jBakgwblcmChDEnoxypqrsMKLGHIJAFdtizeufv
2644 if cls is not None: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2645 try: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2646 return _decorators.get_attribute_from_bases(cls, '__pydantic_config__') 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2647 except AttributeError: 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2648 pass 1jBakgwblcmChDEnoxypqrsAFdtizeufv
2649 return {} 1jBakgwblcmChDEnoxypqrsAFdtizeufv