Coverage for pydantic/json_schema.py: 94.66%
1107 statements
« prev ^ index » next coverage.py v7.10.0, created at 2025-07-26 11:49 +0000
« prev ^ index » next coverage.py v7.10.0, created at 2025-07-26 11:49 +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 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
14import dataclasses 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
15import inspect 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
16import math 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
17import os 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
18import re 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
19import warnings 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
20from collections import Counter, defaultdict 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
21from collections.abc import Hashable, Iterable, Sequence 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
22from copy import deepcopy 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
23from enum import Enum 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
24from re import Pattern 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
25from typing import ( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
39from pydantic_core import MISSING, CoreSchema, PydanticOmit, core_schema, to_jsonable_python 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
40from pydantic_core.core_schema import ComputedField 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
41from typing_extensions import TypeAlias, assert_never, deprecated, final 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
42from typing_inspection.introspection import get_literal_values 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
44from pydantic.warnings import PydanticDeprecatedSince26, PydanticDeprecatedSince29 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
46from ._internal import ( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
47 _config,
48 _core_metadata,
49 _core_utils,
50 _decorators,
51 _internal_dataclass,
52 _mock_val_ser,
53 _schema_generation_shared,
54)
55from .annotated_handlers import GetJsonSchemaHandler 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
56from .config import JsonDict, JsonValue 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
57from .errors import PydanticInvalidForJsonSchema, PydanticSchemaGenerationError, PydanticUserError 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
59if TYPE_CHECKING: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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] 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
68""" 1bdenfogpADEahiqrstuvGHIPcjkwlxmyBJK
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] 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
75""" 1bdenfogpADEahiqrstuvGHIPcjkwlxmyBJK
76A type alias for a JSON schema value. This is a dictionary of string keys to arbitrary JSON values.
77"""
79JsonSchemaMode = Literal['validation', 'serialization'] 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
80""" 1bdenfogpADEahiqrstuvGHIPcjkwlxmyBJK
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'} 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
92JsonSchemaWarningKind = Literal['skipped-choice', 'non-serializable-default', 'skipped-discriminator'] 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
93""" 1bdenfogpADEahiqrstuvGHIPcjkwlxmyBJK
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): 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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 """
109NoDefault = object() 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
110"""A sentinel value used to indicate that no default value should be used when generating a JSON Schema 1bdenfogpADEahiqrstuvGHIPcjkwlxmyBJK
111for a core schema with a default value.
112"""
115# ##### JSON Schema Generation #####
116DEFAULT_REF_TEMPLATE = '#/$defs/{model}' 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
117"""The default format string used to generate reference names.""" 1bdenfogpADEahiqrstuvGHIPcjkwlxmyBJK
119# There are three types of references relevant to building JSON schemas:
120# 1. core_schema "ref" values; these are not exposed as part of the JSON schema
121# * these might look like the fully qualified path of a model, its id, or something similar
122CoreRef = NewType('CoreRef', str) 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
123# 2. keys of the "definitions" object that will eventually go into the JSON schema
124# * by default, these look like "MyModel", though may change in the presence of collisions
125# * eventually, we may want to make it easier to modify the way these names are generated
126DefsRef = NewType('DefsRef', str) 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
127# 3. the values corresponding to the "$ref" key in the schema
128# * By default, these look like "#/$defs/MyModel", as in {"$ref": "#/$defs/MyModel"}
129JsonRef = NewType('JsonRef', str) 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
131CoreModeRef = tuple[CoreRef, JsonSchemaMode] 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
132JsonSchemaKeyT = TypeVar('JsonSchemaKeyT', bound=Hashable) 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
135@dataclasses.dataclass(**_internal_dataclass.slots_true) 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
136class _DefinitionsRemapping: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
137 defs_remapping: dict[DefsRef, DefsRef] 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
138 json_remapping: dict[JsonRef, JsonRef] 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
140 @staticmethod 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
141 def from_prioritized_choices( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
142 prioritized_choices: dict[DefsRef, list[DefsRef]],
143 defs_to_json: dict[DefsRef, JsonRef],
144 definitions: dict[DefsRef, JsonSchemaValue],
145 ) -> _DefinitionsRemapping:
146 """
147 This function should produce a remapping that replaces complex DefsRef with the simpler ones from the
148 prioritized_choices such that applying the name remapping would result in an equivalent JSON schema.
149 """
150 # We need to iteratively simplify the definitions until we reach a fixed point.
151 # The reason for this is that outer definitions may reference inner definitions that get simplified
152 # into an equivalent reference, and the outer definitions won't be equivalent until we've simplified
153 # the inner definitions.
154 copied_definitions = deepcopy(definitions) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
155 definitions_schema = {'$defs': copied_definitions} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
156 for _iter in range(100): # prevent an infinite loop in the case of a bug, 100 iterations should be enough 156 ↛ 185line 156 didn't jump to line 185 because the loop on line 156 didn't complete1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
157 # For every possible remapped DefsRef, collect all schemas that that DefsRef might be used for:
158 schemas_for_alternatives: dict[DefsRef, list[JsonSchemaValue]] = defaultdict(list) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
159 for defs_ref in copied_definitions: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
160 alternatives = prioritized_choices[defs_ref] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
161 for alternative in alternatives: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
162 schemas_for_alternatives[alternative].append(copied_definitions[defs_ref]) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
164 # Deduplicate the schemas for each alternative; the idea is that we only want to remap to a new DefsRef
165 # if it introduces no ambiguity, i.e., there is only one distinct schema for that DefsRef.
166 for defs_ref in schemas_for_alternatives: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
167 schemas_for_alternatives[defs_ref] = _deduplicate_schemas(schemas_for_alternatives[defs_ref]) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
169 # Build the remapping
170 defs_remapping: dict[DefsRef, DefsRef] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
171 json_remapping: dict[JsonRef, JsonRef] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
172 for original_defs_ref in definitions: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
173 alternatives = prioritized_choices[original_defs_ref] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
174 # Pick the first alternative that has only one schema, since that means there is no collision
175 remapped_defs_ref = next(x for x in alternatives if len(schemas_for_alternatives[x]) == 1) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
176 defs_remapping[original_defs_ref] = remapped_defs_ref 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
177 json_remapping[defs_to_json[original_defs_ref]] = defs_to_json[remapped_defs_ref] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
178 remapping = _DefinitionsRemapping(defs_remapping, json_remapping) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
179 new_definitions_schema = remapping.remap_json_schema({'$defs': copied_definitions}) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
180 if definitions_schema == new_definitions_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
181 # We've reached the fixed point
182 return remapping 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
183 definitions_schema = new_definitions_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
185 raise PydanticInvalidForJsonSchema('Failed to simplify the JSON schema definitions')
187 def remap_defs_ref(self, ref: DefsRef) -> DefsRef: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
188 return self.defs_remapping.get(ref, ref) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
190 def remap_json_ref(self, ref: JsonRef) -> JsonRef: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
191 return self.json_remapping.get(ref, ref) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
193 def remap_json_schema(self, schema: Any) -> Any: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
194 """
195 Recursively update the JSON schema replacing all $refs
196 """
197 if isinstance(schema, str): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
198 # Note: this may not really be a JsonRef; we rely on having no collisions between JsonRefs and other strings
199 return self.remap_json_ref(JsonRef(schema)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
200 elif isinstance(schema, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
201 return [self.remap_json_schema(item) for item in schema] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
202 elif isinstance(schema, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
203 for key, value in schema.items(): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
204 if key == '$ref' and isinstance(value, str): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
205 schema['$ref'] = self.remap_json_ref(JsonRef(value)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
206 elif key == '$defs': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
207 schema['$defs'] = { 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
208 self.remap_defs_ref(DefsRef(key)): self.remap_json_schema(value)
209 for key, value in schema['$defs'].items()
210 }
211 else:
212 schema[key] = self.remap_json_schema(value) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
213 return schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
216class GenerateJsonSchema: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
217 """!!! abstract "Usage Documentation"
218 [Customizing the JSON Schema Generation Process](../concepts/json_schema.md#customizing-the-json-schema-generation-process)
220 A class for generating JSON schemas.
222 This class generates JSON schemas based on configured parameters. The default schema dialect
223 is [https://json-schema.org/draft/2020-12/schema](https://json-schema.org/draft/2020-12/schema).
224 The class uses `by_alias` to configure how fields with
225 multiple names are handled and `ref_template` to format reference names.
227 Attributes:
228 schema_dialect: The JSON schema dialect used to generate the schema. See
229 [Declaring a Dialect](https://json-schema.org/understanding-json-schema/reference/schema.html#id4)
230 in the JSON Schema documentation for more information about dialects.
231 ignored_warning_kinds: Warnings to ignore when generating the schema. `self.render_warning_message` will
232 do nothing if its argument `kind` is in `ignored_warning_kinds`;
233 this value can be modified on subclasses to easily control which warnings are emitted.
234 by_alias: Whether to use field aliases when generating the schema.
235 ref_template: The format string used when generating reference names.
236 core_to_json_refs: A mapping of core refs to JSON refs.
237 core_to_defs_refs: A mapping of core refs to definition refs.
238 defs_to_core_refs: A mapping of definition refs to core refs.
239 json_to_defs_refs: A mapping of JSON refs to definition refs.
240 definitions: Definitions in the schema.
242 Args:
243 by_alias: Whether to use field aliases in the generated schemas.
244 ref_template: The format string to use when generating reference names.
246 Raises:
247 JsonSchemaError: If the instance of the class is inadvertently reused after generating a schema.
248 """
250 schema_dialect = 'https://json-schema.org/draft/2020-12/schema' 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
252 # `self.render_warning_message` will do nothing if its argument `kind` is in `ignored_warning_kinds`;
253 # this value can be modified on subclasses to easily control which warnings are emitted
254 ignored_warning_kinds: set[JsonSchemaWarningKind] = {'skipped-choice'} 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
256 def __init__(self, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE): 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
257 self.by_alias = by_alias 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
258 self.ref_template = ref_template 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
260 self.core_to_json_refs: dict[CoreModeRef, JsonRef] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
261 self.core_to_defs_refs: dict[CoreModeRef, DefsRef] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
262 self.defs_to_core_refs: dict[DefsRef, CoreModeRef] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
263 self.json_to_defs_refs: dict[JsonRef, DefsRef] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
265 self.definitions: dict[DefsRef, JsonSchemaValue] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
266 self._config_wrapper_stack = _config.ConfigWrapperStack(_config.ConfigWrapper({})) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
268 self._mode: JsonSchemaMode = 'validation' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
270 # The following includes a mapping of a fully-unique defs ref choice to a list of preferred
271 # alternatives, which are generally simpler, such as only including the class name.
272 # At the end of schema generation, we use these to produce a JSON schema with more human-readable
273 # definitions, which would also work better in a generated OpenAPI client, etc.
274 self._prioritized_defsref_choices: dict[DefsRef, list[DefsRef]] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
275 self._collision_counter: dict[str, int] = defaultdict(int) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
276 self._collision_index: dict[str, int] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
278 self._schema_type_to_method = self.build_schema_type_to_method() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
280 # When we encounter definitions we need to try to build them immediately
281 # so that they are available schemas that reference them
282 # But it's possible that CoreSchema was never going to be used
283 # (e.g. because the CoreSchema that references short circuits is JSON schema generation without needing
284 # the reference) so instead of failing altogether if we can't build a definition we
285 # store the error raised and re-throw it if we end up needing that def
286 self._core_defs_invalid_for_json_schema: dict[DefsRef, PydanticInvalidForJsonSchema] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
288 # This changes to True after generating a schema, to prevent issues caused by accidental reuse
289 # of a single instance of a schema generator
290 self._used = False 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
292 @property 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
293 def _config(self) -> _config.ConfigWrapper: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
294 return self._config_wrapper_stack.tail 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
296 @property 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
297 def mode(self) -> JsonSchemaMode: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
298 if self._config.json_schema_mode_override is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
299 return self._config.json_schema_mode_override 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
300 else:
301 return self._mode 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
303 def build_schema_type_to_method( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
304 self,
305 ) -> dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]]:
306 """Builds a dictionary mapping fields to methods for generating JSON schemas.
308 Returns:
309 A dictionary containing the mapping of `CoreSchemaOrFieldType` to a handler method.
311 Raises:
312 TypeError: If no method has been defined for generating a JSON schema for a given pydantic core schema type.
313 """
314 mapping: dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
315 core_schema_types: list[CoreSchemaOrFieldType] = list(get_literal_values(CoreSchemaOrFieldType)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
316 for key in core_schema_types: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
317 method_name = f'{key.replace("-", "_")}_schema' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
318 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
319 mapping[key] = getattr(self, method_name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
320 except AttributeError as e: # pragma: no cover
321 if os.getenv('PYDANTIC_PRIVATE_ALLOW_UNHANDLED_SCHEMA_TYPES'):
322 continue
323 raise TypeError(
324 f'No method for generating JsonSchema for core_schema.type={key!r} '
325 f'(expected: {type(self).__name__}.{method_name})'
326 ) from e
327 return mapping 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
329 def generate_definitions( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
330 self, inputs: Sequence[tuple[JsonSchemaKeyT, JsonSchemaMode, core_schema.CoreSchema]]
331 ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], dict[DefsRef, JsonSchemaValue]]:
332 """Generates JSON schema definitions from a list of core schemas, pairing the generated definitions with a
333 mapping that links the input keys to the definition references.
335 Args:
336 inputs: A sequence of tuples, where:
338 - The first element is a JSON schema key type.
339 - The second element is the JSON mode: either 'validation' or 'serialization'.
340 - The third element is a core schema.
342 Returns:
343 A tuple where:
345 - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
346 whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
347 JsonRef references to definitions that are defined in the second returned element.)
348 - The second element is a dictionary whose keys are definition references for the JSON schemas
349 from the first returned element, and whose values are the actual JSON schema definitions.
351 Raises:
352 PydanticUserError: Raised if the JSON schema generator has already been used to generate a JSON schema.
353 """
354 if self._used: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
355 raise PydanticUserError( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
356 'This JSON schema generator has already been used to generate a JSON schema. '
357 f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.',
358 code='json-schema-already-used',
359 )
361 for _, mode, schema in inputs: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
362 self._mode = mode 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
363 self.generate_inner(schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
365 definitions_remapping = self._build_definitions_remapping() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
367 json_schemas_map: dict[tuple[JsonSchemaKeyT, JsonSchemaMode], DefsRef] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
368 for key, mode, schema in inputs: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
369 self._mode = mode 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
370 json_schema = self.generate_inner(schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
371 json_schemas_map[(key, mode)] = definitions_remapping.remap_json_schema(json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
373 json_schema = {'$defs': self.definitions} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
374 json_schema = definitions_remapping.remap_json_schema(json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
375 self._used = True 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
376 return json_schemas_map, self.sort(json_schema['$defs']) # type: ignore 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
378 def generate(self, schema: CoreSchema, mode: JsonSchemaMode = 'validation') -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
379 """Generates a JSON schema for a specified schema in a specified mode.
381 Args:
382 schema: A Pydantic model.
383 mode: The mode in which to generate the schema. Defaults to 'validation'.
385 Returns:
386 A JSON schema representing the specified schema.
388 Raises:
389 PydanticUserError: If the JSON schema generator has already been used to generate a JSON schema.
390 """
391 self._mode = mode 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
392 if self._used: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
393 raise PydanticUserError( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
394 'This JSON schema generator has already been used to generate a JSON schema. '
395 f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.',
396 code='json-schema-already-used',
397 )
399 json_schema: JsonSchemaValue = self.generate_inner(schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
400 json_ref_counts = self.get_json_ref_counts(json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
402 ref = cast(JsonRef, json_schema.get('$ref')) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
403 while ref is not None: # may need to unpack multiple levels 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
404 ref_json_schema = self.get_schema_from_definitions(ref) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
405 if json_ref_counts[ref] == 1 and ref_json_schema is not None and len(json_schema) == 1: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
406 # "Unpack" the ref since this is the only reference and there are no sibling keys
407 json_schema = ref_json_schema.copy() # copy to prevent recursive dict reference 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
408 json_ref_counts[ref] -= 1 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
409 ref = cast(JsonRef, json_schema.get('$ref')) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
410 ref = None 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
412 self._garbage_collect_definitions(json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
413 definitions_remapping = self._build_definitions_remapping() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
415 if self.definitions: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
416 json_schema['$defs'] = self.definitions 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
418 json_schema = definitions_remapping.remap_json_schema(json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
420 # For now, we will not set the $schema key. However, if desired, this can be easily added by overriding
421 # this method and adding the following line after a call to super().generate(schema):
422 # json_schema['$schema'] = self.schema_dialect
424 self._used = True 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
425 return self.sort(json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
427 def generate_inner(self, schema: CoreSchemaOrField) -> JsonSchemaValue: # noqa: C901 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
428 """Generates a JSON schema for a given core schema.
430 Args:
431 schema: The given core schema.
433 Returns:
434 The generated JSON schema.
436 TODO: the nested function definitions here seem like bad practice, I'd like to unpack these
437 in a future PR. It'd be great if we could shorten the call stack a bit for JSON schema generation,
438 and I think there's potential for that here.
439 """
440 # If a schema with the same CoreRef has been handled, just return a reference to it
441 # Note that this assumes that it will _never_ be the case that the same CoreRef is used
442 # on types that should have different JSON schemas
443 if 'ref' in schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
444 core_ref = CoreRef(schema['ref']) # type: ignore[typeddict-item] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
445 core_mode_ref = (core_ref, self.mode) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
446 if core_mode_ref in self.core_to_defs_refs and self.core_to_defs_refs[core_mode_ref] in self.definitions: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
447 return {'$ref': self.core_to_json_refs[core_mode_ref]} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
449 def populate_defs(core_schema: CoreSchema, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
450 if 'ref' in core_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
451 core_ref = CoreRef(core_schema['ref']) # type: ignore[typeddict-item] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
452 defs_ref, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
453 json_ref = JsonRef(ref_json_schema['$ref']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
454 # Replace the schema if it's not a reference to itself
455 # What we want to avoid is having the def be just a ref to itself
456 # which is what would happen if we blindly assigned any
457 if json_schema.get('$ref', None) != json_ref: 457 ↛ 460line 457 didn't jump to line 460 because the condition on line 457 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
458 self.definitions[defs_ref] = json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
459 self._core_defs_invalid_for_json_schema.pop(defs_ref, None) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
460 json_schema = ref_json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
461 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
463 def handler_func(schema_or_field: CoreSchemaOrField) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
464 """Generate a JSON schema based on the input schema.
466 Args:
467 schema_or_field: The core schema to generate a JSON schema from.
469 Returns:
470 The generated JSON schema.
472 Raises:
473 TypeError: If an unexpected schema type is encountered.
474 """
475 # Generate the core-schema-type-specific bits of the schema generation:
476 json_schema: JsonSchemaValue | None = None 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
477 if self.mode == 'serialization' and 'serialization' in schema_or_field: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
478 # In this case, we skip the JSON Schema generation of the schema
479 # and use the `'serialization'` schema instead (canonical example:
480 # `Annotated[int, PlainSerializer(str)]`).
481 ser_schema = schema_or_field['serialization'] # type: ignore 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
482 json_schema = self.ser_schema(ser_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
484 # It might be that the 'serialization'` is skipped depending on `when_used`.
485 # This is only relevant for `nullable` schemas though, so we special case here.
486 if ( 1zCFa
487 json_schema is not None
488 and ser_schema.get('when_used') in ('unless-none', 'json-unless-none')
489 and schema_or_field['type'] == 'nullable'
490 ):
491 json_schema = self.get_flattened_anyof([{'type': 'null'}, json_schema]) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
492 if json_schema is None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
493 if _core_utils.is_core_schema(schema_or_field) or _core_utils.is_core_schema_field(schema_or_field): 493 ↛ 497line 493 didn't jump to line 497 because the condition on line 493 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
494 generate_for_schema_type = self._schema_type_to_method[schema_or_field['type']] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
495 json_schema = generate_for_schema_type(schema_or_field) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
496 else:
497 raise TypeError(f'Unexpected schema type: schema={schema_or_field}')
499 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
501 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, handler_func) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
503 metadata = cast(_core_metadata.CoreMetadata, schema.get('metadata', {})) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
505 # TODO: I dislike that we have to wrap these basic dict updates in callables, is there any way around this?
507 if js_updates := metadata.get('pydantic_js_updates'): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
509 def js_updates_handler_func( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
510 schema_or_field: CoreSchemaOrField,
511 current_handler: GetJsonSchemaHandler = current_handler,
512 ) -> JsonSchemaValue:
513 json_schema = {**current_handler(schema_or_field), **js_updates} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
514 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
516 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_updates_handler_func) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
518 if js_extra := metadata.get('pydantic_js_extra'): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
520 def js_extra_handler_func( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
521 schema_or_field: CoreSchemaOrField,
522 current_handler: GetJsonSchemaHandler = current_handler,
523 ) -> JsonSchemaValue:
524 json_schema = current_handler(schema_or_field) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
525 if isinstance(js_extra, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
526 json_schema.update(to_jsonable_python(js_extra)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
527 elif callable(js_extra): 527 ↛ 530line 527 didn't jump to line 530 because the condition on line 527 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
528 # similar to typing issue in _update_class_schema when we're working with callable js extra
529 js_extra(json_schema) # type: ignore 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
530 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
532 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_extra_handler_func) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
534 for js_modify_function in metadata.get('pydantic_js_functions', ()): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
536 def new_handler_func( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
537 schema_or_field: CoreSchemaOrField,
538 current_handler: GetJsonSchemaHandler = current_handler,
539 js_modify_function: GetJsonSchemaFunction = js_modify_function,
540 ) -> JsonSchemaValue:
541 json_schema = js_modify_function(schema_or_field, current_handler) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
542 if _core_utils.is_core_schema(schema_or_field): 542 ↛ 544line 542 didn't jump to line 544 because the condition on line 542 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
543 json_schema = populate_defs(schema_or_field, json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
544 original_schema = current_handler.resolve_ref_schema(json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
545 ref = json_schema.pop('$ref', None) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
546 if ref and json_schema: 546 ↛ 547line 546 didn't jump to line 547 because the condition on line 546 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
547 original_schema.update(json_schema)
548 return original_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
550 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
552 for js_modify_function in metadata.get('pydantic_js_annotation_functions', ()): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
554 def new_handler_func( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
555 schema_or_field: CoreSchemaOrField,
556 current_handler: GetJsonSchemaHandler = current_handler,
557 js_modify_function: GetJsonSchemaFunction = js_modify_function,
558 ) -> JsonSchemaValue:
559 return js_modify_function(schema_or_field, current_handler) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
561 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
563 json_schema = current_handler(schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
564 if _core_utils.is_core_schema(schema): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
565 json_schema = populate_defs(schema, json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
566 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
568 def sort(self, value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
575 keys = value.keys() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
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 true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
577 keys = sorted(keys) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
578 for key in keys: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
579 sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
580 return sorted_dict 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
582 def _sort_recursive(self, value: Any, parent_key: str | None = None) -> Any: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
583 """Recursively sort a JSON schema value."""
584 if isinstance(value, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
585 sorted_dict: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
586 keys = value.keys() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
587 if parent_key not in ('properties', 'default'): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
588 keys = sorted(keys) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
589 for key in keys: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
590 sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
591 return sorted_dict 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
592 elif isinstance(value, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
593 sorted_list: list[JsonSchemaValue] = [] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
594 for item in value: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
595 sorted_list.append(self._sort_recursive(item, parent_key)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
596 return sorted_list 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
597 else:
598 return value 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
600 # ### Schema generation methods
602 def invalid_schema(self, schema: core_schema.InvalidSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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 {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
618 def none_schema(self, schema: core_schema.NoneSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
629 def bool_schema(self, schema: core_schema.BoolSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
640 def int_schema(self, schema: core_schema.IntSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
650 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
651 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
652 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
654 def float_schema(self, schema: core_schema.FloatSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
664 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
665 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
666 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
668 def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
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 """
678 def get_decimal_pattern(schema: core_schema.DecimalSchema) -> str: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
679 max_digits = schema.get('max_digits') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
680 decimal_places = schema.get('decimal_places') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
682 pattern = ( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
683 r'^(?!^[-+.]*$)[+-]?0*' # check it is not empty string and not one or sequence of ".+-" characters.
684 )
686 # Case 1: Both max_digits and decimal_places are set
687 if max_digits is not None and decimal_places is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
688 integer_places = max(0, max_digits - decimal_places) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
689 pattern += ( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
690 rf'(?:'
691 rf'\d{{0,{integer_places}}}'
692 rf'|'
693 rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)'
694 rf'\d{{0,{integer_places}}}\.\d{{0,{decimal_places}}}0*$'
695 rf')'
696 )
698 # Case 2: Only max_digits is set
699 elif max_digits is not None and decimal_places is None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
700 pattern += ( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
701 rf'(?:'
702 rf'\d{{0,{max_digits}}}'
703 rf'|'
704 rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)'
705 rf'\d*\.\d*0*$'
706 rf')'
707 )
709 # Case 3: Only decimal_places is set
710 elif max_digits is None and decimal_places is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
711 pattern += rf'\d*\.?\d{{0,{decimal_places}}}0*$' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
713 # Case 4: Both are None (no restrictions)
714 else:
715 pattern += r'\d*\.?\d*$' # look for arbitrary integer or decimal 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
717 return pattern 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
719 json_schema = self.str_schema(core_schema.str_schema(pattern=get_decimal_pattern(schema))) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
720 if self.mode == 'validation': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
721 multiple_of = schema.get('multiple_of') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
722 le = schema.get('le') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
723 ge = schema.get('ge') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
724 lt = schema.get('lt') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
725 gt = schema.get('gt') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
726 json_schema = { 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
727 'anyOf': [
728 self.float_schema(
729 core_schema.float_schema(
730 allow_inf_nan=schema.get('allow_inf_nan'),
731 multiple_of=None if multiple_of is None else float(multiple_of),
732 le=None if le is None else float(le),
733 ge=None if ge is None else float(ge),
734 lt=None if lt is None else float(lt),
735 gt=None if gt is None else float(gt),
736 )
737 ),
738 json_schema,
739 ],
740 }
741 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
743 def str_schema(self, schema: core_schema.StringSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
744 """Generates a JSON schema that matches a string value.
746 Args:
747 schema: The core schema.
749 Returns:
750 The generated JSON schema.
751 """
752 json_schema = {'type': 'string'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
753 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
754 if isinstance(json_schema.get('pattern'), Pattern): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
755 # TODO: should we add regex flags to the pattern?
756 json_schema['pattern'] = json_schema.get('pattern').pattern # type: ignore 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
757 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
759 def bytes_schema(self, schema: core_schema.BytesSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
760 """Generates a JSON schema that matches a bytes value.
762 Args:
763 schema: The core schema.
765 Returns:
766 The generated JSON schema.
767 """
768 json_schema = {'type': 'string', 'format': 'base64url' if self._config.ser_json_bytes == 'base64' else 'binary'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
769 self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
770 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
772 def date_schema(self, schema: core_schema.DateSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
773 """Generates a JSON schema that matches a date value.
775 Args:
776 schema: The core schema.
778 Returns:
779 The generated JSON schema.
780 """
781 return {'type': 'string', 'format': 'date'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
783 def time_schema(self, schema: core_schema.TimeSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
784 """Generates a JSON schema that matches a time value.
786 Args:
787 schema: The core schema.
789 Returns:
790 The generated JSON schema.
791 """
792 return {'type': 'string', 'format': 'time'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
794 def datetime_schema(self, schema: core_schema.DatetimeSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
795 """Generates a JSON schema that matches a datetime value.
797 Args:
798 schema: The core schema.
800 Returns:
801 The generated JSON schema.
802 """
803 return {'type': 'string', 'format': 'date-time'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
805 def timedelta_schema(self, schema: core_schema.TimedeltaSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
806 """Generates a JSON schema that matches a timedelta value.
808 Args:
809 schema: The core schema.
811 Returns:
812 The generated JSON schema.
813 """
814 if self._config.ser_json_timedelta == 'float': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
815 return {'type': 'number'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
816 return {'type': 'string', 'format': 'duration'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
818 def literal_schema(self, schema: core_schema.LiteralSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
819 """Generates a JSON schema that matches a literal value.
821 Args:
822 schema: The core schema.
824 Returns:
825 The generated JSON schema.
826 """
827 expected = [to_jsonable_python(v.value if isinstance(v, Enum) else v) for v in schema['expected']] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
829 result: dict[str, Any] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
830 if len(expected) == 1: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
831 result['const'] = expected[0] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
832 else:
833 result['enum'] = expected 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
835 types = {type(e) for e in expected} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
836 if types == {str}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
837 result['type'] = 'string' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
838 elif types == {int}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
839 result['type'] = 'integer' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
840 elif types == {float}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
841 result['type'] = 'number' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
842 elif types == {bool}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
843 result['type'] = 'boolean' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
844 elif types == {list}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
845 result['type'] = 'array' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
846 elif types == {type(None)}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
847 result['type'] = 'null' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
848 return result 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
850 def missing_sentinel_schema(self, schema: core_schema.MissingSentinelSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
851 """Generates a JSON schema that matches the `MISSING` sentinel value.
853 Args:
854 schema: The core schema.
856 Returns:
857 The generated JSON schema.
858 """
859 raise PydanticOmit 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
861 def enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
862 """Generates a JSON schema that matches an Enum value.
864 Args:
865 schema: The core schema.
867 Returns:
868 The generated JSON schema.
869 """
870 enum_type = schema['cls'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
871 description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
872 if ( 1zCFa
873 description == 'An enumeration.'
874 ): # This is the default value provided by enum.EnumMeta.__new__; don't use it
875 description = None 1zCbdFaMNhiLOcj
876 result: dict[str, Any] = {'title': enum_type.__name__, 'description': description} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
877 result = {k: v for k, v in result.items() if v is not None} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
879 expected = [to_jsonable_python(v.value) for v in schema['members']] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
881 result['enum'] = expected 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
883 types = {type(e) for e in expected} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
884 if isinstance(enum_type, str) or types == {str}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
885 result['type'] = 'string' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
886 elif isinstance(enum_type, int) or types == {int}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
887 result['type'] = 'integer' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
888 elif isinstance(enum_type, float) or types == {float}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
889 result['type'] = 'number' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
890 elif types == {bool}: 890 ↛ 891line 890 didn't jump to line 891 because the condition on line 890 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
891 result['type'] = 'boolean'
892 elif types == {list}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
893 result['type'] = 'array' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
895 return result 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
897 def is_instance_schema(self, schema: core_schema.IsInstanceSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
898 """Handles JSON schema generation for a core schema that checks if a value is an instance of a class.
900 Unless overridden in a subclass, this raises an error.
902 Args:
903 schema: The core schema.
905 Returns:
906 The generated JSON schema.
907 """
908 return self.handle_invalid_for_json_schema(schema, f'core_schema.IsInstanceSchema ({schema["cls"]})') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
910 def is_subclass_schema(self, schema: core_schema.IsSubclassSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
911 """Handles JSON schema generation for a core schema that checks if a value is a subclass of a class.
913 For backwards compatibility with v1, this does not raise an error, but can be overridden to change this.
915 Args:
916 schema: The core schema.
918 Returns:
919 The generated JSON schema.
920 """
921 # Note: This is for compatibility with V1; you can override if you want different behavior.
922 return {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
924 def callable_schema(self, schema: core_schema.CallableSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
925 """Generates a JSON schema that matches a callable value.
927 Unless overridden in a subclass, this raises an error.
929 Args:
930 schema: The core schema.
932 Returns:
933 The generated JSON schema.
934 """
935 return self.handle_invalid_for_json_schema(schema, 'core_schema.CallableSchema') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
937 def list_schema(self, schema: core_schema.ListSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
938 """Returns a schema that matches a list schema.
940 Args:
941 schema: The core schema.
943 Returns:
944 The generated JSON schema.
945 """
946 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
947 json_schema = {'type': 'array', 'items': items_schema} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
948 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
949 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
951 @deprecated('`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', category=None) 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
952 @final 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
953 def tuple_positional_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
954 """Replaced by `tuple_schema`."""
955 warnings.warn(
956 '`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.',
957 PydanticDeprecatedSince26,
958 stacklevel=2,
959 )
960 return self.tuple_schema(schema)
962 @deprecated('`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', category=None) 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
963 @final 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
964 def tuple_variable_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
965 """Replaced by `tuple_schema`."""
966 warnings.warn(
967 '`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.',
968 PydanticDeprecatedSince26,
969 stacklevel=2,
970 )
971 return self.tuple_schema(schema)
973 def tuple_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
974 """Generates a JSON schema that matches a tuple schema e.g. `tuple[int,
975 str, bool]` or `tuple[int, ...]`.
977 Args:
978 schema: The core schema.
980 Returns:
981 The generated JSON schema.
982 """
983 json_schema: JsonSchemaValue = {'type': 'array'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
984 if 'variadic_item_index' in schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
985 variadic_item_index = schema['variadic_item_index'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
986 if variadic_item_index > 0: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
987 json_schema['minItems'] = variadic_item_index 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
988 json_schema['prefixItems'] = [ 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
989 self.generate_inner(item) for item in schema['items_schema'][:variadic_item_index]
990 ]
991 if variadic_item_index + 1 == len(schema['items_schema']): 991 ↛ 998line 991 didn't jump to line 998 because the condition on line 991 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
992 # if the variadic item is the last item, then represent it faithfully
993 json_schema['items'] = self.generate_inner(schema['items_schema'][variadic_item_index]) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
994 else:
995 # otherwise, 'items' represents the schema for the variadic
996 # item plus the suffix, so just allow anything for simplicity
997 # for now
998 json_schema['items'] = True
999 else:
1000 prefixItems = [self.generate_inner(item) for item in schema['items_schema']] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1001 if prefixItems: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1002 json_schema['prefixItems'] = prefixItems 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1003 json_schema['minItems'] = len(prefixItems) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1004 json_schema['maxItems'] = len(prefixItems) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1005 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1006 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1008 def set_schema(self, schema: core_schema.SetSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1009 """Generates a JSON schema that matches a set schema.
1011 Args:
1012 schema: The core schema.
1014 Returns:
1015 The generated JSON schema.
1016 """
1017 return self._common_set_schema(schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1019 def frozenset_schema(self, schema: core_schema.FrozenSetSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1020 """Generates a JSON schema that matches a frozenset schema.
1022 Args:
1023 schema: The core schema.
1025 Returns:
1026 The generated JSON schema.
1027 """
1028 return self._common_set_schema(schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1030 def _common_set_schema(self, schema: core_schema.SetSchema | core_schema.FrozenSetSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1031 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1032 json_schema = {'type': 'array', 'uniqueItems': True, 'items': items_schema} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1033 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1034 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1036 def generator_schema(self, schema: core_schema.GeneratorSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1037 """Returns a JSON schema that represents the provided GeneratorSchema.
1039 Args:
1040 schema: The schema.
1042 Returns:
1043 The generated JSON schema.
1044 """
1045 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1046 json_schema = {'type': 'array', 'items': items_schema} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1047 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1048 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1050 def dict_schema(self, schema: core_schema.DictSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1051 """Generates a JSON schema that matches a dict schema.
1053 Args:
1054 schema: The core schema.
1056 Returns:
1057 The generated JSON schema.
1058 """
1059 json_schema: JsonSchemaValue = {'type': 'object'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1061 keys_schema = self.generate_inner(schema['keys_schema']).copy() if 'keys_schema' in schema else {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1062 if '$ref' not in keys_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1063 keys_pattern = keys_schema.pop('pattern', None) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1064 # Don't give a title to patternProperties/propertyNames:
1065 keys_schema.pop('title', None) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1066 else:
1067 # Here, we assume that if the keys schema is a definition reference,
1068 # it can't be a simple string core schema (and thus no pattern can exist).
1069 # However, this is only in practice (in theory, a definition reference core
1070 # schema could be generated for a simple string schema).
1071 # Note that we avoid calling `self.resolve_ref_schema`, as it might not exist yet.
1072 keys_pattern = None 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1074 values_schema = self.generate_inner(schema['values_schema']).copy() if 'values_schema' in schema else {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1075 # don't give a title to additionalProperties:
1076 values_schema.pop('title', None) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1078 if values_schema or keys_pattern is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1079 if keys_pattern is None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1080 json_schema['additionalProperties'] = values_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1081 else:
1082 json_schema['patternProperties'] = {keys_pattern: values_schema} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1083 else: # for `dict[str, Any]`, we allow any key and any value, since `str` is the default key type
1084 json_schema['additionalProperties'] = True 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1086 if ( 1zCFa
1087 # The len check indicates that constraints are probably present:
1088 (keys_schema.get('type') == 'string' and len(keys_schema) > 1)
1089 # If this is a definition reference schema, it most likely has constraints:
1090 or '$ref' in keys_schema
1091 ):
1092 keys_schema.pop('type', None) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1093 json_schema['propertyNames'] = keys_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1095 self.update_with_validations(json_schema, schema, self.ValidationsMapping.object) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1096 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1098 def function_before_schema(self, schema: core_schema.BeforeValidatorFunctionSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1099 """Generates a JSON schema that matches a function-before schema.
1101 Args:
1102 schema: The core schema.
1104 Returns:
1105 The generated JSON schema.
1106 """
1107 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1108 return self.generate_inner(input_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1110 return self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1112 def function_after_schema(self, schema: core_schema.AfterValidatorFunctionSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1113 """Generates a JSON schema that matches a function-after schema.
1115 Args:
1116 schema: The core schema.
1118 Returns:
1119 The generated JSON schema.
1120 """
1121 return self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1123 def function_plain_schema(self, schema: core_schema.PlainValidatorFunctionSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1124 """Generates a JSON schema that matches a function-plain schema.
1126 Args:
1127 schema: The core schema.
1129 Returns:
1130 The generated JSON schema.
1131 """
1132 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 1132 ↛ 1135line 1132 didn't jump to line 1135 because the condition on line 1132 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1133 return self.generate_inner(input_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1135 return self.handle_invalid_for_json_schema(
1136 schema, f'core_schema.PlainValidatorFunctionSchema ({schema["function"]})'
1137 )
1139 def function_wrap_schema(self, schema: core_schema.WrapValidatorFunctionSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1140 """Generates a JSON schema that matches a function-wrap schema.
1142 Args:
1143 schema: The core schema.
1145 Returns:
1146 The generated JSON schema.
1147 """
1148 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1149 return self.generate_inner(input_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1151 return self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1153 def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1154 """Generates a JSON schema that matches a schema with a default value.
1156 Args:
1157 schema: The core schema.
1159 Returns:
1160 The generated JSON schema.
1161 """
1162 json_schema = self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1164 default = self.get_default_value(schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1165 if default is NoDefault or default is MISSING: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1166 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1168 # we reflect the application of custom plain, no-info serializers to defaults for
1169 # JSON Schemas viewed in serialization mode:
1170 # TODO: improvements along with https://github.com/pydantic/pydantic/issues/8208
1171 if self.mode == 'serialization': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1172 # `_get_ser_schema_for_default_value()` is used to unpack potentially nested validator schemas:
1173 ser_schema = _get_ser_schema_for_default_value(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1174 if ( 1zCFaMNLO
1175 ser_schema is not None
1176 and (ser_func := ser_schema.get('function'))
1177 and not (default is None and ser_schema.get('when_used') in ('unless-none', 'json-unless-none'))
1178 ):
1179 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1180 default = ser_func(default) # type: ignore 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1181 except Exception: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1182 # It might be that the provided default needs to be validated (read: parsed) first
1183 # (assuming `validate_default` is enabled). However, we can't perform
1184 # such validation during JSON Schema generation so we don't support
1185 # this pattern for now.
1186 # (One example is when using `foo: ByteSize = '1MB'`, which validates and
1187 # serializes as an int. In this case, `ser_func` is `int` and `int('1MB')` fails).
1188 self.emit_warning( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1189 'non-serializable-default',
1190 f'Unable to serialize value {default!r} with the plain serializer; excluding default from JSON schema',
1191 )
1192 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1194 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1195 encoded_default = self.encode_default(default) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1196 except pydantic_core.PydanticSerializationError: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1197 self.emit_warning( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1198 'non-serializable-default',
1199 f'Default value {default} is not JSON serializable; excluding default from JSON schema',
1200 )
1201 # Return the inner schema, as though there was no default
1202 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1204 json_schema['default'] = encoded_default 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1205 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1207 def get_default_value(self, schema: core_schema.WithDefaultSchema) -> Any: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1208 """Get the default value to be used when generating a JSON Schema for a core schema with a default.
1210 The default implementation is to use the statically defined default value. This method can be overridden
1211 if you want to make use of the default factory.
1213 Args:
1214 schema: The `'with-default'` core schema.
1216 Returns:
1217 The default value to use, or [`NoDefault`][pydantic.json_schema.NoDefault] if no default
1218 value is available.
1219 """
1220 return schema.get('default', NoDefault) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1222 def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1223 """Generates a JSON schema that matches a schema that allows null values.
1225 Args:
1226 schema: The core schema.
1228 Returns:
1229 The generated JSON schema.
1230 """
1231 null_schema = {'type': 'null'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1232 inner_json_schema = self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1234 if inner_json_schema == null_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1235 return null_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1236 else:
1237 # Thanks to the equality check against `null_schema` above, I think 'oneOf' would also be valid here;
1238 # I'll use 'anyOf' for now, but it could be changed it if it would work better with some external tooling
1239 return self.get_flattened_anyof([inner_json_schema, null_schema]) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1241 def union_schema(self, schema: core_schema.UnionSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1242 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas.
1244 Args:
1245 schema: The core schema.
1247 Returns:
1248 The generated JSON schema.
1249 """
1250 generated: list[JsonSchemaValue] = [] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1252 choices = schema['choices'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1253 for choice in choices: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1254 # choice will be a tuple if an explicit label was provided
1255 choice_schema = choice[0] if isinstance(choice, tuple) else choice 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1256 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1257 generated.append(self.generate_inner(choice_schema)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1258 except PydanticOmit: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1259 continue 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1260 except PydanticInvalidForJsonSchema as exc: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1261 self.emit_warning('skipped-choice', exc.message) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1262 if len(generated) == 1: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1263 return generated[0] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1264 return self.get_flattened_anyof(generated) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1266 def tagged_union_schema(self, schema: core_schema.TaggedUnionSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1267 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas, where
1268 the schemas are tagged with a discriminator field that indicates which schema should be used to validate
1269 the value.
1271 Args:
1272 schema: The core schema.
1274 Returns:
1275 The generated JSON schema.
1276 """
1277 generated: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1278 for k, v in schema['choices'].items(): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1279 if isinstance(k, Enum): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1280 k = k.value 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1281 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1282 # Use str(k) since keys must be strings for json; while not technically correct,
1283 # it's the closest that can be represented in valid JSON
1284 generated[str(k)] = self.generate_inner(v).copy() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1285 except PydanticOmit:
1286 continue
1287 except PydanticInvalidForJsonSchema as exc:
1288 self.emit_warning('skipped-choice', exc.message)
1290 one_of_choices = _deduplicate_schemas(generated.values()) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1291 json_schema: JsonSchemaValue = {'oneOf': one_of_choices} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1293 # This reflects the v1 behavior; TODO: we should make it possible to exclude OpenAPI stuff from the JSON schema
1294 openapi_discriminator = self._extract_discriminator(schema, one_of_choices) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1295 if openapi_discriminator is not None: 1295 ↛ 1301line 1295 didn't jump to line 1301 because the condition on line 1295 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1296 json_schema['discriminator'] = { 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1297 'propertyName': openapi_discriminator,
1298 'mapping': {k: v.get('$ref', v) for k, v in generated.items()},
1299 }
1301 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1303 def _extract_discriminator( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1304 self, schema: core_schema.TaggedUnionSchema, one_of_choices: list[JsonDict]
1305 ) -> str | None:
1306 """Extract a compatible OpenAPI discriminator from the schema and one_of choices that end up in the final
1307 schema."""
1308 openapi_discriminator: str | None = None 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1310 if isinstance(schema['discriminator'], str): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1311 return schema['discriminator'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1313 if isinstance(schema['discriminator'], list): 1313 ↛ 1346line 1313 didn't jump to line 1346 because the condition on line 1313 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1314 # If the discriminator is a single item list containing a string, that is equivalent to the string case
1315 if len(schema['discriminator']) == 1 and isinstance(schema['discriminator'][0], str): 1315 ↛ 1316line 1315 didn't jump to line 1316 because the condition on line 1315 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1316 return schema['discriminator'][0]
1317 # When an alias is used that is different from the field name, the discriminator will be a list of single
1318 # str lists, one for the attribute and one for the actual alias. The logic here will work even if there is
1319 # more than one possible attribute, and looks for whether a single alias choice is present as a documented
1320 # property on all choices. If so, that property will be used as the OpenAPI discriminator.
1321 for alias_path in schema['discriminator']: 1321 ↛ 1346line 1321 didn't jump to line 1346 because the loop on line 1321 didn't complete1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1322 if not isinstance(alias_path, list): 1322 ↛ 1323line 1322 didn't jump to line 1323 because the condition on line 1322 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1323 break # this means that the discriminator is not a list of alias paths
1324 if len(alias_path) != 1: 1324 ↛ 1325line 1324 didn't jump to line 1325 because the condition on line 1324 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1325 continue # this means that the "alias" does not represent a single field
1326 alias = alias_path[0] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1327 if not isinstance(alias, str): 1327 ↛ 1328line 1327 didn't jump to line 1328 because the condition on line 1327 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1328 continue # this means that the "alias" does not represent a field
1329 alias_is_present_on_all_choices = True 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1330 for choice in one_of_choices: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1331 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1332 choice = self.resolve_ref_schema(choice) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1333 except RuntimeError as exc:
1334 # TODO: fixme - this is a workaround for the fact that we can't always resolve refs
1335 # for tagged union choices at this point in the schema gen process, we might need to do
1336 # another pass at the end like we do for core schemas
1337 self.emit_warning('skipped-discriminator', str(exc))
1338 choice = {}
1339 properties = choice.get('properties', {}) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1340 if not isinstance(properties, dict) or alias not in properties: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1341 alias_is_present_on_all_choices = False 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1342 break 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1343 if alias_is_present_on_all_choices: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1344 openapi_discriminator = alias 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1345 break 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1346 return openapi_discriminator 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1348 def chain_schema(self, schema: core_schema.ChainSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1349 """Generates a JSON schema that matches a core_schema.ChainSchema.
1351 When generating a schema for validation, we return the validation JSON schema for the first step in the chain.
1352 For serialization, we return the serialization JSON schema for the last step in the chain.
1354 Args:
1355 schema: The core schema.
1357 Returns:
1358 The generated JSON schema.
1359 """
1360 step_index = 0 if self.mode == 'validation' else -1 # use first step for validation, last for serialization 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1361 return self.generate_inner(schema['steps'][step_index]) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1363 def lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1364 """Generates a JSON schema that matches a schema that allows values matching either the lax schema or the
1365 strict schema.
1367 Args:
1368 schema: The core schema.
1370 Returns:
1371 The generated JSON schema.
1372 """
1373 # TODO: Need to read the default value off of model config or whatever
1374 use_strict = schema.get('strict', False) # TODO: replace this default False 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1375 # If your JSON schema fails to generate it is probably
1376 # because one of the following two branches failed.
1377 if use_strict: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1378 return self.generate_inner(schema['strict_schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1379 else:
1380 return self.generate_inner(schema['lax_schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1382 def json_or_python_schema(self, schema: core_schema.JsonOrPythonSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1383 """Generates a JSON schema that matches a schema that allows values matching either the JSON schema or the
1384 Python schema.
1386 The JSON schema is used instead of the Python schema. If you want to use the Python schema, you should override
1387 this method.
1389 Args:
1390 schema: The core schema.
1392 Returns:
1393 The generated JSON schema.
1394 """
1395 return self.generate_inner(schema['json_schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1397 def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1398 """Generates a JSON schema that matches a schema that defines a typed dict.
1400 Args:
1401 schema: The core schema.
1403 Returns:
1404 The generated JSON schema.
1405 """
1406 total = schema.get('total', True) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1407 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1408 (name, self.field_is_required(field, total), field)
1409 for name, field in schema['fields'].items()
1410 if self.field_is_present(field)
1411 ]
1412 if self.mode == 'serialization': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1413 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1414 cls = schema.get('cls') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1415 config = _get_typed_dict_config(cls) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1416 with self._config_wrapper_stack.push(config): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1417 json_schema = self._named_required_fields_schema(named_required_fields) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1419 if cls is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1420 self._update_class_schema(json_schema, cls, config) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1421 else:
1422 extra = config.get('extra') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1423 if extra == 'forbid': 1423 ↛ 1424line 1423 didn't jump to line 1424 because the condition on line 1423 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1424 json_schema['additionalProperties'] = False
1425 elif extra == 'allow': 1425 ↛ 1426line 1425 didn't jump to line 1426 because the condition on line 1425 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1426 json_schema['additionalProperties'] = True
1428 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1430 @staticmethod 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1431 def _name_required_computed_fields( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1432 computed_fields: list[ComputedField],
1433 ) -> list[tuple[str, bool, core_schema.ComputedField]]:
1434 return [(field['property_name'], True, field) for field in computed_fields] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1436 def _named_required_fields_schema( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1437 self, named_required_fields: Sequence[tuple[str, bool, CoreSchemaField]]
1438 ) -> JsonSchemaValue:
1439 properties: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1440 required_fields: list[str] = [] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1441 for name, required, field in named_required_fields: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1442 if self.by_alias: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1443 name = self._get_alias_name(field, name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1444 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1445 field_json_schema = self.generate_inner(field).copy() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1446 except PydanticOmit: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1447 continue 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1448 if 'title' not in field_json_schema and self.field_title_should_be_set(field): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1449 title = self.get_title_from_name(name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1450 field_json_schema['title'] = title 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1451 field_json_schema = self.handle_ref_overrides(field_json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1452 properties[name] = field_json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1453 if required: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1454 required_fields.append(name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1456 json_schema = {'type': 'object', 'properties': properties} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1457 if required_fields: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1458 json_schema['required'] = required_fields 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1459 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1461 def _get_alias_name(self, field: CoreSchemaField, name: str) -> str: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1462 if field['type'] == 'computed-field': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1463 alias: Any = field.get('alias', name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1464 elif self.mode == 'validation': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1465 alias = field.get('validation_alias', name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1466 else:
1467 alias = field.get('serialization_alias', name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1468 if isinstance(alias, str): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1469 name = alias 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1470 elif isinstance(alias, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1471 alias = cast('list[str] | str', alias) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1472 for path in alias: 1472 ↛ 1480line 1472 didn't jump to line 1480 because the loop on line 1472 didn't complete1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1473 if isinstance(path, list) and len(path) == 1 and isinstance(path[0], str): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1474 # Use the first valid single-item string path; the code that constructs the alias array
1475 # should ensure the first such item is what belongs in the JSON schema
1476 name = path[0] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1477 break 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1478 else:
1479 assert_never(alias)
1480 return name 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1482 def typed_dict_field_schema(self, schema: core_schema.TypedDictField) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1483 """Generates a JSON schema that matches a schema that defines a typed dict field.
1485 Args:
1486 schema: The core schema.
1488 Returns:
1489 The generated JSON schema.
1490 """
1491 return self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1493 def dataclass_field_schema(self, schema: core_schema.DataclassField) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1494 """Generates a JSON schema that matches a schema that defines a dataclass field.
1496 Args:
1497 schema: The core schema.
1499 Returns:
1500 The generated JSON schema.
1501 """
1502 return self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1504 def model_field_schema(self, schema: core_schema.ModelField) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1505 """Generates a JSON schema that matches a schema that defines a model field.
1507 Args:
1508 schema: The core schema.
1510 Returns:
1511 The generated JSON schema.
1512 """
1513 return self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1515 def computed_field_schema(self, schema: core_schema.ComputedField) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1516 """Generates a JSON schema that matches a schema that defines a computed field.
1518 Args:
1519 schema: The core schema.
1521 Returns:
1522 The generated JSON schema.
1523 """
1524 return self.generate_inner(schema['return_schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1526 def model_schema(self, schema: core_schema.ModelSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1527 """Generates a JSON schema that matches a schema that defines a model.
1529 Args:
1530 schema: The core schema.
1532 Returns:
1533 The generated JSON schema.
1534 """
1535 # We do not use schema['model'].model_json_schema() here
1536 # because it could lead to inconsistent refs handling, etc.
1537 cls = cast('type[BaseModel]', schema['cls']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1538 config = cls.model_config 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1540 with self._config_wrapper_stack.push(config): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1541 json_schema = self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1543 self._update_class_schema(json_schema, cls, config) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1545 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1547 def _update_class_schema(self, json_schema: JsonSchemaValue, cls: type[Any], config: ConfigDict) -> None: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1548 """Update json_schema with the following, extracted from `config` and `cls`:
1550 * title
1551 * description
1552 * additional properties
1553 * json_schema_extra
1554 * deprecated
1556 Done in place, hence there's no return value as the original json_schema is mutated.
1557 No ref resolving is involved here, as that's not appropriate for simple updates.
1558 """
1559 from .main import BaseModel 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1560 from .root_model import RootModel 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1562 if (config_title := config.get('title')) is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1563 json_schema.setdefault('title', config_title) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1564 elif model_title_generator := config.get('model_title_generator'): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1565 title = model_title_generator(cls) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1566 if not isinstance(title, str): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1567 raise TypeError(f'model_title_generator {model_title_generator} must return str, not {title.__class__}') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1568 json_schema.setdefault('title', title) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1569 if 'title' not in json_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1570 json_schema['title'] = cls.__name__ 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1572 # BaseModel and dataclasses; don't use cls.__doc__ as it will contain the verbose class signature by default
1573 docstring = None if cls is BaseModel or dataclasses.is_dataclass(cls) else cls.__doc__ 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1575 if docstring: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1576 json_schema.setdefault('description', inspect.cleandoc(docstring)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1577 elif issubclass(cls, RootModel) and (root_description := cls.__pydantic_fields__['root'].description): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1578 json_schema.setdefault('description', root_description) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1580 extra = config.get('extra') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1581 if 'additionalProperties' not in json_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1582 if extra == 'allow': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1583 json_schema['additionalProperties'] = True 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1584 elif extra == 'forbid': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1585 json_schema['additionalProperties'] = False 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1587 json_schema_extra = config.get('json_schema_extra') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1588 if issubclass(cls, BaseModel) and cls.__pydantic_root_model__: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1589 root_json_schema_extra = cls.model_fields['root'].json_schema_extra 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1590 if json_schema_extra and root_json_schema_extra: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1591 raise ValueError( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1592 '"model_config[\'json_schema_extra\']" and "Field.json_schema_extra" on "RootModel.root"'
1593 ' field must not be set simultaneously'
1594 )
1595 if root_json_schema_extra: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1596 json_schema_extra = root_json_schema_extra 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1598 if isinstance(json_schema_extra, (staticmethod, classmethod)): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1599 # In older versions of python, this is necessary to ensure staticmethod/classmethods are callable
1600 json_schema_extra = json_schema_extra.__get__(cls) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1602 if isinstance(json_schema_extra, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1603 json_schema.update(json_schema_extra) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1604 elif callable(json_schema_extra): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1605 # FIXME: why are there type ignores here? We support two signatures for json_schema_extra callables...
1606 if len(inspect.signature(json_schema_extra).parameters) > 1: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1607 json_schema_extra(json_schema, cls) # type: ignore 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1608 else:
1609 json_schema_extra(json_schema) # type: ignore 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1610 elif json_schema_extra is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1611 raise ValueError( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1612 f"model_config['json_schema_extra']={json_schema_extra} should be a dict, callable, or None"
1613 )
1615 if hasattr(cls, '__deprecated__'): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1616 json_schema['deprecated'] = True 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1618 def resolve_ref_schema(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1619 """Resolve a JsonSchemaValue to the non-ref schema if it is a $ref schema.
1621 Args:
1622 json_schema: The schema to resolve.
1624 Returns:
1625 The resolved schema.
1627 Raises:
1628 RuntimeError: If the schema reference can't be found in definitions.
1629 """
1630 while '$ref' in json_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1631 ref = json_schema['$ref'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1632 schema_to_update = self.get_schema_from_definitions(JsonRef(ref)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1633 if schema_to_update is None: 1633 ↛ 1634line 1633 didn't jump to line 1634 because the condition on line 1633 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1634 raise RuntimeError(f'Cannot update undefined schema for $ref={ref}')
1635 json_schema = schema_to_update 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1636 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1638 def model_fields_schema(self, schema: core_schema.ModelFieldsSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1639 """Generates a JSON schema that matches a schema that defines a model's fields.
1641 Args:
1642 schema: The core schema.
1644 Returns:
1645 The generated JSON schema.
1646 """
1647 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1648 (name, self.field_is_required(field, total=True), field)
1649 for name, field in schema['fields'].items()
1650 if self.field_is_present(field)
1651 ]
1652 if self.mode == 'serialization': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1653 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1654 json_schema = self._named_required_fields_schema(named_required_fields) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1655 extras_schema = schema.get('extras_schema', None) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1656 if extras_schema is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1657 schema_to_update = self.resolve_ref_schema(json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1658 schema_to_update['additionalProperties'] = self.generate_inner(extras_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1659 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1661 def field_is_present(self, field: CoreSchemaField) -> bool: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1662 """Whether the field should be included in the generated JSON schema.
1664 Args:
1665 field: The schema for the field itself.
1667 Returns:
1668 `True` if the field should be included in the generated JSON schema, `False` otherwise.
1669 """
1670 if self.mode == 'serialization': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1671 # If you still want to include the field in the generated JSON schema,
1672 # override this method and return True
1673 return not field.get('serialization_exclude') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1674 elif self.mode == 'validation': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1675 return True 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1676 else:
1677 assert_never(self.mode)
1679 def field_is_required( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1680 self,
1681 field: core_schema.ModelField | core_schema.DataclassField | core_schema.TypedDictField,
1682 total: bool,
1683 ) -> bool:
1684 """Whether the field should be marked as required in the generated JSON schema.
1685 (Note that this is irrelevant if the field is not present in the JSON schema.).
1687 Args:
1688 field: The schema for the field itself.
1689 total: Only applies to `TypedDictField`s.
1690 Indicates if the `TypedDict` this field belongs to is total, in which case any fields that don't
1691 explicitly specify `required=False` are required.
1693 Returns:
1694 `True` if the field should be marked as required in the generated JSON schema, `False` otherwise.
1695 """
1696 if self.mode == 'serialization' and self._config.json_schema_serialization_defaults_required: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1697 return not field.get('serialization_exclude') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1698 else:
1699 if field['type'] == 'typed-dict-field': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1700 return field.get('required', total) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1701 else:
1702 return field['schema']['type'] != 'default' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1704 def dataclass_args_schema(self, schema: core_schema.DataclassArgsSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1705 """Generates a JSON schema that matches a schema that defines a dataclass's constructor arguments.
1707 Args:
1708 schema: The core schema.
1710 Returns:
1711 The generated JSON schema.
1712 """
1713 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1714 (field['name'], self.field_is_required(field, total=True), field)
1715 for field in schema['fields']
1716 if self.field_is_present(field)
1717 ]
1718 if self.mode == 'serialization': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1719 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1720 return self._named_required_fields_schema(named_required_fields) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1722 def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1723 """Generates a JSON schema that matches a schema that defines a dataclass.
1725 Args:
1726 schema: The core schema.
1728 Returns:
1729 The generated JSON schema.
1730 """
1731 from ._internal._dataclasses import is_stdlib_dataclass 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1733 cls = schema['cls'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1734 config: ConfigDict = getattr(cls, '__pydantic_config__', cast('ConfigDict', {})) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1736 with self._config_wrapper_stack.push(config): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1737 json_schema = self.generate_inner(schema['schema']).copy() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1739 self._update_class_schema(json_schema, cls, config) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1741 # Dataclass-specific handling of description
1742 if is_stdlib_dataclass(cls): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1743 # vanilla dataclass; don't use cls.__doc__ as it will contain the class signature by default
1744 description = None 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1745 else:
1746 description = None if cls.__doc__ is None else inspect.cleandoc(cls.__doc__) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1747 if description: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1748 json_schema['description'] = description 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1750 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1752 def arguments_schema(self, schema: core_schema.ArgumentsSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1753 """Generates a JSON schema that matches a schema that defines a function's arguments.
1755 Args:
1756 schema: The core schema.
1758 Returns:
1759 The generated JSON schema.
1760 """
1761 prefer_positional = schema.get('metadata', {}).get('pydantic_js_prefer_positional_arguments') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1763 arguments = schema['arguments_schema'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1764 kw_only_arguments = [a for a in arguments if a.get('mode') == 'keyword_only'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1765 kw_or_p_arguments = [a for a in arguments if a.get('mode') in {'positional_or_keyword', None}] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1766 p_only_arguments = [a for a in arguments if a.get('mode') == 'positional_only'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1767 var_args_schema = schema.get('var_args_schema') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1768 var_kwargs_schema = schema.get('var_kwargs_schema') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1770 if prefer_positional: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1771 positional_possible = not kw_only_arguments and not var_kwargs_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1772 if positional_possible: 1772 ↛ 1775line 1772 didn't jump to line 1775 because the condition on line 1772 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1773 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1775 keyword_possible = not p_only_arguments and not var_args_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1776 if keyword_possible: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1777 return self.kw_arguments_schema(kw_or_p_arguments + kw_only_arguments, var_kwargs_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1779 if not prefer_positional: 1779 ↛ 1784line 1779 didn't jump to line 1784 because the condition on line 1779 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1780 positional_possible = not kw_only_arguments and not var_kwargs_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1781 if positional_possible: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1782 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1784 raise PydanticInvalidForJsonSchema( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1785 'Unable to generate JSON schema for arguments validator with positional-only and keyword-only arguments'
1786 )
1788 def kw_arguments_schema( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1789 self, arguments: list[core_schema.ArgumentsParameter], var_kwargs_schema: CoreSchema | None
1790 ) -> JsonSchemaValue:
1791 """Generates a JSON schema that matches a schema that defines a function's keyword arguments.
1793 Args:
1794 arguments: The core schema.
1796 Returns:
1797 The generated JSON schema.
1798 """
1799 properties: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1800 required: list[str] = [] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1801 for argument in arguments: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1802 name = self.get_argument_name(argument) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1803 argument_schema = self.generate_inner(argument['schema']).copy() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1804 argument_schema['title'] = self.get_title_from_name(name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1805 properties[name] = argument_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1807 if argument['schema']['type'] != 'default': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1808 # This assumes that if the argument has a default value,
1809 # the inner schema must be of type WithDefaultSchema.
1810 # I believe this is true, but I am not 100% sure
1811 required.append(name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1813 json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1814 if required: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1815 json_schema['required'] = required 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1817 if var_kwargs_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1818 additional_properties_schema = self.generate_inner(var_kwargs_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1819 if additional_properties_schema: 1819 ↛ 1823line 1819 didn't jump to line 1823 because the condition on line 1819 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1820 json_schema['additionalProperties'] = additional_properties_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1821 else:
1822 json_schema['additionalProperties'] = False 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1823 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1825 def p_arguments_schema( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1826 self, arguments: list[core_schema.ArgumentsParameter], var_args_schema: CoreSchema | None
1827 ) -> JsonSchemaValue:
1828 """Generates a JSON schema that matches a schema that defines a function's positional arguments.
1830 Args:
1831 arguments: The core schema.
1833 Returns:
1834 The generated JSON schema.
1835 """
1836 prefix_items: list[JsonSchemaValue] = [] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1837 min_items = 0 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1839 for argument in arguments: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1840 name = self.get_argument_name(argument) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1842 argument_schema = self.generate_inner(argument['schema']).copy() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1843 argument_schema['title'] = self.get_title_from_name(name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1844 prefix_items.append(argument_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1846 if argument['schema']['type'] != 'default': 1846 ↛ 1839line 1846 didn't jump to line 1839 because the condition on line 1846 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1847 # This assumes that if the argument has a default value,
1848 # the inner schema must be of type WithDefaultSchema.
1849 # I believe this is true, but I am not 100% sure
1850 min_items += 1 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1852 json_schema: JsonSchemaValue = {'type': 'array'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1853 if prefix_items: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1854 json_schema['prefixItems'] = prefix_items 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1855 if min_items: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1856 json_schema['minItems'] = min_items 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1858 if var_args_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1859 items_schema = self.generate_inner(var_args_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1860 if items_schema: 1860 ↛ 1865line 1860 didn't jump to line 1865 because the condition on line 1860 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1861 json_schema['items'] = items_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1862 else:
1863 json_schema['maxItems'] = len(prefix_items) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1865 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1867 def get_argument_name(self, argument: core_schema.ArgumentsParameter | core_schema.ArgumentsV3Parameter) -> str: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1868 """Retrieves the name of an argument.
1870 Args:
1871 argument: The core schema.
1873 Returns:
1874 The name of the argument.
1875 """
1876 name = argument['name'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1877 if self.by_alias: 1877 ↛ 1883line 1877 didn't jump to line 1883 because the condition on line 1877 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1878 alias = argument.get('alias') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1879 if isinstance(alias, str): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1880 name = alias 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1881 else:
1882 pass # might want to do something else? 1bdenfogpADEahiqrstuvGHIcjkwlxmyBJK
1883 return name 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1885 def arguments_v3_schema(self, schema: core_schema.ArgumentsV3Schema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1886 """Generates a JSON schema that matches a schema that defines a function's arguments.
1888 Args:
1889 schema: The core schema.
1891 Returns:
1892 The generated JSON schema.
1893 """
1894 arguments = schema['arguments_schema'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1895 properties: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1896 required: list[str] = [] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1897 for argument in arguments: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1898 mode = argument.get('mode', 'positional_or_keyword') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1899 name = self.get_argument_name(argument) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1900 argument_schema = self.generate_inner(argument['schema']).copy() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1901 if mode == 'var_args': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1902 argument_schema = {'type': 'array', 'items': argument_schema} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1903 elif mode == 'var_kwargs_uniform': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1904 argument_schema = {'type': 'object', 'additionalProperties': argument_schema} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1906 argument_schema.setdefault('title', self.get_title_from_name(name)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1907 properties[name] = argument_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1909 if ( 1zCFa
1910 (mode == 'var_kwargs_unpacked_typed_dict' and 'required' in argument_schema)
1911 or mode not in {'var_args', 'var_kwargs_uniform', 'var_kwargs_unpacked_typed_dict'}
1912 and argument['schema']['type'] != 'default'
1913 ):
1914 # This assumes that if the argument has a default value,
1915 # the inner schema must be of type WithDefaultSchema.
1916 # I believe this is true, but I am not 100% sure
1917 required.append(name) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1919 json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1920 if required: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1921 json_schema['required'] = required 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1922 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1924 def call_schema(self, schema: core_schema.CallSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1925 """Generates a JSON schema that matches a schema that defines a function call.
1927 Args:
1928 schema: The core schema.
1930 Returns:
1931 The generated JSON schema.
1932 """
1933 return self.generate_inner(schema['arguments_schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1935 def custom_error_schema(self, schema: core_schema.CustomErrorSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1936 """Generates a JSON schema that matches a schema that defines a custom error.
1938 Args:
1939 schema: The core schema.
1941 Returns:
1942 The generated JSON schema.
1943 """
1944 return self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1946 def json_schema(self, schema: core_schema.JsonSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1947 """Generates a JSON schema that matches a schema that defines a JSON object.
1949 Args:
1950 schema: The core schema.
1952 Returns:
1953 The generated JSON schema.
1954 """
1955 content_core_schema = schema.get('schema') or core_schema.any_schema() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1956 content_json_schema = self.generate_inner(content_core_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1957 if self.mode == 'validation': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1958 return {'type': 'string', 'contentMediaType': 'application/json', 'contentSchema': content_json_schema} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1959 else:
1960 # self.mode == 'serialization'
1961 return content_json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1963 def url_schema(self, schema: core_schema.UrlSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1964 """Generates a JSON schema that matches a schema that defines a URL.
1966 Args:
1967 schema: The core schema.
1969 Returns:
1970 The generated JSON schema.
1971 """
1972 json_schema = {'type': 'string', 'format': 'uri', 'minLength': 1} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1973 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1974 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1976 def multi_host_url_schema(self, schema: core_schema.MultiHostUrlSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1977 """Generates a JSON schema that matches a schema that defines a URL that can be used with multiple hosts.
1979 Args:
1980 schema: The core schema.
1982 Returns:
1983 The generated JSON schema.
1984 """
1985 # Note: 'multi-host-uri' is a custom/pydantic-specific format, not part of the JSON Schema spec
1986 json_schema = {'type': 'string', 'format': 'multi-host-uri', 'minLength': 1} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1987 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1988 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
1990 def uuid_schema(self, schema: core_schema.UuidSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
1991 """Generates a JSON schema that matches a UUID.
1993 Args:
1994 schema: The core schema.
1996 Returns:
1997 The generated JSON schema.
1998 """
1999 return {'type': 'string', 'format': 'uuid'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2001 def definitions_schema(self, schema: core_schema.DefinitionsSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2002 """Generates a JSON schema that matches a schema that defines a JSON object with definitions.
2004 Args:
2005 schema: The core schema.
2007 Returns:
2008 The generated JSON schema.
2009 """
2010 for definition in schema['definitions']: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2011 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2012 self.generate_inner(definition) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2013 except PydanticInvalidForJsonSchema as e:
2014 core_ref: CoreRef = CoreRef(definition['ref']) # type: ignore
2015 self._core_defs_invalid_for_json_schema[self.get_defs_ref((core_ref, self.mode))] = e
2016 continue
2017 return self.generate_inner(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2019 def definition_ref_schema(self, schema: core_schema.DefinitionReferenceSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2020 """Generates a JSON schema that matches a schema that references a definition.
2022 Args:
2023 schema: The core schema.
2025 Returns:
2026 The generated JSON schema.
2027 """
2028 core_ref = CoreRef(schema['schema_ref']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2029 _, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2030 return ref_json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2032 def ser_schema( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2033 self, schema: core_schema.SerSchema | core_schema.IncExSeqSerSchema | core_schema.IncExDictSerSchema
2034 ) -> JsonSchemaValue | None:
2035 """Generates a JSON schema that matches a schema that defines a serialized object.
2037 Args:
2038 schema: The core schema.
2040 Returns:
2041 The generated JSON schema.
2042 """
2043 schema_type = schema['type'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2044 if schema_type == 'function-plain' or schema_type == 'function-wrap': 2044 ↛ 2049line 2044 didn't jump to line 2049 because the condition on line 2044 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2045 # PlainSerializerFunctionSerSchema or WrapSerializerFunctionSerSchema
2046 return_schema = schema.get('return_schema') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2047 if return_schema is not None: 2047 ↛ 2055line 2047 didn't jump to line 2055 because the condition on line 2047 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2048 return self.generate_inner(return_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2049 elif schema_type == 'format' or schema_type == 'to-string':
2050 # FormatSerSchema or ToStringSerSchema
2051 return self.str_schema(core_schema.str_schema())
2052 elif schema['type'] == 'model':
2053 # ModelSerSchema
2054 return self.generate_inner(schema['schema'])
2055 return None
2057 def complex_schema(self, schema: core_schema.ComplexSchema) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2058 """Generates a JSON schema that matches a complex number.
2060 JSON has no standard way to represent complex numbers. Complex number is not a numeric
2061 type. Here we represent complex number as strings following the rule defined by Python.
2062 For instance, '1+2j' is an accepted complex string. Details can be found in
2063 [Python's `complex` documentation][complex].
2065 Args:
2066 schema: The core schema.
2068 Returns:
2069 The generated JSON schema.
2070 """
2071 return {'type': 'string'} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2073 # ### Utility methods
2075 def get_title_from_name(self, name: str) -> str: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2076 """Retrieves a title from a name.
2078 Args:
2079 name: The name to retrieve a title from.
2081 Returns:
2082 The title.
2083 """
2084 return name.title().replace('_', ' ').strip() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2086 def field_title_should_be_set(self, schema: CoreSchemaOrField) -> bool: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2087 """Returns true if a field with the given schema should have a title set based on the field name.
2089 Intuitively, we want this to return true for schemas that wouldn't otherwise provide their own title
2090 (e.g., int, float, str), and false for those that would (e.g., BaseModel subclasses).
2092 Args:
2093 schema: The schema to check.
2095 Returns:
2096 `True` if the field should have a title set, `False` otherwise.
2097 """
2098 if _core_utils.is_core_schema_field(schema): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2099 if schema['type'] == 'computed-field': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2100 field_schema = schema['return_schema'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2101 else:
2102 field_schema = schema['schema'] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2103 return self.field_title_should_be_set(field_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2105 elif _core_utils.is_core_schema(schema): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2106 if schema.get('ref'): # things with refs, such as models and enums, should not have titles set 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2107 return False 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2108 if schema['type'] in {'default', 'nullable', 'definitions'}: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2109 return self.field_title_should_be_set(schema['schema']) # type: ignore[typeddict-item] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2110 if _core_utils.is_function_with_inner_schema(schema): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2111 return self.field_title_should_be_set(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2112 if schema['type'] == 'definition-ref': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2113 # Referenced schemas should not have titles set for the same reason
2114 # schemas with refs should not
2115 return False 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2116 return True # anything else should have title set 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2118 else:
2119 raise PydanticInvalidForJsonSchema(f'Unexpected schema type: schema={schema}') # pragma: no cover
2121 def normalize_name(self, name: str) -> str: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2122 """Normalizes a name to be used as a key in a dictionary.
2124 Args:
2125 name: The name to normalize.
2127 Returns:
2128 The normalized name.
2129 """
2130 return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name).replace('.', '__') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2132 def get_defs_ref(self, core_mode_ref: CoreModeRef) -> DefsRef: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2133 """Override this method to change the way that definitions keys are generated from a core reference.
2135 Args:
2136 core_mode_ref: The core reference.
2138 Returns:
2139 The definitions key.
2140 """
2141 # Split the core ref into "components"; generic origins and arguments are each separate components
2142 core_ref, mode = core_mode_ref 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2143 components = re.split(r'([\][,])', core_ref) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2144 # Remove IDs from each component
2145 components = [x.rsplit(':', 1)[0] for x in components] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2146 core_ref_no_id = ''.join(components) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2147 # Remove everything before the last period from each "component"
2148 components = [re.sub(r'(?:[^.[\]]+\.)+((?:[^.[\]]+))', r'\1', x) for x in components] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2149 short_ref = ''.join(components) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2151 mode_title = _MODE_TITLE_MAPPING[mode] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2153 # It is important that the generated defs_ref values be such that at least one choice will not
2154 # be generated for any other core_ref. Currently, this should be the case because we include
2155 # the id of the source type in the core_ref
2156 name = DefsRef(self.normalize_name(short_ref)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2157 name_mode = DefsRef(self.normalize_name(short_ref) + f'-{mode_title}') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2158 module_qualname = DefsRef(self.normalize_name(core_ref_no_id)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2159 module_qualname_mode = DefsRef(f'{module_qualname}-{mode_title}') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2160 module_qualname_id = DefsRef(self.normalize_name(core_ref)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2161 occurrence_index = self._collision_index.get(module_qualname_id) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2162 if occurrence_index is None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2163 self._collision_counter[module_qualname] += 1 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2164 occurrence_index = self._collision_index[module_qualname_id] = self._collision_counter[module_qualname] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2166 module_qualname_occurrence = DefsRef(f'{module_qualname}__{occurrence_index}') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2167 module_qualname_occurrence_mode = DefsRef(f'{module_qualname_mode}__{occurrence_index}') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2169 self._prioritized_defsref_choices[module_qualname_occurrence_mode] = [ 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2170 name,
2171 name_mode,
2172 module_qualname,
2173 module_qualname_mode,
2174 module_qualname_occurrence,
2175 module_qualname_occurrence_mode,
2176 ]
2178 return module_qualname_occurrence_mode 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2180 def get_cache_defs_ref_schema(self, core_ref: CoreRef) -> tuple[DefsRef, JsonSchemaValue]: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2181 """This method wraps the get_defs_ref method with some cache-lookup/population logic,
2182 and returns both the produced defs_ref and the JSON schema that will refer to the right definition.
2184 Args:
2185 core_ref: The core reference to get the definitions reference for.
2187 Returns:
2188 A tuple of the definitions reference and the JSON schema that will refer to it.
2189 """
2190 core_mode_ref = (core_ref, self.mode) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2191 maybe_defs_ref = self.core_to_defs_refs.get(core_mode_ref) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2192 if maybe_defs_ref is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2193 json_ref = self.core_to_json_refs[core_mode_ref] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2194 return maybe_defs_ref, {'$ref': json_ref} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2196 defs_ref = self.get_defs_ref(core_mode_ref) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2198 # populate the ref translation mappings
2199 self.core_to_defs_refs[core_mode_ref] = defs_ref 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2200 self.defs_to_core_refs[defs_ref] = core_mode_ref 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2202 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2203 self.core_to_json_refs[core_mode_ref] = json_ref 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2204 self.json_to_defs_refs[json_ref] = defs_ref 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2205 ref_json_schema = {'$ref': json_ref} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2206 return defs_ref, ref_json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2208 def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2209 """Remove any sibling keys that are redundant with the referenced schema.
2211 Args:
2212 json_schema: The schema to remove redundant sibling keys from.
2214 Returns:
2215 The schema with redundant sibling keys removed.
2216 """
2217 if '$ref' in json_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2218 # prevent modifications to the input; this copy may be safe to drop if there is significant overhead
2219 json_schema = json_schema.copy() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2221 referenced_json_schema = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2222 if referenced_json_schema is None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2223 # This can happen when building schemas for models with not-yet-defined references.
2224 # It may be a good idea to do a recursive pass at the end of the generation to remove
2225 # any redundant override keys.
2226 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2227 for k, v in list(json_schema.items()): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2228 if k == '$ref': 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2229 continue 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2230 if k in referenced_json_schema and referenced_json_schema[k] == v: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2231 del json_schema[k] # redundant key 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2233 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2235 def get_schema_from_definitions(self, json_ref: JsonRef) -> JsonSchemaValue | None: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2236 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2237 def_ref = self.json_to_defs_refs[json_ref] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2238 if def_ref in self._core_defs_invalid_for_json_schema: 2238 ↛ 2239line 2238 didn't jump to line 2239 because the condition on line 2238 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2239 raise self._core_defs_invalid_for_json_schema[def_ref]
2240 return self.definitions.get(def_ref, None) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2241 except KeyError: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2242 if json_ref.startswith(('http://', 'https://')): 2242 ↛ 2244line 2242 didn't jump to line 2244 because the condition on line 2242 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2243 return None 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2244 raise
2246 def encode_default(self, dft: Any) -> Any: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2247 """Encode a default value to a JSON-serializable value.
2249 This is used to encode default values for fields in the generated JSON schema.
2251 Args:
2252 dft: The default value to encode.
2254 Returns:
2255 The encoded default value.
2256 """
2257 from .type_adapter import TypeAdapter, _type_has_config 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2259 config = self._config 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2260 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2261 default = ( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2262 dft
2263 if _type_has_config(type(dft))
2264 else TypeAdapter(type(dft), config=config.config_dict).dump_python(
2265 dft, by_alias=self.by_alias, mode='json'
2266 )
2267 )
2268 except PydanticSchemaGenerationError: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2269 raise pydantic_core.PydanticSerializationError(f'Unable to encode default value {dft}') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2271 return pydantic_core.to_jsonable_python( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2272 default, timedelta_mode=config.ser_json_timedelta, bytes_mode=config.ser_json_bytes, by_alias=self.by_alias
2273 )
2275 def update_with_validations( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2276 self, json_schema: JsonSchemaValue, core_schema: CoreSchema, mapping: dict[str, str]
2277 ) -> None:
2278 """Update the json_schema with the corresponding validations specified in the core_schema,
2279 using the provided mapping to translate keys in core_schema to the appropriate keys for a JSON schema.
2281 Args:
2282 json_schema: The JSON schema to update.
2283 core_schema: The core schema to get the validations from.
2284 mapping: A mapping from core_schema attribute names to the corresponding JSON schema attribute names.
2285 """
2286 for core_key, json_schema_key in mapping.items(): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2287 if core_key in core_schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2288 json_schema[json_schema_key] = core_schema[core_key] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2290 class ValidationsMapping: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2291 """This class just contains mappings from core_schema attribute names to the corresponding
2292 JSON schema attribute names. While I suspect it is unlikely to be necessary, you can in
2293 principle override this class in a subclass of GenerateJsonSchema (by inheriting from
2294 GenerateJsonSchema.ValidationsMapping) to change these mappings.
2295 """
2297 numeric = { 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2298 'multiple_of': 'multipleOf',
2299 'le': 'maximum',
2300 'ge': 'minimum',
2301 'lt': 'exclusiveMaximum',
2302 'gt': 'exclusiveMinimum',
2303 }
2304 bytes = { 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2305 'min_length': 'minLength',
2306 'max_length': 'maxLength',
2307 }
2308 string = { 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2309 'min_length': 'minLength',
2310 'max_length': 'maxLength',
2311 'pattern': 'pattern',
2312 }
2313 array = { 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2314 'min_length': 'minItems',
2315 'max_length': 'maxItems',
2316 }
2317 object = { 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2318 'min_length': 'minProperties',
2319 'max_length': 'maxProperties',
2320 }
2322 def get_flattened_anyof(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2323 members = [] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2324 for schema in schemas: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2325 if len(schema) == 1 and 'anyOf' in schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2326 members.extend(schema['anyOf']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2327 else:
2328 members.append(schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2329 members = _deduplicate_schemas(members) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2330 if len(members) == 1: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2331 return members[0] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2332 return {'anyOf': members} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2334 def get_json_ref_counts(self, json_schema: JsonSchemaValue) -> dict[JsonRef, int]: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2335 """Get all values corresponding to the key '$ref' anywhere in the json_schema."""
2336 json_refs: dict[JsonRef, int] = Counter() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2338 def _add_json_refs(schema: Any) -> None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2339 if isinstance(schema, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2340 if '$ref' in schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2341 json_ref = JsonRef(schema['$ref']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2342 if not isinstance(json_ref, str): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2343 return # in this case, '$ref' might have been the name of a property 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2344 already_visited = json_ref in json_refs 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2345 json_refs[json_ref] += 1 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2346 if already_visited: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2347 return # prevent recursion on a definition that was already visited 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2348 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2349 defs_ref = self.json_to_defs_refs[json_ref] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2350 if defs_ref in self._core_defs_invalid_for_json_schema: 2350 ↛ 2351line 2350 didn't jump to line 2351 because the condition on line 2350 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2351 raise self._core_defs_invalid_for_json_schema[defs_ref]
2352 _add_json_refs(self.definitions[defs_ref]) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2353 except KeyError: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2354 if not json_ref.startswith(('http://', 'https://')): 2354 ↛ 2355line 2354 didn't jump to line 2355 because the condition on line 2354 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2355 raise
2357 for k, v in schema.items(): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2358 if k == 'examples' and isinstance(v, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2359 # Skip examples that may contain arbitrary values and references
2360 # (see the comment in `_get_all_json_refs` for more details).
2361 continue 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2362 _add_json_refs(v) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2363 elif isinstance(schema, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2364 for v in schema: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2365 _add_json_refs(v) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2367 _add_json_refs(json_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2368 return json_refs 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2370 def handle_invalid_for_json_schema(self, schema: CoreSchemaOrField, error_info: str) -> JsonSchemaValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2371 raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2373 def emit_warning(self, kind: JsonSchemaWarningKind, detail: str) -> None: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2374 """This method simply emits PydanticJsonSchemaWarnings based on handling in the `warning_message` method."""
2375 message = self.render_warning_message(kind, detail) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2376 if message is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2377 warnings.warn(message, PydanticJsonSchemaWarning) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2379 def render_warning_message(self, kind: JsonSchemaWarningKind, detail: str) -> str | None: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2380 """This method is responsible for ignoring warnings as desired, and for formatting the warning messages.
2382 You can override the value of `ignored_warning_kinds` in a subclass of GenerateJsonSchema
2383 to modify what warnings are generated. If you want more control, you can override this method;
2384 just return None in situations where you don't want warnings to be emitted.
2386 Args:
2387 kind: The kind of warning to render. It can be one of the following:
2389 - 'skipped-choice': A choice field was skipped because it had no valid choices.
2390 - 'non-serializable-default': A default value was skipped because it was not JSON-serializable.
2391 detail: A string with additional details about the warning.
2393 Returns:
2394 The formatted warning message, or `None` if no warning should be emitted.
2395 """
2396 if kind in self.ignored_warning_kinds: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2397 return None 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2398 return f'{detail} [{kind}]' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2400 def _build_definitions_remapping(self) -> _DefinitionsRemapping: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2401 defs_to_json: dict[DefsRef, JsonRef] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2402 for defs_refs in self._prioritized_defsref_choices.values(): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2403 for defs_ref in defs_refs: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2404 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2405 defs_to_json[defs_ref] = json_ref 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2407 return _DefinitionsRemapping.from_prioritized_choices( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2408 self._prioritized_defsref_choices, defs_to_json, self.definitions
2409 )
2411 def _garbage_collect_definitions(self, schema: JsonSchemaValue) -> None: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2412 visited_defs_refs: set[DefsRef] = set() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2413 unvisited_json_refs = _get_all_json_refs(schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2414 while unvisited_json_refs: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2415 next_json_ref = unvisited_json_refs.pop() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2416 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2417 next_defs_ref = self.json_to_defs_refs[next_json_ref] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2418 if next_defs_ref in visited_defs_refs: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2419 continue 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2420 visited_defs_refs.add(next_defs_ref) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2421 unvisited_json_refs.update(_get_all_json_refs(self.definitions[next_defs_ref])) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2422 except KeyError: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2423 if not next_json_ref.startswith(('http://', 'https://')): 2423 ↛ 2424line 2423 didn't jump to line 2424 because the condition on line 2423 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2424 raise
2426 self.definitions = {k: v for k, v in self.definitions.items() if k in visited_defs_refs} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2429# ##### Start JSON Schema Generation Functions #####
2432def model_json_schema( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2433 cls: type[BaseModel] | type[PydanticDataclass],
2434 by_alias: bool = True,
2435 ref_template: str = DEFAULT_REF_TEMPLATE,
2436 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
2437 mode: JsonSchemaMode = 'validation',
2438) -> dict[str, Any]:
2439 """Utility function to generate a JSON Schema for a model.
2441 Args:
2442 cls: The model class to generate a JSON Schema for.
2443 by_alias: If `True` (the default), fields will be serialized according to their alias.
2444 If `False`, fields will be serialized according to their attribute name.
2445 ref_template: The template to use for generating JSON Schema references.
2446 schema_generator: The class to use for generating the JSON Schema.
2447 mode: The mode to use for generating the JSON Schema. It can be one of the following:
2449 - 'validation': Generate a JSON Schema for validating data.
2450 - 'serialization': Generate a JSON Schema for serializing data.
2452 Returns:
2453 The generated JSON Schema.
2454 """
2455 from .main import BaseModel 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2457 schema_generator_instance = schema_generator(by_alias=by_alias, ref_template=ref_template) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2459 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2460 cls.__pydantic_core_schema__.rebuild() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2462 if cls is BaseModel: 2462 ↛ 2463line 2462 didn't jump to line 2463 because the condition on line 2462 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2463 raise AttributeError('model_json_schema() must be called on a subclass of BaseModel, not BaseModel itself.')
2465 assert not isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it' 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2466 return schema_generator_instance.generate(cls.__pydantic_core_schema__, mode=mode) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2469def models_json_schema( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2470 models: Sequence[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode]],
2471 *,
2472 by_alias: bool = True,
2473 title: str | None = None,
2474 description: str | None = None,
2475 ref_template: str = DEFAULT_REF_TEMPLATE,
2476 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
2477) -> tuple[dict[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]:
2478 """Utility function to generate a JSON Schema for multiple models.
2480 Args:
2481 models: A sequence of tuples of the form (model, mode).
2482 by_alias: Whether field aliases should be used as keys in the generated JSON Schema.
2483 title: The title of the generated JSON Schema.
2484 description: The description of the generated JSON Schema.
2485 ref_template: The reference template to use for generating JSON Schema references.
2486 schema_generator: The schema generator to use for generating the JSON Schema.
2488 Returns:
2489 A tuple where:
2490 - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
2491 whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
2492 JsonRef references to definitions that are defined in the second returned element.)
2493 - The second element is a JSON schema containing all definitions referenced in the first returned
2494 element, along with the optional title and description keys.
2495 """
2496 for cls, _ in models: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2497 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 2497 ↛ 2498line 2497 didn't jump to line 2498 because the condition on line 2497 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2498 cls.__pydantic_core_schema__.rebuild()
2500 instance = schema_generator(by_alias=by_alias, ref_template=ref_template) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2501 inputs: list[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode, CoreSchema]] = [ 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2502 (m, mode, m.__pydantic_core_schema__) for m, mode in models
2503 ]
2504 json_schemas_map, definitions = instance.generate_definitions(inputs) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2506 json_schema: dict[str, Any] = {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2507 if definitions: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2508 json_schema['$defs'] = definitions 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2509 if title: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2510 json_schema['title'] = title 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2511 if description: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2512 json_schema['description'] = description 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2514 return json_schemas_map, json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2517# ##### End JSON Schema Generation Functions #####
2520_HashableJsonValue: TypeAlias = Union[ 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2521 int, float, str, bool, None, tuple['_HashableJsonValue', ...], tuple[tuple[str, '_HashableJsonValue'], ...]
2522]
2525def _deduplicate_schemas(schemas: Iterable[JsonDict]) -> list[JsonDict]: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2526 return list({_make_json_hashable(schema): schema for schema in schemas}.values()) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2529def _make_json_hashable(value: JsonValue) -> _HashableJsonValue: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2530 if isinstance(value, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2531 return tuple(sorted((k, _make_json_hashable(v)) for k, v in value.items())) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2532 elif isinstance(value, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2533 return tuple(_make_json_hashable(v) for v in value) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2534 else:
2535 return value 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2538@dataclasses.dataclass(**_internal_dataclass.slots_true) 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2539class WithJsonSchema: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2540 """!!! abstract "Usage Documentation"
2541 [`WithJsonSchema` Annotation](../concepts/json_schema.md#withjsonschema-annotation)
2543 Add this as an annotation on a field to override the (base) JSON schema that would be generated for that field.
2544 This provides a way to set a JSON schema for types that would otherwise raise errors when producing a JSON schema,
2545 such as Callable, or types that have an is-instance core schema, without needing to go so far as creating a
2546 custom subclass of pydantic.json_schema.GenerateJsonSchema.
2547 Note that any _modifications_ to the schema that would normally be made (such as setting the title for model fields)
2548 will still be performed.
2550 If `mode` is set this will only apply to that schema generation mode, allowing you
2551 to set different json schemas for validation and serialization.
2552 """
2554 json_schema: JsonSchemaValue | None 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2555 mode: Literal['validation', 'serialization'] | None = None 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2557 def __get_pydantic_json_schema__( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2558 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2559 ) -> JsonSchemaValue:
2560 mode = self.mode or handler.mode 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2561 if mode != handler.mode: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2562 return handler(core_schema) 1zbefgALcklmB
2563 if self.json_schema is None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2564 # This exception is handled in pydantic.json_schema.GenerateJsonSchema._named_required_fields_schema
2565 raise PydanticOmit 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2566 else:
2567 return self.json_schema.copy() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2569 def __hash__(self) -> int: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2570 return hash(type(self.mode)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2573class Examples: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2574 """Add examples to a JSON schema.
2576 If the JSON Schema already contains examples, the provided examples
2577 will be appended.
2579 If `mode` is set this will only apply to that schema generation mode,
2580 allowing you to add different examples for validation and serialization.
2581 """
2583 @overload 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2584 @deprecated('Using a dict for `examples` is deprecated since v2.9 and will be removed in v3.0. Use a list instead.') 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2585 def __init__( 1zCbdenfogpADEMNhiqrstuvGHIPLOcjkwlxmyBJK
2586 self, examples: dict[str, Any], mode: Literal['validation', 'serialization'] | None = None 1bdenfogpFahiqrstuvPcjkwlxmy
2587 ) -> None: ... 1fogpFastuvPlxmy
2589 @overload 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2590 def __init__(self, examples: list[Any], mode: Literal['validation', 'serialization'] | None = None) -> None: ... 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2592 def __init__( 1zCbdenfogpADEMNhiqrstuvGHIPLOcjkwlxmyBJK
2593 self, examples: dict[str, Any] | list[Any], mode: Literal['validation', 'serialization'] | None = None
2594 ) -> None:
2595 if isinstance(examples, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2596 warnings.warn( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2597 'Using a dict for `examples` is deprecated, use a list instead.',
2598 PydanticDeprecatedSince29,
2599 stacklevel=2,
2600 )
2601 self.examples = examples 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2602 self.mode = mode 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2604 def __get_pydantic_json_schema__( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2605 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2606 ) -> JsonSchemaValue:
2607 mode = self.mode or handler.mode 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2608 json_schema = handler(core_schema) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2609 if mode != handler.mode: 2609 ↛ 2610line 2609 didn't jump to line 2610 because the condition on line 2609 was never true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2610 return json_schema
2611 examples = json_schema.get('examples') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2612 if examples is None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2613 json_schema['examples'] = to_jsonable_python(self.examples) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2614 if isinstance(examples, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2615 if isinstance(self.examples, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2616 warnings.warn( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2617 'Updating existing JSON Schema examples of type dict with examples of type list. '
2618 'Only the existing examples values will be retained. Note that dict support for '
2619 'examples is deprecated and will be removed in v3.0.',
2620 UserWarning,
2621 )
2622 json_schema['examples'] = to_jsonable_python( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2623 [ex for value in examples.values() for ex in value] + self.examples
2624 )
2625 else:
2626 json_schema['examples'] = to_jsonable_python({**examples, **self.examples}) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2627 if isinstance(examples, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2628 if isinstance(self.examples, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2629 json_schema['examples'] = to_jsonable_python(examples + self.examples) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2630 elif isinstance(self.examples, dict): 2630 ↛ 2641line 2630 didn't jump to line 2641 because the condition on line 2630 was always true1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2631 warnings.warn( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2632 'Updating existing JSON Schema examples of type list with examples of type dict. '
2633 'Only the examples values will be retained. Note that dict support for '
2634 'examples is deprecated and will be removed in v3.0.',
2635 UserWarning,
2636 )
2637 json_schema['examples'] = to_jsonable_python( 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2638 examples + [ex for value in self.examples.values() for ex in value]
2639 )
2641 return json_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2643 def __hash__(self) -> int: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2644 return hash(type(self.mode)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2647def _get_all_json_refs(item: Any) -> set[JsonRef]: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2648 """Get all the definitions references from a JSON schema."""
2649 refs: set[JsonRef] = set() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2650 stack = [item] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2652 while stack: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2653 current = stack.pop() 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2654 if isinstance(current, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2655 for key, value in current.items(): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2656 if key == 'examples' and isinstance(value, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2657 # Skip examples that may contain arbitrary values and references
2658 # (e.g. `{"examples": [{"$ref": "..."}]}`). Note: checking for value
2659 # of type list is necessary to avoid skipping valid portions of the schema,
2660 # for instance when "examples" is used as a property key. A more robust solution
2661 # could be found, but would require more advanced JSON Schema parsing logic.
2662 continue 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2663 if key == '$ref' and isinstance(value, str): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2664 refs.add(JsonRef(value)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2665 elif isinstance(value, dict): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2666 stack.append(value) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2667 elif isinstance(value, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2668 stack.extend(value) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2669 elif isinstance(current, list): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2670 stack.extend(current) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2672 return refs 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2675AnyType = TypeVar('AnyType') 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2677if TYPE_CHECKING: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2678 SkipJsonSchema = Annotated[AnyType, ...]
2679else:
2681 @dataclasses.dataclass(**_internal_dataclass.slots_true) 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2682 class SkipJsonSchema: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2683 """!!! abstract "Usage Documentation"
2684 [`SkipJsonSchema` Annotation](../concepts/json_schema.md#skipjsonschema-annotation)
2686 Add this as an annotation on a field to skip generating a JSON schema for that field.
2688 Example:
2689 ```python
2690 from pprint import pprint
2691 from typing import Union
2693 from pydantic import BaseModel
2694 from pydantic.json_schema import SkipJsonSchema
2696 class Model(BaseModel):
2697 a: Union[int, None] = None # (1)!
2698 b: Union[int, SkipJsonSchema[None]] = None # (2)!
2699 c: SkipJsonSchema[Union[int, None]] = None # (3)!
2701 pprint(Model.model_json_schema())
2702 '''
2703 {
2704 'properties': {
2705 'a': {
2706 'anyOf': [
2707 {'type': 'integer'},
2708 {'type': 'null'}
2709 ],
2710 'default': None,
2711 'title': 'A'
2712 },
2713 'b': {
2714 'default': None,
2715 'title': 'B',
2716 'type': 'integer'
2717 }
2718 },
2719 'title': 'Model',
2720 'type': 'object'
2721 }
2722 '''
2723 ```
2725 1. The integer and null types are both included in the schema for `a`.
2726 2. The integer type is the only type included in the schema for `b`.
2727 3. The entirety of the `c` field is omitted from the schema.
2728 """
2730 def __class_getitem__(cls, item: AnyType) -> AnyType: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2731 return Annotated[item, cls()] 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2733 def __get_pydantic_json_schema__( 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2734 self, core_schema: CoreSchema, handler: GetJsonSchemaHandler
2735 ) -> JsonSchemaValue:
2736 raise PydanticOmit 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2738 def __hash__(self) -> int: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2739 return hash(type(self)) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2742def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2743 if cls is not None: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2744 try: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2745 return _decorators.get_attribute_from_bases(cls, '__pydantic_config__') 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2746 except AttributeError: 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2747 pass 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2748 return {} 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2751def _get_ser_schema_for_default_value(schema: CoreSchema) -> core_schema.PlainSerializerFunctionSerSchema | None: 1zCbdenfogpADEFaMNhiqrstuvGHIPLOcjkwlxmyBJK
2752 """Get a `'function-plain'` serialization schema that can be used to serialize a default value.
2754 This takes into account having the serialization schema nested under validation schema(s).
2755 """
2756 if ( 1zCbdenFaMNhiqrLOcjkw
2757 (ser_schema := schema.get('serialization'))
2758 and ser_schema['type'] == 'function-plain'
2759 and not ser_schema.get('info_arg')
2760 ):
2761 return ser_schema 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2762 if _core_utils.is_function_with_inner_schema(schema): 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK
2763 return _get_ser_schema_for_default_value(schema['schema']) 1zCbdenfogpADEFaMNhiqrstuvGHILOcjkwlxmyBJK