Coverage for pydantic/json_schema.py: 94.65%
1105 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-22 09:30 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-22 09:30 +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 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
14import dataclasses 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
15import inspect 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
16import math 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
17import os 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
18import re 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
19import warnings 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
20from collections import Counter, defaultdict 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
21from collections.abc import Hashable, Iterable, Sequence 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
22from copy import deepcopy 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
23from enum import Enum 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
24from re import Pattern 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
25from typing import ( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
39from pydantic_core import CoreSchema, PydanticOmit, core_schema, to_jsonable_python 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
40from pydantic_core.core_schema import ComputedField 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
41from typing_extensions import TypeAlias, assert_never, deprecated, final 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
42from typing_inspection.introspection import get_literal_values 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
44from pydantic.warnings import PydanticDeprecatedSince26, PydanticDeprecatedSince29 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
46from ._internal import ( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
56from .config import JsonDict, JsonValue 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
57from .errors import PydanticInvalidForJsonSchema, PydanticSchemaGenerationError, PydanticUserError 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
59if TYPE_CHECKING: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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] 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
68""" 1bdenfogpADOahiqrstuvFGMPcjkwlxmyBHN
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] 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
75""" 1bdenfogpADOahiqrstuvFGMPcjkwlxmyBHN
76A type alias for a JSON schema value. This is a dictionary of string keys to arbitrary JSON values.
77"""
79JsonSchemaMode = Literal['validation', 'serialization'] 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
80""" 1bdenfogpADOahiqrstuvFGMPcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
92JsonSchemaWarningKind = Literal['skipped-choice', 'non-serializable-default', 'skipped-discriminator'] 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
93""" 1bdenfogpADOahiqrstuvFGMPcjkwlxmyBHN
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): 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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() 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
110"""A sentinel value used to indicate that no default value should be used when generating a JSON Schema 1bdenfogpADOahiqrstuvFGMPcjkwlxmyBHN
111for a core schema with a default value.
112"""
115# ##### JSON Schema Generation #####
116DEFAULT_REF_TEMPLATE = '#/$defs/{model}' 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
117"""The default format string used to generate reference names.""" 1bdenfogpADOahiqrstuvFGMPcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
131CoreModeRef = tuple[CoreRef, JsonSchemaMode] 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
132JsonSchemaKeyT = TypeVar('JsonSchemaKeyT', bound=Hashable) 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
135@dataclasses.dataclass(**_internal_dataclass.slots_true) 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
136class _DefinitionsRemapping: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
137 defs_remapping: dict[DefsRef, DefsRef] 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
138 json_remapping: dict[JsonRef, JsonRef] 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
140 @staticmethod 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
141 def from_prioritized_choices( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
155 definitions_schema = {'$defs': copied_definitions} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 complete1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
159 for defs_ref in copied_definitions: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
160 alternatives = prioritized_choices[defs_ref] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
161 for alternative in alternatives: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
162 schemas_for_alternatives[alternative].append(copied_definitions[defs_ref]) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
167 schemas_for_alternatives[defs_ref] = _deduplicate_schemas(schemas_for_alternatives[defs_ref]) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
169 # Build the remapping
170 defs_remapping: dict[DefsRef, DefsRef] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
171 json_remapping: dict[JsonRef, JsonRef] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
172 for original_defs_ref in definitions: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
173 alternatives = prioritized_choices[original_defs_ref] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
176 defs_remapping[original_defs_ref] = remapped_defs_ref 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
177 json_remapping[defs_to_json[original_defs_ref]] = defs_to_json[remapped_defs_ref] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
178 remapping = _DefinitionsRemapping(defs_remapping, json_remapping) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
179 new_definitions_schema = remapping.remap_json_schema({'$defs': copied_definitions}) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
180 if definitions_schema == new_definitions_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
181 # We've reached the fixed point
182 return remapping 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
183 definitions_schema = new_definitions_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
185 raise PydanticInvalidForJsonSchema('Failed to simplify the JSON schema definitions')
187 def remap_defs_ref(self, ref: DefsRef) -> DefsRef: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
188 return self.defs_remapping.get(ref, ref) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
190 def remap_json_ref(self, ref: JsonRef) -> JsonRef: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
191 return self.json_remapping.get(ref, ref) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
193 def remap_json_schema(self, schema: Any) -> Any: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
194 """
195 Recursively update the JSON schema replacing all $refs
196 """
197 if isinstance(schema, str): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
200 elif isinstance(schema, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
201 return [self.remap_json_schema(item) for item in schema] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
202 elif isinstance(schema, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
203 for key, value in schema.items(): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
204 if key == '$ref' and isinstance(value, str): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
205 schema['$ref'] = self.remap_json_ref(JsonRef(value)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
206 elif key == '$defs': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
207 schema['$defs'] = { 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
213 return schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
216class GenerateJsonSchema: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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' 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
256 def __init__(self, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE): 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
257 self.by_alias = by_alias 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
258 self.ref_template = ref_template 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
260 self.core_to_json_refs: dict[CoreModeRef, JsonRef] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
261 self.core_to_defs_refs: dict[CoreModeRef, DefsRef] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
262 self.defs_to_core_refs: dict[DefsRef, CoreModeRef] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
263 self.json_to_defs_refs: dict[JsonRef, DefsRef] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
265 self.definitions: dict[DefsRef, JsonSchemaValue] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
266 self._config_wrapper_stack = _config.ConfigWrapperStack(_config.ConfigWrapper({})) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
268 self._mode: JsonSchemaMode = 'validation' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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]] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
275 self._collision_counter: dict[str, int] = defaultdict(int) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
276 self._collision_index: dict[str, int] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
278 self._schema_type_to_method = self.build_schema_type_to_method() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
292 @property 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
293 def _config(self) -> _config.ConfigWrapper: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
294 return self._config_wrapper_stack.tail 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
296 @property 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
297 def mode(self) -> JsonSchemaMode: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
298 if self._config.json_schema_mode_override is not None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
299 return self._config.json_schema_mode_override 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
300 else:
301 return self._mode 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
303 def build_schema_type_to_method( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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]] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
315 core_schema_types: list[CoreSchemaOrFieldType] = list(get_literal_values(CoreSchemaOrFieldType)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
316 for key in core_schema_types: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
317 method_name = f'{key.replace("-", "_")}_schema' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
318 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
319 mapping[key] = getattr(self, method_name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
329 def generate_definitions( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
355 raise PydanticUserError( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
362 self._mode = mode 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
363 self.generate_inner(schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
365 definitions_remapping = self._build_definitions_remapping() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
367 json_schemas_map: dict[tuple[JsonSchemaKeyT, JsonSchemaMode], DefsRef] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
368 for key, mode, schema in inputs: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
369 self._mode = mode 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
370 json_schema = self.generate_inner(schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
371 json_schemas_map[(key, mode)] = definitions_remapping.remap_json_schema(json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
373 json_schema = {'$defs': self.definitions} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
374 json_schema = definitions_remapping.remap_json_schema(json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
375 self._used = True 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
376 return json_schemas_map, self.sort(json_schema['$defs']) # type: ignore 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
378 def generate(self, schema: CoreSchema, mode: JsonSchemaMode = 'validation') -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
392 if self._used: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
393 raise PydanticUserError( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
400 json_ref_counts = self.get_json_ref_counts(json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
402 ref = cast(JsonRef, json_schema.get('$ref')) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
403 while ref is not None: # may need to unpack multiple levels 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
404 ref_json_schema = self.get_schema_from_definitions(ref) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
405 if json_ref_counts[ref] == 1 and ref_json_schema is not None and len(json_schema) == 1: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
408 json_ref_counts[ref] -= 1 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
409 ref = cast(JsonRef, json_schema.get('$ref')) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
410 ref = None 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
412 self._garbage_collect_definitions(json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
413 definitions_remapping = self._build_definitions_remapping() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
415 if self.definitions: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
416 json_schema['$defs'] = self.definitions 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
418 json_schema = definitions_remapping.remap_json_schema(json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
425 return self.sort(json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
427 def generate_inner(self, schema: CoreSchemaOrField) -> JsonSchemaValue: # noqa: C901 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
444 core_ref = CoreRef(schema['ref']) # type: ignore[typeddict-item] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
445 core_mode_ref = (core_ref, self.mode) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
446 if core_mode_ref in self.core_to_defs_refs and self.core_to_defs_refs[core_mode_ref] in self.definitions: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
447 return {'$ref': self.core_to_json_refs[core_mode_ref]} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
449 def populate_defs(core_schema: CoreSchema, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
450 if 'ref' in core_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
451 core_ref = CoreRef(core_schema['ref']) # type: ignore[typeddict-item] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
452 defs_ref, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
453 json_ref = JsonRef(ref_json_schema['$ref']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
458 self.definitions[defs_ref] = json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
459 self._core_defs_invalid_for_json_schema.pop(defs_ref, None) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
460 json_schema = ref_json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
461 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
463 def handler_func(schema_or_field: CoreSchemaOrField) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
477 if self.mode == 'serialization' and 'serialization' in schema_or_field: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
482 json_schema = self.ser_schema(ser_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 ( 1zCEa
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]) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
492 if json_schema is None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
494 generate_for_schema_type = self._schema_type_to_method[schema_or_field['type']] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
495 json_schema = generate_for_schema_type(schema_or_field) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
496 else:
497 raise TypeError(f'Unexpected schema type: schema={schema_or_field}')
499 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
501 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, handler_func) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
503 metadata = cast(_core_metadata.CoreMetadata, schema.get('metadata', {})) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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'): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
509 def js_updates_handler_func( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
510 schema_or_field: CoreSchemaOrField,
511 current_handler: GetJsonSchemaHandler = current_handler,
512 ) -> JsonSchemaValue:
513 json_schema = {**current_handler(schema_or_field), **js_updates} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
514 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
516 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_updates_handler_func) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
518 if js_extra := metadata.get('pydantic_js_extra'): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
520 def js_extra_handler_func( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
521 schema_or_field: CoreSchemaOrField,
522 current_handler: GetJsonSchemaHandler = current_handler,
523 ) -> JsonSchemaValue:
524 json_schema = current_handler(schema_or_field) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
525 if isinstance(js_extra, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
526 json_schema.update(to_jsonable_python(js_extra)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
527 elif callable(js_extra): 527 ↛ 530line 527 didn't jump to line 530 because the condition on line 527 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBH
528 # similar to typing issue in _update_class_schema when we're working with callable js extra
529 js_extra(json_schema) # type: ignore 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBH
530 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
532 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_extra_handler_func) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
534 for js_modify_function in metadata.get('pydantic_js_functions', ()): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
536 def new_handler_func( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
543 json_schema = populate_defs(schema_or_field, json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
544 original_schema = current_handler.resolve_ref_schema(json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
545 ref = json_schema.pop('$ref', None) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
546 if ref and json_schema: 546 ↛ 547line 546 didn't jump to line 547 because the condition on line 546 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
547 original_schema.update(json_schema)
548 return original_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
550 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
552 for js_modify_function in metadata.get('pydantic_js_annotation_functions', ()): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
554 def new_handler_func( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
561 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
563 json_schema = current_handler(schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
564 if _core_utils.is_core_schema(schema): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
565 json_schema = populate_defs(schema, json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
566 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
568 def sort(self, value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
575 keys = value.keys() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
577 keys = sorted(keys) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
578 for key in keys: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
579 sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
580 return sorted_dict 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
582 def _sort_recursive(self, value: Any, parent_key: str | None = None) -> Any: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
583 """Recursively sort a JSON schema value."""
584 if isinstance(value, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
585 sorted_dict: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
586 keys = value.keys() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
587 if parent_key not in ('properties', 'default'): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
588 keys = sorted(keys) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
589 for key in keys: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
590 sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
591 return sorted_dict 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
592 elif isinstance(value, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
593 sorted_list: list[JsonSchemaValue] = [] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
594 for item in value: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
595 sorted_list.append(self._sort_recursive(item, parent_key)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
596 return sorted_list 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
597 else:
598 return value 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
600 # ### Schema generation methods
602 def invalid_schema(self, schema: core_schema.InvalidSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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 {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
618 def none_schema(self, schema: core_schema.NoneSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
629 def bool_schema(self, schema: core_schema.BoolSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
640 def int_schema(self, schema: core_schema.IntSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
650 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
651 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
652 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
654 def float_schema(self, schema: core_schema.FloatSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
664 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
665 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
666 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
668 def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
679 max_digits = schema.get('max_digits') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
680 decimal_places = schema.get('decimal_places') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
682 pattern = ( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
688 integer_places = max(0, max_digits - decimal_places) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
689 pattern += ( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
700 pattern += ( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
711 pattern += rf'\d*\.?\d{{0,{decimal_places}}}0*$' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
713 # Case 4: Both are None (no restrictions)
714 else:
715 pattern += r'\d*\.?\d*$' # look for arbitrary integer or decimal 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
717 return pattern 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
719 json_schema = self.str_schema(core_schema.str_schema(pattern=get_decimal_pattern(schema))) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
720 if self.mode == 'validation': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
721 multiple_of = schema.get('multiple_of') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
722 le = schema.get('le') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
723 ge = schema.get('ge') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
724 lt = schema.get('lt') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
725 gt = schema.get('gt') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
726 json_schema = { 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
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 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
743 def str_schema(self, schema: core_schema.StringSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
753 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
754 if isinstance(json_schema.get('pattern'), Pattern): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
755 # TODO: should we add regex flags to the pattern?
756 json_schema['pattern'] = json_schema.get('pattern').pattern # type: ignore 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
757 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
759 def bytes_schema(self, schema: core_schema.BytesSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
769 self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
770 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
772 def date_schema(self, schema: core_schema.DateSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
783 def time_schema(self, schema: core_schema.TimeSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
794 def datetime_schema(self, schema: core_schema.DatetimeSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
805 def timedelta_schema(self, schema: core_schema.TimedeltaSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
815 return {'type': 'number'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
816 return {'type': 'string', 'format': 'duration'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
818 def literal_schema(self, schema: core_schema.LiteralSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
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']] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
829 result: dict[str, Any] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
830 if len(expected) == 1: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
831 result['const'] = expected[0] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
832 else:
833 result['enum'] = expected 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
835 types = {type(e) for e in expected} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
836 if types == {str}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
837 result['type'] = 'string' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
838 elif types == {int}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
839 result['type'] = 'integer' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
840 elif types == {float}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
841 result['type'] = 'number' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
842 elif types == {bool}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
843 result['type'] = 'boolean' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
844 elif types == {list}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
845 result['type'] = 'array' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
846 elif types == {type(None)}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
847 result['type'] = 'null' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
848 return result 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
850 def enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
851 """Generates a JSON schema that matches an Enum value.
853 Args:
854 schema: The core schema.
856 Returns:
857 The generated JSON schema.
858 """
859 enum_type = schema['cls'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
860 description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
861 if ( 1zCEa
862 description == 'An enumeration.'
863 ): # This is the default value provided by enum.EnumMeta.__new__; don't use it
864 description = None 1zCbdEaJKhiILcj
865 result: dict[str, Any] = {'title': enum_type.__name__, 'description': description} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
866 result = {k: v for k, v in result.items() if v is not None} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
868 expected = [to_jsonable_python(v.value) for v in schema['members']] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
870 result['enum'] = expected 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
872 types = {type(e) for e in expected} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
873 if isinstance(enum_type, str) or types == {str}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
874 result['type'] = 'string' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
875 elif isinstance(enum_type, int) or types == {int}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
876 result['type'] = 'integer' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
877 elif isinstance(enum_type, float) or types == {float}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
878 result['type'] = 'number' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
879 elif types == {bool}: 879 ↛ 880line 879 didn't jump to line 880 because the condition on line 879 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
880 result['type'] = 'boolean'
881 elif types == {list}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
882 result['type'] = 'array' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
884 return result 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
886 def is_instance_schema(self, schema: core_schema.IsInstanceSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
887 """Handles JSON schema generation for a core schema that checks if a value is an instance of a class.
889 Unless overridden in a subclass, this raises an error.
891 Args:
892 schema: The core schema.
894 Returns:
895 The generated JSON schema.
896 """
897 return self.handle_invalid_for_json_schema(schema, f'core_schema.IsInstanceSchema ({schema["cls"]})') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
899 def is_subclass_schema(self, schema: core_schema.IsSubclassSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
900 """Handles JSON schema generation for a core schema that checks if a value is a subclass of a class.
902 For backwards compatibility with v1, this does not raise an error, but can be overridden to change this.
904 Args:
905 schema: The core schema.
907 Returns:
908 The generated JSON schema.
909 """
910 # Note: This is for compatibility with V1; you can override if you want different behavior.
911 return {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
913 def callable_schema(self, schema: core_schema.CallableSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
914 """Generates a JSON schema that matches a callable value.
916 Unless overridden in a subclass, this raises an error.
918 Args:
919 schema: The core schema.
921 Returns:
922 The generated JSON schema.
923 """
924 return self.handle_invalid_for_json_schema(schema, 'core_schema.CallableSchema') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
926 def list_schema(self, schema: core_schema.ListSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
927 """Returns a schema that matches a list schema.
929 Args:
930 schema: The core schema.
932 Returns:
933 The generated JSON schema.
934 """
935 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
936 json_schema = {'type': 'array', 'items': items_schema} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
937 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
938 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
940 @deprecated('`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', category=None) 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
941 @final 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
942 def tuple_positional_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
943 """Replaced by `tuple_schema`."""
944 warnings.warn(
945 '`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.',
946 PydanticDeprecatedSince26,
947 stacklevel=2,
948 )
949 return self.tuple_schema(schema)
951 @deprecated('`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', category=None) 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
952 @final 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
953 def tuple_variable_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
954 """Replaced by `tuple_schema`."""
955 warnings.warn(
956 '`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.',
957 PydanticDeprecatedSince26,
958 stacklevel=2,
959 )
960 return self.tuple_schema(schema)
962 def tuple_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
963 """Generates a JSON schema that matches a tuple schema e.g. `tuple[int,
964 str, bool]` or `tuple[int, ...]`.
966 Args:
967 schema: The core schema.
969 Returns:
970 The generated JSON schema.
971 """
972 json_schema: JsonSchemaValue = {'type': 'array'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
973 if 'variadic_item_index' in schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
974 variadic_item_index = schema['variadic_item_index'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
975 if variadic_item_index > 0: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
976 json_schema['minItems'] = variadic_item_index 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
977 json_schema['prefixItems'] = [ 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
978 self.generate_inner(item) for item in schema['items_schema'][:variadic_item_index]
979 ]
980 if variadic_item_index + 1 == len(schema['items_schema']): 980 ↛ 987line 980 didn't jump to line 987 because the condition on line 980 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
981 # if the variadic item is the last item, then represent it faithfully
982 json_schema['items'] = self.generate_inner(schema['items_schema'][variadic_item_index]) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
983 else:
984 # otherwise, 'items' represents the schema for the variadic
985 # item plus the suffix, so just allow anything for simplicity
986 # for now
987 json_schema['items'] = True
988 else:
989 prefixItems = [self.generate_inner(item) for item in schema['items_schema']] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
990 if prefixItems: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
991 json_schema['prefixItems'] = prefixItems 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
992 json_schema['minItems'] = len(prefixItems) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
993 json_schema['maxItems'] = len(prefixItems) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
994 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
995 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
997 def set_schema(self, schema: core_schema.SetSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
998 """Generates a JSON schema that matches a set schema.
1000 Args:
1001 schema: The core schema.
1003 Returns:
1004 The generated JSON schema.
1005 """
1006 return self._common_set_schema(schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1008 def frozenset_schema(self, schema: core_schema.FrozenSetSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1009 """Generates a JSON schema that matches a frozenset schema.
1011 Args:
1012 schema: The core schema.
1014 Returns:
1015 The generated JSON schema.
1016 """
1017 return self._common_set_schema(schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1019 def _common_set_schema(self, schema: core_schema.SetSchema | core_schema.FrozenSetSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1020 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1021 json_schema = {'type': 'array', 'uniqueItems': True, 'items': items_schema} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1022 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1023 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1025 def generator_schema(self, schema: core_schema.GeneratorSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1026 """Returns a JSON schema that represents the provided GeneratorSchema.
1028 Args:
1029 schema: The schema.
1031 Returns:
1032 The generated JSON schema.
1033 """
1034 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1035 json_schema = {'type': 'array', 'items': items_schema} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1036 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1037 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1039 def dict_schema(self, schema: core_schema.DictSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1040 """Generates a JSON schema that matches a dict schema.
1042 Args:
1043 schema: The core schema.
1045 Returns:
1046 The generated JSON schema.
1047 """
1048 json_schema: JsonSchemaValue = {'type': 'object'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1050 keys_schema = self.generate_inner(schema['keys_schema']).copy() if 'keys_schema' in schema else {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1051 if '$ref' not in keys_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1052 keys_pattern = keys_schema.pop('pattern', None) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1053 # Don't give a title to patternProperties/propertyNames:
1054 keys_schema.pop('title', None) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1055 else:
1056 # Here, we assume that if the keys schema is a definition reference,
1057 # it can't be a simple string core schema (and thus no pattern can exist).
1058 # However, this is only in practice (in theory, a definition reference core
1059 # schema could be generated for a simple string schema).
1060 # Note that we avoid calling `self.resolve_ref_schema`, as it might not exist yet.
1061 keys_pattern = None 1zCbdenfogpADEaJKhiqrstuvFGILcjkwlxmyBH
1063 values_schema = self.generate_inner(schema['values_schema']).copy() if 'values_schema' in schema else {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1064 # don't give a title to additionalProperties:
1065 values_schema.pop('title', None) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1067 if values_schema or keys_pattern is not None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1068 if keys_pattern is None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1069 json_schema['additionalProperties'] = values_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1070 else:
1071 json_schema['patternProperties'] = {keys_pattern: values_schema} 1zCbdenfogpADEaJKhiqrstuvFGILcjkwlxmyBH
1072 else: # for `dict[str, Any]`, we allow any key and any value, since `str` is the default key type
1073 json_schema['additionalProperties'] = True 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1075 if ( 1zCEa
1076 # The len check indicates that constraints are probably present:
1077 (keys_schema.get('type') == 'string' and len(keys_schema) > 1)
1078 # If this is a definition reference schema, it most likely has constraints:
1079 or '$ref' in keys_schema
1080 ):
1081 keys_schema.pop('type', None) 1zCbdenfogpADEaJKhiqrstuvFGILcjkwlxmyBH
1082 json_schema['propertyNames'] = keys_schema 1zCbdenfogpADEaJKhiqrstuvFGILcjkwlxmyBH
1084 self.update_with_validations(json_schema, schema, self.ValidationsMapping.object) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1085 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1087 def function_before_schema(self, schema: core_schema.BeforeValidatorFunctionSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1088 """Generates a JSON schema that matches a function-before schema.
1090 Args:
1091 schema: The core schema.
1093 Returns:
1094 The generated JSON schema.
1095 """
1096 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1097 return self.generate_inner(input_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1099 return self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1101 def function_after_schema(self, schema: core_schema.AfterValidatorFunctionSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1102 """Generates a JSON schema that matches a function-after schema.
1104 Args:
1105 schema: The core schema.
1107 Returns:
1108 The generated JSON schema.
1109 """
1110 return self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1112 def function_plain_schema(self, schema: core_schema.PlainValidatorFunctionSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1113 """Generates a JSON schema that matches a function-plain schema.
1115 Args:
1116 schema: The core schema.
1118 Returns:
1119 The generated JSON schema.
1120 """
1121 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 1121 ↛ 1124line 1121 didn't jump to line 1124 because the condition on line 1121 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1122 return self.generate_inner(input_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1124 return self.handle_invalid_for_json_schema(
1125 schema, f'core_schema.PlainValidatorFunctionSchema ({schema["function"]})'
1126 )
1128 def function_wrap_schema(self, schema: core_schema.WrapValidatorFunctionSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1129 """Generates a JSON schema that matches a function-wrap schema.
1131 Args:
1132 schema: The core schema.
1134 Returns:
1135 The generated JSON schema.
1136 """
1137 if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1138 return self.generate_inner(input_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1140 return self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1142 def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1143 """Generates a JSON schema that matches a schema with a default value.
1145 Args:
1146 schema: The core schema.
1148 Returns:
1149 The generated JSON schema.
1150 """
1151 json_schema = self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1153 default = self.get_default_value(schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1154 if default is NoDefault: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1155 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1157 # we reflect the application of custom plain, no-info serializers to defaults for
1158 # JSON Schemas viewed in serialization mode:
1159 # TODO: improvements along with https://github.com/pydantic/pydantic/issues/8208
1160 if self.mode == 'serialization': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1161 # `_get_ser_schema_for_default_value()` is used to unpack potentially nested validator schemas:
1162 ser_schema = _get_ser_schema_for_default_value(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1163 if ( 1zCEaJKIL
1164 ser_schema is not None
1165 and (ser_func := ser_schema.get('function'))
1166 and not (default is None and ser_schema.get('when_used') in ('unless-none', 'json-unless-none'))
1167 ):
1168 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1169 default = ser_func(default) # type: ignore 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1170 except Exception: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1171 # It might be that the provided default needs to be validated (read: parsed) first
1172 # (assuming `validate_default` is enabled). However, we can't perform
1173 # such validation during JSON Schema generation so we don't support
1174 # this pattern for now.
1175 # (One example is when using `foo: ByteSize = '1MB'`, which validates and
1176 # serializes as an int. In this case, `ser_func` is `int` and `int('1MB')` fails).
1177 self.emit_warning( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1178 'non-serializable-default',
1179 f'Unable to serialize value {default!r} with the plain serializer; excluding default from JSON schema',
1180 )
1181 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1183 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1184 encoded_default = self.encode_default(default) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1185 except pydantic_core.PydanticSerializationError: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1186 self.emit_warning( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1187 'non-serializable-default',
1188 f'Default value {default} is not JSON serializable; excluding default from JSON schema',
1189 )
1190 # Return the inner schema, as though there was no default
1191 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1193 json_schema['default'] = encoded_default 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1194 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1196 def get_default_value(self, schema: core_schema.WithDefaultSchema) -> Any: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1197 """Get the default value to be used when generating a JSON Schema for a core schema with a default.
1199 The default implementation is to use the statically defined default value. This method can be overridden
1200 if you want to make use of the default factory.
1202 Args:
1203 schema: The `'with-default'` core schema.
1205 Returns:
1206 The default value to use, or [`NoDefault`][pydantic.json_schema.NoDefault] if no default
1207 value is available.
1208 """
1209 return schema.get('default', NoDefault) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1211 def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1212 """Generates a JSON schema that matches a schema that allows null values.
1214 Args:
1215 schema: The core schema.
1217 Returns:
1218 The generated JSON schema.
1219 """
1220 null_schema = {'type': 'null'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1221 inner_json_schema = self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1223 if inner_json_schema == null_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1224 return null_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1225 else:
1226 # Thanks to the equality check against `null_schema` above, I think 'oneOf' would also be valid here;
1227 # I'll use 'anyOf' for now, but it could be changed it if it would work better with some external tooling
1228 return self.get_flattened_anyof([inner_json_schema, null_schema]) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1230 def union_schema(self, schema: core_schema.UnionSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1231 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas.
1233 Args:
1234 schema: The core schema.
1236 Returns:
1237 The generated JSON schema.
1238 """
1239 generated: list[JsonSchemaValue] = [] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1241 choices = schema['choices'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1242 for choice in choices: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1243 # choice will be a tuple if an explicit label was provided
1244 choice_schema = choice[0] if isinstance(choice, tuple) else choice 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1245 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1246 generated.append(self.generate_inner(choice_schema)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1247 except PydanticOmit: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1248 continue 1zCbdenfogpADEaJKhiqrstuvFGILcjkwlxmyBH
1249 except PydanticInvalidForJsonSchema as exc: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1250 self.emit_warning('skipped-choice', exc.message) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1251 if len(generated) == 1: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1252 return generated[0] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1253 return self.get_flattened_anyof(generated) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1255 def tagged_union_schema(self, schema: core_schema.TaggedUnionSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1256 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas, where
1257 the schemas are tagged with a discriminator field that indicates which schema should be used to validate
1258 the value.
1260 Args:
1261 schema: The core schema.
1263 Returns:
1264 The generated JSON schema.
1265 """
1266 generated: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1267 for k, v in schema['choices'].items(): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1268 if isinstance(k, Enum): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1269 k = k.value 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1270 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1271 # Use str(k) since keys must be strings for json; while not technically correct,
1272 # it's the closest that can be represented in valid JSON
1273 generated[str(k)] = self.generate_inner(v).copy() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1274 except PydanticOmit:
1275 continue
1276 except PydanticInvalidForJsonSchema as exc:
1277 self.emit_warning('skipped-choice', exc.message)
1279 one_of_choices = _deduplicate_schemas(generated.values()) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1280 json_schema: JsonSchemaValue = {'oneOf': one_of_choices} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1282 # This reflects the v1 behavior; TODO: we should make it possible to exclude OpenAPI stuff from the JSON schema
1283 openapi_discriminator = self._extract_discriminator(schema, one_of_choices) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1284 if openapi_discriminator is not None: 1284 ↛ 1290line 1284 didn't jump to line 1290 because the condition on line 1284 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1285 json_schema['discriminator'] = { 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1286 'propertyName': openapi_discriminator,
1287 'mapping': {k: v.get('$ref', v) for k, v in generated.items()},
1288 }
1290 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1292 def _extract_discriminator( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1293 self, schema: core_schema.TaggedUnionSchema, one_of_choices: list[JsonDict]
1294 ) -> str | None:
1295 """Extract a compatible OpenAPI discriminator from the schema and one_of choices that end up in the final
1296 schema."""
1297 openapi_discriminator: str | None = None 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1299 if isinstance(schema['discriminator'], str): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1300 return schema['discriminator'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1302 if isinstance(schema['discriminator'], list): 1302 ↛ 1335line 1302 didn't jump to line 1335 because the condition on line 1302 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1303 # If the discriminator is a single item list containing a string, that is equivalent to the string case
1304 if len(schema['discriminator']) == 1 and isinstance(schema['discriminator'][0], str): 1304 ↛ 1305line 1304 didn't jump to line 1305 because the condition on line 1304 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1305 return schema['discriminator'][0]
1306 # When an alias is used that is different from the field name, the discriminator will be a list of single
1307 # str lists, one for the attribute and one for the actual alias. The logic here will work even if there is
1308 # more than one possible attribute, and looks for whether a single alias choice is present as a documented
1309 # property on all choices. If so, that property will be used as the OpenAPI discriminator.
1310 for alias_path in schema['discriminator']: 1310 ↛ 1335line 1310 didn't jump to line 1335 because the loop on line 1310 didn't complete1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1311 if not isinstance(alias_path, list): 1311 ↛ 1312line 1311 didn't jump to line 1312 because the condition on line 1311 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1312 break # this means that the discriminator is not a list of alias paths
1313 if len(alias_path) != 1: 1313 ↛ 1314line 1313 didn't jump to line 1314 because the condition on line 1313 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1314 continue # this means that the "alias" does not represent a single field
1315 alias = alias_path[0] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1316 if not isinstance(alias, str): 1316 ↛ 1317line 1316 didn't jump to line 1317 because the condition on line 1316 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1317 continue # this means that the "alias" does not represent a field
1318 alias_is_present_on_all_choices = True 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1319 for choice in one_of_choices: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1320 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1321 choice = self.resolve_ref_schema(choice) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1322 except RuntimeError as exc:
1323 # TODO: fixme - this is a workaround for the fact that we can't always resolve refs
1324 # for tagged union choices at this point in the schema gen process, we might need to do
1325 # another pass at the end like we do for core schemas
1326 self.emit_warning('skipped-discriminator', str(exc))
1327 choice = {}
1328 properties = choice.get('properties', {}) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1329 if not isinstance(properties, dict) or alias not in properties: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1330 alias_is_present_on_all_choices = False 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1331 break 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1332 if alias_is_present_on_all_choices: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1333 openapi_discriminator = alias 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1334 break 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1335 return openapi_discriminator 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1337 def chain_schema(self, schema: core_schema.ChainSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1338 """Generates a JSON schema that matches a core_schema.ChainSchema.
1340 When generating a schema for validation, we return the validation JSON schema for the first step in the chain.
1341 For serialization, we return the serialization JSON schema for the last step in the chain.
1343 Args:
1344 schema: The core schema.
1346 Returns:
1347 The generated JSON schema.
1348 """
1349 step_index = 0 if self.mode == 'validation' else -1 # use first step for validation, last for serialization 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1350 return self.generate_inner(schema['steps'][step_index]) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1352 def lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1353 """Generates a JSON schema that matches a schema that allows values matching either the lax schema or the
1354 strict schema.
1356 Args:
1357 schema: The core schema.
1359 Returns:
1360 The generated JSON schema.
1361 """
1362 # TODO: Need to read the default value off of model config or whatever
1363 use_strict = schema.get('strict', False) # TODO: replace this default False 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1364 # If your JSON schema fails to generate it is probably
1365 # because one of the following two branches failed.
1366 if use_strict: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1367 return self.generate_inner(schema['strict_schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1368 else:
1369 return self.generate_inner(schema['lax_schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1371 def json_or_python_schema(self, schema: core_schema.JsonOrPythonSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1372 """Generates a JSON schema that matches a schema that allows values matching either the JSON schema or the
1373 Python schema.
1375 The JSON schema is used instead of the Python schema. If you want to use the Python schema, you should override
1376 this method.
1378 Args:
1379 schema: The core schema.
1381 Returns:
1382 The generated JSON schema.
1383 """
1384 return self.generate_inner(schema['json_schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1386 def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1387 """Generates a JSON schema that matches a schema that defines a typed dict.
1389 Args:
1390 schema: The core schema.
1392 Returns:
1393 The generated JSON schema.
1394 """
1395 total = schema.get('total', True) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1396 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1397 (name, self.field_is_required(field, total), field)
1398 for name, field in schema['fields'].items()
1399 if self.field_is_present(field)
1400 ]
1401 if self.mode == 'serialization': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1402 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1403 cls = schema.get('cls') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1404 config = _get_typed_dict_config(cls) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1405 with self._config_wrapper_stack.push(config): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1406 json_schema = self._named_required_fields_schema(named_required_fields) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1408 if cls is not None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1409 self._update_class_schema(json_schema, cls, config) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1410 else:
1411 extra = config.get('extra') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1412 if extra == 'forbid': 1412 ↛ 1413line 1412 didn't jump to line 1413 because the condition on line 1412 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1413 json_schema['additionalProperties'] = False
1414 elif extra == 'allow': 1414 ↛ 1415line 1414 didn't jump to line 1415 because the condition on line 1414 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1415 json_schema['additionalProperties'] = True
1417 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1419 @staticmethod 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1420 def _name_required_computed_fields( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1421 computed_fields: list[ComputedField],
1422 ) -> list[tuple[str, bool, core_schema.ComputedField]]:
1423 return [(field['property_name'], True, field) for field in computed_fields] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1425 def _named_required_fields_schema( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1426 self, named_required_fields: Sequence[tuple[str, bool, CoreSchemaField]]
1427 ) -> JsonSchemaValue:
1428 properties: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1429 required_fields: list[str] = [] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1430 for name, required, field in named_required_fields: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1431 if self.by_alias: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1432 name = self._get_alias_name(field, name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1433 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1434 field_json_schema = self.generate_inner(field).copy() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1435 except PydanticOmit: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1436 continue 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1437 if 'title' not in field_json_schema and self.field_title_should_be_set(field): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1438 title = self.get_title_from_name(name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1439 field_json_schema['title'] = title 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1440 field_json_schema = self.handle_ref_overrides(field_json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1441 properties[name] = field_json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1442 if required: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1443 required_fields.append(name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1445 json_schema = {'type': 'object', 'properties': properties} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1446 if required_fields: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1447 json_schema['required'] = required_fields 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1448 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1450 def _get_alias_name(self, field: CoreSchemaField, name: str) -> str: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1451 if field['type'] == 'computed-field': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1452 alias: Any = field.get('alias', name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1453 elif self.mode == 'validation': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1454 alias = field.get('validation_alias', name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1455 else:
1456 alias = field.get('serialization_alias', name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1457 if isinstance(alias, str): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1458 name = alias 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1459 elif isinstance(alias, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1460 alias = cast('list[str] | str', alias) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1461 for path in alias: 1461 ↛ 1469line 1461 didn't jump to line 1469 because the loop on line 1461 didn't complete1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1462 if isinstance(path, list) and len(path) == 1 and isinstance(path[0], str): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1463 # Use the first valid single-item string path; the code that constructs the alias array
1464 # should ensure the first such item is what belongs in the JSON schema
1465 name = path[0] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1466 break 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1467 else:
1468 assert_never(alias)
1469 return name 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1471 def typed_dict_field_schema(self, schema: core_schema.TypedDictField) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1472 """Generates a JSON schema that matches a schema that defines a typed dict field.
1474 Args:
1475 schema: The core schema.
1477 Returns:
1478 The generated JSON schema.
1479 """
1480 return self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1482 def dataclass_field_schema(self, schema: core_schema.DataclassField) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1483 """Generates a JSON schema that matches a schema that defines a dataclass field.
1485 Args:
1486 schema: The core schema.
1488 Returns:
1489 The generated JSON schema.
1490 """
1491 return self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1493 def model_field_schema(self, schema: core_schema.ModelField) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1494 """Generates a JSON schema that matches a schema that defines a model field.
1496 Args:
1497 schema: The core schema.
1499 Returns:
1500 The generated JSON schema.
1501 """
1502 return self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1504 def computed_field_schema(self, schema: core_schema.ComputedField) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1505 """Generates a JSON schema that matches a schema that defines a computed field.
1507 Args:
1508 schema: The core schema.
1510 Returns:
1511 The generated JSON schema.
1512 """
1513 return self.generate_inner(schema['return_schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1515 def model_schema(self, schema: core_schema.ModelSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1516 """Generates a JSON schema that matches a schema that defines a model.
1518 Args:
1519 schema: The core schema.
1521 Returns:
1522 The generated JSON schema.
1523 """
1524 # We do not use schema['model'].model_json_schema() here
1525 # because it could lead to inconsistent refs handling, etc.
1526 cls = cast('type[BaseModel]', schema['cls']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1527 config = cls.model_config 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1529 with self._config_wrapper_stack.push(config): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1530 json_schema = self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1532 self._update_class_schema(json_schema, cls, config) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1534 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1536 def _update_class_schema(self, json_schema: JsonSchemaValue, cls: type[Any], config: ConfigDict) -> None: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1537 """Update json_schema with the following, extracted from `config` and `cls`:
1539 * title
1540 * description
1541 * additional properties
1542 * json_schema_extra
1543 * deprecated
1545 Done in place, hence there's no return value as the original json_schema is mutated.
1546 No ref resolving is involved here, as that's not appropriate for simple updates.
1547 """
1548 from .main import BaseModel 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1549 from .root_model import RootModel 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1551 if (config_title := config.get('title')) is not None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1552 json_schema.setdefault('title', config_title) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1553 elif model_title_generator := config.get('model_title_generator'): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1554 title = model_title_generator(cls) 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1555 if not isinstance(title, str): 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1556 raise TypeError(f'model_title_generator {model_title_generator} must return str, not {title.__class__}') 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1557 json_schema.setdefault('title', title) 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1558 if 'title' not in json_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1559 json_schema['title'] = cls.__name__ 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1561 # BaseModel and dataclasses; don't use cls.__doc__ as it will contain the verbose class signature by default
1562 docstring = None if cls is BaseModel or dataclasses.is_dataclass(cls) else cls.__doc__ 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1564 if docstring: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1565 json_schema.setdefault('description', inspect.cleandoc(docstring)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1566 elif issubclass(cls, RootModel) and (root_description := cls.__pydantic_fields__['root'].description): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1567 json_schema.setdefault('description', root_description) 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1569 extra = config.get('extra') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1570 if 'additionalProperties' not in json_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1571 if extra == 'allow': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1572 json_schema['additionalProperties'] = True 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1573 elif extra == 'forbid': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1574 json_schema['additionalProperties'] = False 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1576 json_schema_extra = config.get('json_schema_extra') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1577 if issubclass(cls, BaseModel) and cls.__pydantic_root_model__: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1578 root_json_schema_extra = cls.model_fields['root'].json_schema_extra 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1579 if json_schema_extra and root_json_schema_extra: 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1580 raise ValueError( 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1581 '"model_config[\'json_schema_extra\']" and "Field.json_schema_extra" on "RootModel.root"'
1582 ' field must not be set simultaneously'
1583 )
1584 if root_json_schema_extra: 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1585 json_schema_extra = root_json_schema_extra 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1587 if isinstance(json_schema_extra, (staticmethod, classmethod)): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1588 # In older versions of python, this is necessary to ensure staticmethod/classmethods are callable
1589 json_schema_extra = json_schema_extra.__get__(cls) 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBH
1591 if isinstance(json_schema_extra, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1592 json_schema.update(json_schema_extra) 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1593 elif callable(json_schema_extra): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1594 # FIXME: why are there type ignores here? We support two signatures for json_schema_extra callables...
1595 if len(inspect.signature(json_schema_extra).parameters) > 1: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1596 json_schema_extra(json_schema, cls) # type: ignore 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1597 else:
1598 json_schema_extra(json_schema) # type: ignore 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1599 elif json_schema_extra is not None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1600 raise ValueError( 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1601 f"model_config['json_schema_extra']={json_schema_extra} should be a dict, callable, or None"
1602 )
1604 if hasattr(cls, '__deprecated__'): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1605 json_schema['deprecated'] = True 1zCbdenfogpADEaJKhiqrstuvFGMILcjkwlxmyBHN
1607 def resolve_ref_schema(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1608 """Resolve a JsonSchemaValue to the non-ref schema if it is a $ref schema.
1610 Args:
1611 json_schema: The schema to resolve.
1613 Returns:
1614 The resolved schema.
1616 Raises:
1617 RuntimeError: If the schema reference can't be found in definitions.
1618 """
1619 while '$ref' in json_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1620 ref = json_schema['$ref'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1621 schema_to_update = self.get_schema_from_definitions(JsonRef(ref)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1622 if schema_to_update is None: 1622 ↛ 1623line 1622 didn't jump to line 1623 because the condition on line 1622 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1623 raise RuntimeError(f'Cannot update undefined schema for $ref={ref}')
1624 json_schema = schema_to_update 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1625 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1627 def model_fields_schema(self, schema: core_schema.ModelFieldsSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1628 """Generates a JSON schema that matches a schema that defines a model's fields.
1630 Args:
1631 schema: The core schema.
1633 Returns:
1634 The generated JSON schema.
1635 """
1636 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1637 (name, self.field_is_required(field, total=True), field)
1638 for name, field in schema['fields'].items()
1639 if self.field_is_present(field)
1640 ]
1641 if self.mode == 'serialization': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1642 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1643 json_schema = self._named_required_fields_schema(named_required_fields) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1644 extras_schema = schema.get('extras_schema', None) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1645 if extras_schema is not None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1646 schema_to_update = self.resolve_ref_schema(json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1647 schema_to_update['additionalProperties'] = self.generate_inner(extras_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1648 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1650 def field_is_present(self, field: CoreSchemaField) -> bool: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1651 """Whether the field should be included in the generated JSON schema.
1653 Args:
1654 field: The schema for the field itself.
1656 Returns:
1657 `True` if the field should be included in the generated JSON schema, `False` otherwise.
1658 """
1659 if self.mode == 'serialization': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1660 # If you still want to include the field in the generated JSON schema,
1661 # override this method and return True
1662 return not field.get('serialization_exclude') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1663 elif self.mode == 'validation': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1664 return True 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1665 else:
1666 assert_never(self.mode)
1668 def field_is_required( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1669 self,
1670 field: core_schema.ModelField | core_schema.DataclassField | core_schema.TypedDictField,
1671 total: bool,
1672 ) -> bool:
1673 """Whether the field should be marked as required in the generated JSON schema.
1674 (Note that this is irrelevant if the field is not present in the JSON schema.).
1676 Args:
1677 field: The schema for the field itself.
1678 total: Only applies to `TypedDictField`s.
1679 Indicates if the `TypedDict` this field belongs to is total, in which case any fields that don't
1680 explicitly specify `required=False` are required.
1682 Returns:
1683 `True` if the field should be marked as required in the generated JSON schema, `False` otherwise.
1684 """
1685 if self.mode == 'serialization' and self._config.json_schema_serialization_defaults_required: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1686 return not field.get('serialization_exclude') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1687 else:
1688 if field['type'] == 'typed-dict-field': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1689 return field.get('required', total) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1690 else:
1691 return field['schema']['type'] != 'default' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1693 def dataclass_args_schema(self, schema: core_schema.DataclassArgsSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1694 """Generates a JSON schema that matches a schema that defines a dataclass's constructor arguments.
1696 Args:
1697 schema: The core schema.
1699 Returns:
1700 The generated JSON schema.
1701 """
1702 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1703 (field['name'], self.field_is_required(field, total=True), field)
1704 for field in schema['fields']
1705 if self.field_is_present(field)
1706 ]
1707 if self.mode == 'serialization': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1708 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1709 return self._named_required_fields_schema(named_required_fields) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1711 def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1712 """Generates a JSON schema that matches a schema that defines a dataclass.
1714 Args:
1715 schema: The core schema.
1717 Returns:
1718 The generated JSON schema.
1719 """
1720 from ._internal._dataclasses import is_stdlib_dataclass 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1722 cls = schema['cls'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1723 config: ConfigDict = getattr(cls, '__pydantic_config__', cast('ConfigDict', {})) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1725 with self._config_wrapper_stack.push(config): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1726 json_schema = self.generate_inner(schema['schema']).copy() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1728 self._update_class_schema(json_schema, cls, config) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1730 # Dataclass-specific handling of description
1731 if is_stdlib_dataclass(cls): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1732 # vanilla dataclass; don't use cls.__doc__ as it will contain the class signature by default
1733 description = None 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1734 else:
1735 description = None if cls.__doc__ is None else inspect.cleandoc(cls.__doc__) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1736 if description: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1737 json_schema['description'] = description 1zCbdenfogpADEaJKhiqrstuvFGILcjkwlxmyBH
1739 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1741 def arguments_schema(self, schema: core_schema.ArgumentsSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1742 """Generates a JSON schema that matches a schema that defines a function's arguments.
1744 Args:
1745 schema: The core schema.
1747 Returns:
1748 The generated JSON schema.
1749 """
1750 prefer_positional = schema.get('metadata', {}).get('pydantic_js_prefer_positional_arguments') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1752 arguments = schema['arguments_schema'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1753 kw_only_arguments = [a for a in arguments if a.get('mode') == 'keyword_only'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1754 kw_or_p_arguments = [a for a in arguments if a.get('mode') in {'positional_or_keyword', None}] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1755 p_only_arguments = [a for a in arguments if a.get('mode') == 'positional_only'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1756 var_args_schema = schema.get('var_args_schema') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1757 var_kwargs_schema = schema.get('var_kwargs_schema') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1759 if prefer_positional: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1760 positional_possible = not kw_only_arguments and not var_kwargs_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1761 if positional_possible: 1761 ↛ 1764line 1761 didn't jump to line 1764 because the condition on line 1761 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1762 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1764 keyword_possible = not p_only_arguments and not var_args_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1765 if keyword_possible: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1766 return self.kw_arguments_schema(kw_or_p_arguments + kw_only_arguments, var_kwargs_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1768 if not prefer_positional: 1768 ↛ 1773line 1768 didn't jump to line 1773 because the condition on line 1768 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1769 positional_possible = not kw_only_arguments and not var_kwargs_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1770 if positional_possible: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1771 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1773 raise PydanticInvalidForJsonSchema( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1774 'Unable to generate JSON schema for arguments validator with positional-only and keyword-only arguments'
1775 )
1777 def kw_arguments_schema( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1778 self, arguments: list[core_schema.ArgumentsParameter], var_kwargs_schema: CoreSchema | None
1779 ) -> JsonSchemaValue:
1780 """Generates a JSON schema that matches a schema that defines a function's keyword arguments.
1782 Args:
1783 arguments: The core schema.
1785 Returns:
1786 The generated JSON schema.
1787 """
1788 properties: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1789 required: list[str] = [] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1790 for argument in arguments: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1791 name = self.get_argument_name(argument) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1792 argument_schema = self.generate_inner(argument['schema']).copy() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1793 argument_schema['title'] = self.get_title_from_name(name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1794 properties[name] = argument_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1796 if argument['schema']['type'] != 'default': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1797 # This assumes that if the argument has a default value,
1798 # the inner schema must be of type WithDefaultSchema.
1799 # I believe this is true, but I am not 100% sure
1800 required.append(name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1802 json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1803 if required: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1804 json_schema['required'] = required 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1806 if var_kwargs_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1807 additional_properties_schema = self.generate_inner(var_kwargs_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1808 if additional_properties_schema: 1808 ↛ 1812line 1808 didn't jump to line 1812 because the condition on line 1808 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1809 json_schema['additionalProperties'] = additional_properties_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1810 else:
1811 json_schema['additionalProperties'] = False 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1812 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1814 def p_arguments_schema( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1815 self, arguments: list[core_schema.ArgumentsParameter], var_args_schema: CoreSchema | None
1816 ) -> JsonSchemaValue:
1817 """Generates a JSON schema that matches a schema that defines a function's positional arguments.
1819 Args:
1820 arguments: The core schema.
1822 Returns:
1823 The generated JSON schema.
1824 """
1825 prefix_items: list[JsonSchemaValue] = [] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1826 min_items = 0 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1828 for argument in arguments: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1829 name = self.get_argument_name(argument) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1831 argument_schema = self.generate_inner(argument['schema']).copy() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1832 argument_schema['title'] = self.get_title_from_name(name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1833 prefix_items.append(argument_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1835 if argument['schema']['type'] != 'default': 1835 ↛ 1828line 1835 didn't jump to line 1828 because the condition on line 1835 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1836 # This assumes that if the argument has a default value,
1837 # the inner schema must be of type WithDefaultSchema.
1838 # I believe this is true, but I am not 100% sure
1839 min_items += 1 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1841 json_schema: JsonSchemaValue = {'type': 'array'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1842 if prefix_items: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1843 json_schema['prefixItems'] = prefix_items 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1844 if min_items: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1845 json_schema['minItems'] = min_items 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1847 if var_args_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1848 items_schema = self.generate_inner(var_args_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1849 if items_schema: 1849 ↛ 1854line 1849 didn't jump to line 1854 because the condition on line 1849 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1850 json_schema['items'] = items_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1851 else:
1852 json_schema['maxItems'] = len(prefix_items) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1854 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1856 def get_argument_name(self, argument: core_schema.ArgumentsParameter | core_schema.ArgumentsV3Parameter) -> str: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1857 """Retrieves the name of an argument.
1859 Args:
1860 argument: The core schema.
1862 Returns:
1863 The name of the argument.
1864 """
1865 name = argument['name'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1866 if self.by_alias: 1866 ↛ 1872line 1866 didn't jump to line 1872 because the condition on line 1866 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1867 alias = argument.get('alias') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1868 if isinstance(alias, str): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1869 name = alias 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1870 else:
1871 pass # might want to do something else? 1bdenfogpADOahiqrstuvFGMcjkwlxmyBHN
1872 return name 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1874 def arguments_v3_schema(self, schema: core_schema.ArgumentsV3Schema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1875 """Generates a JSON schema that matches a schema that defines a function's arguments.
1877 Args:
1878 schema: The core schema.
1880 Returns:
1881 The generated JSON schema.
1882 """
1883 arguments = schema['arguments_schema'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1884 properties: dict[str, JsonSchemaValue] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1885 required: list[str] = [] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1886 for argument in arguments: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1887 mode = argument.get('mode', 'positional_or_keyword') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1888 name = self.get_argument_name(argument) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1889 argument_schema = self.generate_inner(argument['schema']).copy() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1890 if mode == 'var_args': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1891 argument_schema = {'type': 'array', 'items': argument_schema} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1892 elif mode == 'var_kwargs_uniform': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1893 argument_schema = {'type': 'object', 'additionalProperties': argument_schema} 1zCbdenfogpADEaJKhiqrstuvFGILcjkwlxmyBH
1895 argument_schema.setdefault('title', self.get_title_from_name(name)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1896 properties[name] = argument_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1898 if ( 1zCEa
1899 (mode == 'var_kwargs_unpacked_typed_dict' and 'required' in argument_schema)
1900 or mode not in {'var_args', 'var_kwargs_uniform', 'var_kwargs_unpacked_typed_dict'}
1901 and argument['schema']['type'] != 'default'
1902 ):
1903 # This assumes that if the argument has a default value,
1904 # the inner schema must be of type WithDefaultSchema.
1905 # I believe this is true, but I am not 100% sure
1906 required.append(name) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1908 json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1909 if required: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1910 json_schema['required'] = required 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1911 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1913 def call_schema(self, schema: core_schema.CallSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1914 """Generates a JSON schema that matches a schema that defines a function call.
1916 Args:
1917 schema: The core schema.
1919 Returns:
1920 The generated JSON schema.
1921 """
1922 return self.generate_inner(schema['arguments_schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1924 def custom_error_schema(self, schema: core_schema.CustomErrorSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1925 """Generates a JSON schema that matches a schema that defines a custom error.
1927 Args:
1928 schema: The core schema.
1930 Returns:
1931 The generated JSON schema.
1932 """
1933 return self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1935 def json_schema(self, schema: core_schema.JsonSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1936 """Generates a JSON schema that matches a schema that defines a JSON object.
1938 Args:
1939 schema: The core schema.
1941 Returns:
1942 The generated JSON schema.
1943 """
1944 content_core_schema = schema.get('schema') or core_schema.any_schema() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1945 content_json_schema = self.generate_inner(content_core_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1946 if self.mode == 'validation': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1947 return {'type': 'string', 'contentMediaType': 'application/json', 'contentSchema': content_json_schema} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1948 else:
1949 # self.mode == 'serialization'
1950 return content_json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1952 def url_schema(self, schema: core_schema.UrlSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1953 """Generates a JSON schema that matches a schema that defines a URL.
1955 Args:
1956 schema: The core schema.
1958 Returns:
1959 The generated JSON schema.
1960 """
1961 json_schema = {'type': 'string', 'format': 'uri', 'minLength': 1} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1962 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1963 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1965 def multi_host_url_schema(self, schema: core_schema.MultiHostUrlSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1966 """Generates a JSON schema that matches a schema that defines a URL that can be used with multiple hosts.
1968 Args:
1969 schema: The core schema.
1971 Returns:
1972 The generated JSON schema.
1973 """
1974 # Note: 'multi-host-uri' is a custom/pydantic-specific format, not part of the JSON Schema spec
1975 json_schema = {'type': 'string', 'format': 'multi-host-uri', 'minLength': 1} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1976 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1977 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1979 def uuid_schema(self, schema: core_schema.UuidSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1980 """Generates a JSON schema that matches a UUID.
1982 Args:
1983 schema: The core schema.
1985 Returns:
1986 The generated JSON schema.
1987 """
1988 return {'type': 'string', 'format': 'uuid'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
1990 def definitions_schema(self, schema: core_schema.DefinitionsSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
1991 """Generates a JSON schema that matches a schema that defines a JSON object with definitions.
1993 Args:
1994 schema: The core schema.
1996 Returns:
1997 The generated JSON schema.
1998 """
1999 for definition in schema['definitions']: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2000 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2001 self.generate_inner(definition) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2002 except PydanticInvalidForJsonSchema as e:
2003 core_ref: CoreRef = CoreRef(definition['ref']) # type: ignore
2004 self._core_defs_invalid_for_json_schema[self.get_defs_ref((core_ref, self.mode))] = e
2005 continue
2006 return self.generate_inner(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2008 def definition_ref_schema(self, schema: core_schema.DefinitionReferenceSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2009 """Generates a JSON schema that matches a schema that references a definition.
2011 Args:
2012 schema: The core schema.
2014 Returns:
2015 The generated JSON schema.
2016 """
2017 core_ref = CoreRef(schema['schema_ref']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2018 _, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2019 return ref_json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2021 def ser_schema( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2022 self, schema: core_schema.SerSchema | core_schema.IncExSeqSerSchema | core_schema.IncExDictSerSchema
2023 ) -> JsonSchemaValue | None:
2024 """Generates a JSON schema that matches a schema that defines a serialized object.
2026 Args:
2027 schema: The core schema.
2029 Returns:
2030 The generated JSON schema.
2031 """
2032 schema_type = schema['type'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2033 if schema_type == 'function-plain' or schema_type == 'function-wrap': 2033 ↛ 2038line 2033 didn't jump to line 2038 because the condition on line 2033 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2034 # PlainSerializerFunctionSerSchema or WrapSerializerFunctionSerSchema
2035 return_schema = schema.get('return_schema') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2036 if return_schema is not None: 2036 ↛ 2044line 2036 didn't jump to line 2044 because the condition on line 2036 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2037 return self.generate_inner(return_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2038 elif schema_type == 'format' or schema_type == 'to-string':
2039 # FormatSerSchema or ToStringSerSchema
2040 return self.str_schema(core_schema.str_schema())
2041 elif schema['type'] == 'model':
2042 # ModelSerSchema
2043 return self.generate_inner(schema['schema'])
2044 return None
2046 def complex_schema(self, schema: core_schema.ComplexSchema) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2047 """Generates a JSON schema that matches a complex number.
2049 JSON has no standard way to represent complex numbers. Complex number is not a numeric
2050 type. Here we represent complex number as strings following the rule defined by Python.
2051 For instance, '1+2j' is an accepted complex string. Details can be found in
2052 [Python's `complex` documentation][complex].
2054 Args:
2055 schema: The core schema.
2057 Returns:
2058 The generated JSON schema.
2059 """
2060 return {'type': 'string'} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2062 # ### Utility methods
2064 def get_title_from_name(self, name: str) -> str: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2065 """Retrieves a title from a name.
2067 Args:
2068 name: The name to retrieve a title from.
2070 Returns:
2071 The title.
2072 """
2073 return name.title().replace('_', ' ').strip() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2075 def field_title_should_be_set(self, schema: CoreSchemaOrField) -> bool: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2076 """Returns true if a field with the given schema should have a title set based on the field name.
2078 Intuitively, we want this to return true for schemas that wouldn't otherwise provide their own title
2079 (e.g., int, float, str), and false for those that would (e.g., BaseModel subclasses).
2081 Args:
2082 schema: The schema to check.
2084 Returns:
2085 `True` if the field should have a title set, `False` otherwise.
2086 """
2087 if _core_utils.is_core_schema_field(schema): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2088 if schema['type'] == 'computed-field': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2089 field_schema = schema['return_schema'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2090 else:
2091 field_schema = schema['schema'] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2092 return self.field_title_should_be_set(field_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2094 elif _core_utils.is_core_schema(schema): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2095 if schema.get('ref'): # things with refs, such as models and enums, should not have titles set 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2096 return False 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2097 if schema['type'] in {'default', 'nullable', 'definitions'}: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2098 return self.field_title_should_be_set(schema['schema']) # type: ignore[typeddict-item] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2099 if _core_utils.is_function_with_inner_schema(schema): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2100 return self.field_title_should_be_set(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2101 if schema['type'] == 'definition-ref': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2102 # Referenced schemas should not have titles set for the same reason
2103 # schemas with refs should not
2104 return False 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2105 return True # anything else should have title set 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2107 else:
2108 raise PydanticInvalidForJsonSchema(f'Unexpected schema type: schema={schema}') # pragma: no cover
2110 def normalize_name(self, name: str) -> str: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2111 """Normalizes a name to be used as a key in a dictionary.
2113 Args:
2114 name: The name to normalize.
2116 Returns:
2117 The normalized name.
2118 """
2119 return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name).replace('.', '__') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2121 def get_defs_ref(self, core_mode_ref: CoreModeRef) -> DefsRef: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2122 """Override this method to change the way that definitions keys are generated from a core reference.
2124 Args:
2125 core_mode_ref: The core reference.
2127 Returns:
2128 The definitions key.
2129 """
2130 # Split the core ref into "components"; generic origins and arguments are each separate components
2131 core_ref, mode = core_mode_ref 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2132 components = re.split(r'([\][,])', core_ref) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2133 # Remove IDs from each component
2134 components = [x.rsplit(':', 1)[0] for x in components] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2135 core_ref_no_id = ''.join(components) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2136 # Remove everything before the last period from each "component"
2137 components = [re.sub(r'(?:[^.[\]]+\.)+((?:[^.[\]]+))', r'\1', x) for x in components] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2138 short_ref = ''.join(components) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2140 mode_title = _MODE_TITLE_MAPPING[mode] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2142 # It is important that the generated defs_ref values be such that at least one choice will not
2143 # be generated for any other core_ref. Currently, this should be the case because we include
2144 # the id of the source type in the core_ref
2145 name = DefsRef(self.normalize_name(short_ref)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2146 name_mode = DefsRef(self.normalize_name(short_ref) + f'-{mode_title}') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2147 module_qualname = DefsRef(self.normalize_name(core_ref_no_id)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2148 module_qualname_mode = DefsRef(f'{module_qualname}-{mode_title}') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2149 module_qualname_id = DefsRef(self.normalize_name(core_ref)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2150 occurrence_index = self._collision_index.get(module_qualname_id) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2151 if occurrence_index is None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2152 self._collision_counter[module_qualname] += 1 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2153 occurrence_index = self._collision_index[module_qualname_id] = self._collision_counter[module_qualname] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2155 module_qualname_occurrence = DefsRef(f'{module_qualname}__{occurrence_index}') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2156 module_qualname_occurrence_mode = DefsRef(f'{module_qualname_mode}__{occurrence_index}') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2158 self._prioritized_defsref_choices[module_qualname_occurrence_mode] = [ 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2159 name,
2160 name_mode,
2161 module_qualname,
2162 module_qualname_mode,
2163 module_qualname_occurrence,
2164 module_qualname_occurrence_mode,
2165 ]
2167 return module_qualname_occurrence_mode 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2169 def get_cache_defs_ref_schema(self, core_ref: CoreRef) -> tuple[DefsRef, JsonSchemaValue]: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2170 """This method wraps the get_defs_ref method with some cache-lookup/population logic,
2171 and returns both the produced defs_ref and the JSON schema that will refer to the right definition.
2173 Args:
2174 core_ref: The core reference to get the definitions reference for.
2176 Returns:
2177 A tuple of the definitions reference and the JSON schema that will refer to it.
2178 """
2179 core_mode_ref = (core_ref, self.mode) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2180 maybe_defs_ref = self.core_to_defs_refs.get(core_mode_ref) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2181 if maybe_defs_ref is not None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2182 json_ref = self.core_to_json_refs[core_mode_ref] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2183 return maybe_defs_ref, {'$ref': json_ref} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2185 defs_ref = self.get_defs_ref(core_mode_ref) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2187 # populate the ref translation mappings
2188 self.core_to_defs_refs[core_mode_ref] = defs_ref 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2189 self.defs_to_core_refs[defs_ref] = core_mode_ref 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2191 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2192 self.core_to_json_refs[core_mode_ref] = json_ref 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2193 self.json_to_defs_refs[json_ref] = defs_ref 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2194 ref_json_schema = {'$ref': json_ref} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2195 return defs_ref, ref_json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2197 def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2198 """Remove any sibling keys that are redundant with the referenced schema.
2200 Args:
2201 json_schema: The schema to remove redundant sibling keys from.
2203 Returns:
2204 The schema with redundant sibling keys removed.
2205 """
2206 if '$ref' in json_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2207 # prevent modifications to the input; this copy may be safe to drop if there is significant overhead
2208 json_schema = json_schema.copy() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2210 referenced_json_schema = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2211 if referenced_json_schema is None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2212 # This can happen when building schemas for models with not-yet-defined references.
2213 # It may be a good idea to do a recursive pass at the end of the generation to remove
2214 # any redundant override keys.
2215 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2216 for k, v in list(json_schema.items()): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2217 if k == '$ref': 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2218 continue 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2219 if k in referenced_json_schema and referenced_json_schema[k] == v: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2220 del json_schema[k] # redundant key 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2222 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2224 def get_schema_from_definitions(self, json_ref: JsonRef) -> JsonSchemaValue | None: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2225 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2226 def_ref = self.json_to_defs_refs[json_ref] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2227 if def_ref in self._core_defs_invalid_for_json_schema: 2227 ↛ 2228line 2227 didn't jump to line 2228 because the condition on line 2227 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2228 raise self._core_defs_invalid_for_json_schema[def_ref]
2229 return self.definitions.get(def_ref, None) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2230 except KeyError: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2231 if json_ref.startswith(('http://', 'https://')): 2231 ↛ 2233line 2231 didn't jump to line 2233 because the condition on line 2231 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2232 return None 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2233 raise
2235 def encode_default(self, dft: Any) -> Any: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2236 """Encode a default value to a JSON-serializable value.
2238 This is used to encode default values for fields in the generated JSON schema.
2240 Args:
2241 dft: The default value to encode.
2243 Returns:
2244 The encoded default value.
2245 """
2246 from .type_adapter import TypeAdapter, _type_has_config 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2248 config = self._config 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2249 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2250 default = ( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2251 dft
2252 if _type_has_config(type(dft))
2253 else TypeAdapter(type(dft), config=config.config_dict).dump_python(
2254 dft, by_alias=self.by_alias, mode='json'
2255 )
2256 )
2257 except PydanticSchemaGenerationError: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2258 raise pydantic_core.PydanticSerializationError(f'Unable to encode default value {dft}') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2260 return pydantic_core.to_jsonable_python( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2261 default, timedelta_mode=config.ser_json_timedelta, bytes_mode=config.ser_json_bytes, by_alias=self.by_alias
2262 )
2264 def update_with_validations( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2265 self, json_schema: JsonSchemaValue, core_schema: CoreSchema, mapping: dict[str, str]
2266 ) -> None:
2267 """Update the json_schema with the corresponding validations specified in the core_schema,
2268 using the provided mapping to translate keys in core_schema to the appropriate keys for a JSON schema.
2270 Args:
2271 json_schema: The JSON schema to update.
2272 core_schema: The core schema to get the validations from.
2273 mapping: A mapping from core_schema attribute names to the corresponding JSON schema attribute names.
2274 """
2275 for core_key, json_schema_key in mapping.items(): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2276 if core_key in core_schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2277 json_schema[json_schema_key] = core_schema[core_key] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2279 class ValidationsMapping: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2280 """This class just contains mappings from core_schema attribute names to the corresponding
2281 JSON schema attribute names. While I suspect it is unlikely to be necessary, you can in
2282 principle override this class in a subclass of GenerateJsonSchema (by inheriting from
2283 GenerateJsonSchema.ValidationsMapping) to change these mappings.
2284 """
2286 numeric = { 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2287 'multiple_of': 'multipleOf',
2288 'le': 'maximum',
2289 'ge': 'minimum',
2290 'lt': 'exclusiveMaximum',
2291 'gt': 'exclusiveMinimum',
2292 }
2293 bytes = { 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2294 'min_length': 'minLength',
2295 'max_length': 'maxLength',
2296 }
2297 string = { 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2298 'min_length': 'minLength',
2299 'max_length': 'maxLength',
2300 'pattern': 'pattern',
2301 }
2302 array = { 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2303 'min_length': 'minItems',
2304 'max_length': 'maxItems',
2305 }
2306 object = { 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2307 'min_length': 'minProperties',
2308 'max_length': 'maxProperties',
2309 }
2311 def get_flattened_anyof(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2312 members = [] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2313 for schema in schemas: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2314 if len(schema) == 1 and 'anyOf' in schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2315 members.extend(schema['anyOf']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2316 else:
2317 members.append(schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2318 members = _deduplicate_schemas(members) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2319 if len(members) == 1: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2320 return members[0] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2321 return {'anyOf': members} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2323 def get_json_ref_counts(self, json_schema: JsonSchemaValue) -> dict[JsonRef, int]: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2324 """Get all values corresponding to the key '$ref' anywhere in the json_schema."""
2325 json_refs: dict[JsonRef, int] = Counter() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2327 def _add_json_refs(schema: Any) -> None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2328 if isinstance(schema, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2329 if '$ref' in schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2330 json_ref = JsonRef(schema['$ref']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2331 if not isinstance(json_ref, str): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2332 return # in this case, '$ref' might have been the name of a property 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2333 already_visited = json_ref in json_refs 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2334 json_refs[json_ref] += 1 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2335 if already_visited: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2336 return # prevent recursion on a definition that was already visited 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2337 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2338 defs_ref = self.json_to_defs_refs[json_ref] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2339 if defs_ref in self._core_defs_invalid_for_json_schema: 2339 ↛ 2340line 2339 didn't jump to line 2340 because the condition on line 2339 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2340 raise self._core_defs_invalid_for_json_schema[defs_ref]
2341 _add_json_refs(self.definitions[defs_ref]) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2342 except KeyError: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2343 if not json_ref.startswith(('http://', 'https://')): 2343 ↛ 2344line 2343 didn't jump to line 2344 because the condition on line 2343 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2344 raise
2346 for k, v in schema.items(): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2347 if k == 'examples' and isinstance(v, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2348 # Skip examples that may contain arbitrary values and references
2349 # (see the comment in `_get_all_json_refs` for more details).
2350 continue 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2351 _add_json_refs(v) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2352 elif isinstance(schema, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2353 for v in schema: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2354 _add_json_refs(v) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2356 _add_json_refs(json_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2357 return json_refs 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2359 def handle_invalid_for_json_schema(self, schema: CoreSchemaOrField, error_info: str) -> JsonSchemaValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2360 raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2362 def emit_warning(self, kind: JsonSchemaWarningKind, detail: str) -> None: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2363 """This method simply emits PydanticJsonSchemaWarnings based on handling in the `warning_message` method."""
2364 message = self.render_warning_message(kind, detail) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2365 if message is not None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2366 warnings.warn(message, PydanticJsonSchemaWarning) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2368 def render_warning_message(self, kind: JsonSchemaWarningKind, detail: str) -> str | None: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2369 """This method is responsible for ignoring warnings as desired, and for formatting the warning messages.
2371 You can override the value of `ignored_warning_kinds` in a subclass of GenerateJsonSchema
2372 to modify what warnings are generated. If you want more control, you can override this method;
2373 just return None in situations where you don't want warnings to be emitted.
2375 Args:
2376 kind: The kind of warning to render. It can be one of the following:
2378 - 'skipped-choice': A choice field was skipped because it had no valid choices.
2379 - 'non-serializable-default': A default value was skipped because it was not JSON-serializable.
2380 detail: A string with additional details about the warning.
2382 Returns:
2383 The formatted warning message, or `None` if no warning should be emitted.
2384 """
2385 if kind in self.ignored_warning_kinds: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2386 return None 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2387 return f'{detail} [{kind}]' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2389 def _build_definitions_remapping(self) -> _DefinitionsRemapping: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2390 defs_to_json: dict[DefsRef, JsonRef] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2391 for defs_refs in self._prioritized_defsref_choices.values(): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2392 for defs_ref in defs_refs: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2393 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2394 defs_to_json[defs_ref] = json_ref 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2396 return _DefinitionsRemapping.from_prioritized_choices( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2397 self._prioritized_defsref_choices, defs_to_json, self.definitions
2398 )
2400 def _garbage_collect_definitions(self, schema: JsonSchemaValue) -> None: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2401 visited_defs_refs: set[DefsRef] = set() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2402 unvisited_json_refs = _get_all_json_refs(schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2403 while unvisited_json_refs: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2404 next_json_ref = unvisited_json_refs.pop() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2405 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2406 next_defs_ref = self.json_to_defs_refs[next_json_ref] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2407 if next_defs_ref in visited_defs_refs: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2408 continue 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2409 visited_defs_refs.add(next_defs_ref) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2410 unvisited_json_refs.update(_get_all_json_refs(self.definitions[next_defs_ref])) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2411 except KeyError: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2412 if not next_json_ref.startswith(('http://', 'https://')): 2412 ↛ 2413line 2412 didn't jump to line 2413 because the condition on line 2412 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2413 raise
2415 self.definitions = {k: v for k, v in self.definitions.items() if k in visited_defs_refs} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2418# ##### Start JSON Schema Generation Functions #####
2421def model_json_schema( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2422 cls: type[BaseModel] | type[PydanticDataclass],
2423 by_alias: bool = True,
2424 ref_template: str = DEFAULT_REF_TEMPLATE,
2425 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
2426 mode: JsonSchemaMode = 'validation',
2427) -> dict[str, Any]:
2428 """Utility function to generate a JSON Schema for a model.
2430 Args:
2431 cls: The model class to generate a JSON Schema for.
2432 by_alias: If `True` (the default), fields will be serialized according to their alias.
2433 If `False`, fields will be serialized according to their attribute name.
2434 ref_template: The template to use for generating JSON Schema references.
2435 schema_generator: The class to use for generating the JSON Schema.
2436 mode: The mode to use for generating the JSON Schema. It can be one of the following:
2438 - 'validation': Generate a JSON Schema for validating data.
2439 - 'serialization': Generate a JSON Schema for serializing data.
2441 Returns:
2442 The generated JSON Schema.
2443 """
2444 from .main import BaseModel 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2446 schema_generator_instance = schema_generator(by_alias=by_alias, ref_template=ref_template) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2448 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2449 cls.__pydantic_core_schema__.rebuild() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2451 if cls is BaseModel: 2451 ↛ 2452line 2451 didn't jump to line 2452 because the condition on line 2451 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2452 raise AttributeError('model_json_schema() must be called on a subclass of BaseModel, not BaseModel itself.')
2454 assert not isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it' 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2455 return schema_generator_instance.generate(cls.__pydantic_core_schema__, mode=mode) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2458def models_json_schema( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2459 models: Sequence[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode]],
2460 *,
2461 by_alias: bool = True,
2462 title: str | None = None,
2463 description: str | None = None,
2464 ref_template: str = DEFAULT_REF_TEMPLATE,
2465 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
2466) -> tuple[dict[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]:
2467 """Utility function to generate a JSON Schema for multiple models.
2469 Args:
2470 models: A sequence of tuples of the form (model, mode).
2471 by_alias: Whether field aliases should be used as keys in the generated JSON Schema.
2472 title: The title of the generated JSON Schema.
2473 description: The description of the generated JSON Schema.
2474 ref_template: The reference template to use for generating JSON Schema references.
2475 schema_generator: The schema generator to use for generating the JSON Schema.
2477 Returns:
2478 A tuple where:
2479 - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
2480 whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
2481 JsonRef references to definitions that are defined in the second returned element.)
2482 - The second element is a JSON schema containing all definitions referenced in the first returned
2483 element, along with the optional title and description keys.
2484 """
2485 for cls, _ in models: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2486 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 2486 ↛ 2487line 2486 didn't jump to line 2487 because the condition on line 2486 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2487 cls.__pydantic_core_schema__.rebuild()
2489 instance = schema_generator(by_alias=by_alias, ref_template=ref_template) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2490 inputs: list[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode, CoreSchema]] = [ 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2491 (m, mode, m.__pydantic_core_schema__) for m, mode in models
2492 ]
2493 json_schemas_map, definitions = instance.generate_definitions(inputs) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2495 json_schema: dict[str, Any] = {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2496 if definitions: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2497 json_schema['$defs'] = definitions 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2498 if title: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2499 json_schema['title'] = title 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2500 if description: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2501 json_schema['description'] = description 1zCbdenfogpADEaJKhiqrstuvFGILcjkwlxmyBH
2503 return json_schemas_map, json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2506# ##### End JSON Schema Generation Functions #####
2509_HashableJsonValue: TypeAlias = Union[ 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2510 int, float, str, bool, None, tuple['_HashableJsonValue', ...], tuple[tuple[str, '_HashableJsonValue'], ...]
2511]
2514def _deduplicate_schemas(schemas: Iterable[JsonDict]) -> list[JsonDict]: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2515 return list({_make_json_hashable(schema): schema for schema in schemas}.values()) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2518def _make_json_hashable(value: JsonValue) -> _HashableJsonValue: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2519 if isinstance(value, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2520 return tuple(sorted((k, _make_json_hashable(v)) for k, v in value.items())) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2521 elif isinstance(value, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2522 return tuple(_make_json_hashable(v) for v in value) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2523 else:
2524 return value 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2527@dataclasses.dataclass(**_internal_dataclass.slots_true) 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2528class WithJsonSchema: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2529 """!!! abstract "Usage Documentation"
2530 [`WithJsonSchema` Annotation](../concepts/json_schema.md#withjsonschema-annotation)
2532 Add this as an annotation on a field to override the (base) JSON schema that would be generated for that field.
2533 This provides a way to set a JSON schema for types that would otherwise raise errors when producing a JSON schema,
2534 such as Callable, or types that have an is-instance core schema, without needing to go so far as creating a
2535 custom subclass of pydantic.json_schema.GenerateJsonSchema.
2536 Note that any _modifications_ to the schema that would normally be made (such as setting the title for model fields)
2537 will still be performed.
2539 If `mode` is set this will only apply to that schema generation mode, allowing you
2540 to set different json schemas for validation and serialization.
2541 """
2543 json_schema: JsonSchemaValue | None 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2544 mode: Literal['validation', 'serialization'] | None = None 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2546 def __get_pydantic_json_schema__( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2547 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2548 ) -> JsonSchemaValue:
2549 mode = self.mode or handler.mode 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2550 if mode != handler.mode: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2551 return handler(core_schema) 1zbefgAIcklmB
2552 if self.json_schema is None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2553 # This exception is handled in pydantic.json_schema.GenerateJsonSchema._named_required_fields_schema
2554 raise PydanticOmit 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2555 else:
2556 return self.json_schema.copy() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2558 def __hash__(self) -> int: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2559 return hash(type(self.mode)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2562class Examples: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2563 """Add examples to a JSON schema.
2565 If the JSON Schema already contains examples, the provided examples
2566 will be appended.
2568 If `mode` is set this will only apply to that schema generation mode,
2569 allowing you to add different examples for validation and serialization.
2570 """
2572 @overload 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2573 @deprecated('Using a dict for `examples` is deprecated since v2.9 and will be removed in v3.0. Use a list instead.') 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2574 def __init__( 1zCbdenfogpADOJKhiqrstuvFGMPILcjkwlxmyBHN
2575 self, examples: dict[str, Any], mode: Literal['validation', 'serialization'] | None = None 1bdenfogpEahiqrstuvPcjkwlxmy
2576 ) -> None: ... 1fogpEastuvPlxmy
2578 @overload 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2579 def __init__(self, examples: list[Any], mode: Literal['validation', 'serialization'] | None = None) -> None: ... 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2581 def __init__( 1zCbdenfogpADOJKhiqrstuvFGMPILcjkwlxmyBHN
2582 self, examples: dict[str, Any] | list[Any], mode: Literal['validation', 'serialization'] | None = None
2583 ) -> None:
2584 if isinstance(examples, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2585 warnings.warn( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2586 'Using a dict for `examples` is deprecated, use a list instead.',
2587 PydanticDeprecatedSince29,
2588 stacklevel=2,
2589 )
2590 self.examples = examples 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2591 self.mode = mode 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2593 def __get_pydantic_json_schema__( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2594 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2595 ) -> JsonSchemaValue:
2596 mode = self.mode or handler.mode 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2597 json_schema = handler(core_schema) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2598 if mode != handler.mode: 2598 ↛ 2599line 2598 didn't jump to line 2599 because the condition on line 2598 was never true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2599 return json_schema
2600 examples = json_schema.get('examples') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2601 if examples is None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2602 json_schema['examples'] = to_jsonable_python(self.examples) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2603 if isinstance(examples, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2604 if isinstance(self.examples, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2605 warnings.warn( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2606 'Updating existing JSON Schema examples of type dict with examples of type list. '
2607 'Only the existing examples values will be retained. Note that dict support for '
2608 'examples is deprecated and will be removed in v3.0.',
2609 UserWarning,
2610 )
2611 json_schema['examples'] = to_jsonable_python( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2612 [ex for value in examples.values() for ex in value] + self.examples
2613 )
2614 else:
2615 json_schema['examples'] = to_jsonable_python({**examples, **self.examples}) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2616 if isinstance(examples, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2617 if isinstance(self.examples, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2618 json_schema['examples'] = to_jsonable_python(examples + self.examples) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2619 elif isinstance(self.examples, dict): 2619 ↛ 2630line 2619 didn't jump to line 2630 because the condition on line 2619 was always true1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2620 warnings.warn( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2621 'Updating existing JSON Schema examples of type list with examples of type dict. '
2622 'Only the examples values will be retained. Note that dict support for '
2623 'examples is deprecated and will be removed in v3.0.',
2624 UserWarning,
2625 )
2626 json_schema['examples'] = to_jsonable_python( 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2627 examples + [ex for value in self.examples.values() for ex in value]
2628 )
2630 return json_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2632 def __hash__(self) -> int: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2633 return hash(type(self.mode)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2636def _get_all_json_refs(item: Any) -> set[JsonRef]: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2637 """Get all the definitions references from a JSON schema."""
2638 refs: set[JsonRef] = set() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2639 stack = [item] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2641 while stack: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2642 current = stack.pop() 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2643 if isinstance(current, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2644 for key, value in current.items(): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2645 if key == 'examples' and isinstance(value, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2646 # Skip examples that may contain arbitrary values and references
2647 # (e.g. `{"examples": [{"$ref": "..."}]}`). Note: checking for value
2648 # of type list is necessary to avoid skipping valid portions of the schema,
2649 # for instance when "examples" is used as a property key. A more robust solution
2650 # could be found, but would require more advanced JSON Schema parsing logic.
2651 continue 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2652 if key == '$ref' and isinstance(value, str): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2653 refs.add(JsonRef(value)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2654 elif isinstance(value, dict): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2655 stack.append(value) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2656 elif isinstance(value, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2657 stack.extend(value) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2658 elif isinstance(current, list): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2659 stack.extend(current) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2661 return refs 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2664AnyType = TypeVar('AnyType') 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2666if TYPE_CHECKING: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2667 SkipJsonSchema = Annotated[AnyType, ...]
2668else:
2670 @dataclasses.dataclass(**_internal_dataclass.slots_true) 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2671 class SkipJsonSchema: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2672 """!!! abstract "Usage Documentation"
2673 [`SkipJsonSchema` Annotation](../concepts/json_schema.md#skipjsonschema-annotation)
2675 Add this as an annotation on a field to skip generating a JSON schema for that field.
2677 Example:
2678 ```python
2679 from pprint import pprint
2680 from typing import Union
2682 from pydantic import BaseModel
2683 from pydantic.json_schema import SkipJsonSchema
2685 class Model(BaseModel):
2686 a: Union[int, None] = None # (1)!
2687 b: Union[int, SkipJsonSchema[None]] = None # (2)!
2688 c: SkipJsonSchema[Union[int, None]] = None # (3)!
2690 pprint(Model.model_json_schema())
2691 '''
2692 {
2693 'properties': {
2694 'a': {
2695 'anyOf': [
2696 {'type': 'integer'},
2697 {'type': 'null'}
2698 ],
2699 'default': None,
2700 'title': 'A'
2701 },
2702 'b': {
2703 'default': None,
2704 'title': 'B',
2705 'type': 'integer'
2706 }
2707 },
2708 'title': 'Model',
2709 'type': 'object'
2710 }
2711 '''
2712 ```
2714 1. The integer and null types are both included in the schema for `a`.
2715 2. The integer type is the only type included in the schema for `b`.
2716 3. The entirety of the `c` field is omitted from the schema.
2717 """
2719 def __class_getitem__(cls, item: AnyType) -> AnyType: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2720 return Annotated[item, cls()] 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2722 def __get_pydantic_json_schema__( 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2723 self, core_schema: CoreSchema, handler: GetJsonSchemaHandler
2724 ) -> JsonSchemaValue:
2725 raise PydanticOmit 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2727 def __hash__(self) -> int: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2728 return hash(type(self)) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2731def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2732 if cls is not None: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2733 try: 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2734 return _decorators.get_attribute_from_bases(cls, '__pydantic_config__') 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2735 except AttributeError: 1zCbdenfogpADOEaJKhiqrstuvFGILcjkwlxmyBH
2736 pass 1zCbdenfogpADOEaJKhiqrstuvFGILcjkwlxmyBH
2737 return {} 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2740def _get_ser_schema_for_default_value(schema: CoreSchema) -> core_schema.PlainSerializerFunctionSerSchema | None: 1zCbdenfogpADOEaJKhiqrstuvFGMPILcjkwlxmyBHN
2741 """Get a `'function-plain'` serialization schema that can be used to serialize a default value.
2743 This takes into account having the serialization schema nested under validation schema(s).
2744 """
2745 if ( 1zCbdenEaJKhiqrILcjkw
2746 (ser_schema := schema.get('serialization'))
2747 and ser_schema['type'] == 'function-plain'
2748 and not ser_schema.get('info_arg')
2749 ):
2750 return ser_schema 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2751 if _core_utils.is_function_with_inner_schema(schema): 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN
2752 return _get_ser_schema_for_default_value(schema['schema']) 1zCbdenfogpADOEaJKhiqrstuvFGMILcjkwlxmyBHN