Coverage for pydantic/json_schema.py: 94.57%
1020 statements
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +0000
1"""
2Usage docs: https://docs.pydantic.dev/2.5/concepts/json_schema/
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 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
14import dataclasses 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
15import inspect 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
16import math 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
17import re 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
18import warnings 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
19from collections import defaultdict 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
20from copy import deepcopy 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
21from dataclasses import is_dataclass 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
22from enum import Enum 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
23from typing import ( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
24 TYPE_CHECKING,
25 Any,
26 Callable,
27 Counter,
28 Dict,
29 Hashable,
30 Iterable,
31 NewType,
32 Pattern,
33 Sequence,
34 Tuple,
35 TypeVar,
36 Union,
37 cast,
38)
40import pydantic_core 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
41from pydantic_core import CoreSchema, PydanticOmit, core_schema, to_jsonable_python 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
42from pydantic_core.core_schema import ComputedField 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
43from typing_extensions import Annotated, Literal, TypeAlias, assert_never, deprecated, final 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
45from pydantic.warnings import PydanticDeprecatedSince26 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
47from ._internal import ( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
48 _config,
49 _core_metadata,
50 _core_utils,
51 _decorators,
52 _internal_dataclass,
53 _mock_val_ser,
54 _schema_generation_shared,
55 _typing_extra,
56)
57from .annotated_handlers import GetJsonSchemaHandler 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
58from .config import JsonDict, JsonSchemaExtraCallable, JsonValue 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
59from .errors import PydanticInvalidForJsonSchema, PydanticSchemaGenerationError, PydanticUserError 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
61if TYPE_CHECKING: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
62 from . import ConfigDict
63 from ._internal._core_utils import CoreSchemaField, CoreSchemaOrField
64 from ._internal._dataclasses import PydanticDataclass
65 from ._internal._schema_generation_shared import GetJsonSchemaFunction
66 from .main import BaseModel
69CoreSchemaOrFieldType = Literal[core_schema.CoreSchemaType, core_schema.CoreSchemaFieldType] 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
70""" 1adejkpbfglmqrGHIJKLMchinos
71A type alias for defined schema types that represents a union of
72`core_schema.CoreSchemaType` and
73`core_schema.CoreSchemaFieldType`.
74"""
76JsonSchemaValue = Dict[str, Any] 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
77""" 1adejkpbfglmqrGHIJKLMchinos
78A type alias for a JSON schema value. This is a dictionary of string keys to arbitrary JSON values.
79"""
81JsonSchemaMode = Literal['validation', 'serialization'] 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
82""" 1adejkpbfglmqrGHIJKLMchinos
83A type alias that represents the mode of a JSON schema; either 'validation' or 'serialization'.
85For some types, the inputs to validation differ from the outputs of serialization. For example,
86computed fields will only be present when serializing, and should not be provided when
87validating. This flag provides a way to indicate whether you want the JSON schema required
88for validation inputs, or that will be matched by serialization outputs.
89"""
91_MODE_TITLE_MAPPING: dict[JsonSchemaMode, str] = {'validation': 'Input', 'serialization': 'Output'} 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
94@deprecated( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
95 '`update_json_schema` is deprecated, use a simple `my_dict.update(update_dict)` call instead.',
96 category=None,
97)
98def update_json_schema(schema: JsonSchemaValue, updates: dict[str, Any]) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
99 """Update a JSON schema in-place by providing a dictionary of updates.
101 This function sets the provided key-value pairs in the schema and returns the updated schema.
103 Args:
104 schema: The JSON schema to update.
105 updates: A dictionary of key-value pairs to set in the schema.
107 Returns:
108 The updated JSON schema.
109 """
110 schema.update(updates)
111 return schema
114JsonSchemaWarningKind = Literal['skipped-choice', 'non-serializable-default'] 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
115""" 1adejkpbfglmqrGHIJKLMchinos
116A type alias representing the kinds of warnings that can be emitted during JSON schema generation.
118See [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message]
119for more details.
120"""
123class PydanticJsonSchemaWarning(UserWarning): 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
124 """This class is used to emit warnings produced during JSON schema generation.
125 See the [`GenerateJsonSchema.emit_warning`][pydantic.json_schema.GenerateJsonSchema.emit_warning] and
126 [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message]
127 methods for more details; these can be overridden to control warning behavior.
128 """
131# ##### JSON Schema Generation #####
132DEFAULT_REF_TEMPLATE = '#/$defs/{model}' 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
133"""The default format string used to generate reference names.""" 1adejkpbfglmqrGHIJKLMchinos
135# There are three types of references relevant to building JSON schemas:
136# 1. core_schema "ref" values; these are not exposed as part of the JSON schema
137# * these might look like the fully qualified path of a model, its id, or something similar
138CoreRef = NewType('CoreRef', str) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
139# 2. keys of the "definitions" object that will eventually go into the JSON schema
140# * by default, these look like "MyModel", though may change in the presence of collisions
141# * eventually, we may want to make it easier to modify the way these names are generated
142DefsRef = NewType('DefsRef', str) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
143# 3. the values corresponding to the "$ref" key in the schema
144# * By default, these look like "#/$defs/MyModel", as in {"$ref": "#/$defs/MyModel"}
145JsonRef = NewType('JsonRef', str) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
147CoreModeRef = Tuple[CoreRef, JsonSchemaMode] 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
148JsonSchemaKeyT = TypeVar('JsonSchemaKeyT', bound=Hashable) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
151@dataclasses.dataclass(**_internal_dataclass.slots_true) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
152class _DefinitionsRemapping: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
153 defs_remapping: dict[DefsRef, DefsRef] 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
154 json_remapping: dict[JsonRef, JsonRef] 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
156 @staticmethod 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
157 def from_prioritized_choices( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
158 prioritized_choices: dict[DefsRef, list[DefsRef]],
159 defs_to_json: dict[DefsRef, JsonRef],
160 definitions: dict[DefsRef, JsonSchemaValue],
161 ) -> _DefinitionsRemapping:
162 """
163 This function should produce a remapping that replaces complex DefsRef with the simpler ones from the
164 prioritized_choices such that applying the name remapping would result in an equivalent JSON schema.
165 """
166 # We need to iteratively simplify the definitions until we reach a fixed point.
167 # The reason for this is that outer definitions may reference inner definitions that get simplified
168 # into an equivalent reference, and the outer definitions won't be equivalent until we've simplified
169 # the inner definitions.
170 copied_definitions = deepcopy(definitions) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
171 definitions_schema = {'$defs': copied_definitions} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
172 for _iter in range(100): # prevent an infinite loop in the case of a bug, 100 iterations should be enough 172 ↛ 201line 172 didn't jump to line 201, because the loop on line 172 didn't complete1yCtvadejkpwbEFzAfglmqrBDuxchinos
173 # For every possible remapped DefsRef, collect all schemas that that DefsRef might be used for:
174 schemas_for_alternatives: dict[DefsRef, list[JsonSchemaValue]] = defaultdict(list) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
175 for defs_ref in copied_definitions: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
176 alternatives = prioritized_choices[defs_ref] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
177 for alternative in alternatives: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
178 schemas_for_alternatives[alternative].append(copied_definitions[defs_ref]) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
180 # Deduplicate the schemas for each alternative; the idea is that we only want to remap to a new DefsRef
181 # if it introduces no ambiguity, i.e., there is only one distinct schema for that DefsRef.
182 for defs_ref, schemas in schemas_for_alternatives.items(): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
183 schemas_for_alternatives[defs_ref] = _deduplicate_schemas(schemas_for_alternatives[defs_ref]) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
185 # Build the remapping
186 defs_remapping: dict[DefsRef, DefsRef] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
187 json_remapping: dict[JsonRef, JsonRef] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
188 for original_defs_ref in definitions: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
189 alternatives = prioritized_choices[original_defs_ref] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
190 # Pick the first alternative that has only one schema, since that means there is no collision
191 remapped_defs_ref = next(x for x in alternatives if len(schemas_for_alternatives[x]) == 1) 191 ↛ exitline 191 didn't finish the generator expression on line 1911yCtvadejkpwbEFzAfglmqrBDuxchinos
192 defs_remapping[original_defs_ref] = remapped_defs_ref 1yCtvadejkpwbEFzAfglmqrBDuxchinos
193 json_remapping[defs_to_json[original_defs_ref]] = defs_to_json[remapped_defs_ref] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
194 remapping = _DefinitionsRemapping(defs_remapping, json_remapping) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
195 new_definitions_schema = remapping.remap_json_schema({'$defs': copied_definitions}) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
196 if definitions_schema == new_definitions_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
197 # We've reached the fixed point
198 return remapping 1yCtvadejkpwbEFzAfglmqrBDuxchinos
199 definitions_schema = new_definitions_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
201 raise PydanticInvalidForJsonSchema('Failed to simplify the JSON schema definitions')
203 def remap_defs_ref(self, ref: DefsRef) -> DefsRef: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
204 return self.defs_remapping.get(ref, ref) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
206 def remap_json_ref(self, ref: JsonRef) -> JsonRef: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
207 return self.json_remapping.get(ref, ref) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
209 def remap_json_schema(self, schema: Any) -> Any: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
210 """
211 Recursively update the JSON schema replacing all $refs
212 """
213 if isinstance(schema, str): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
214 # Note: this may not really be a JsonRef; we rely on having no collisions between JsonRefs and other strings
215 return self.remap_json_ref(JsonRef(schema)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
216 elif isinstance(schema, list): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
217 return [self.remap_json_schema(item) for item in schema] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
218 elif isinstance(schema, dict): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
219 for key, value in schema.items(): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
220 if key == '$ref' and isinstance(value, str): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
221 schema['$ref'] = self.remap_json_ref(JsonRef(value)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
222 elif key == '$defs': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
223 schema['$defs'] = { 1yCtvadejkpwbEFzAfglmqrBDuxchinos
224 self.remap_defs_ref(DefsRef(key)): self.remap_json_schema(value)
225 for key, value in schema['$defs'].items()
226 }
227 else:
228 schema[key] = self.remap_json_schema(value) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
229 return schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
232class GenerateJsonSchema: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
233 """Usage docs: https://docs.pydantic.dev/2.8/concepts/json_schema/#customizing-the-json-schema-generation-process
235 A class for generating JSON schemas.
237 This class generates JSON schemas based on configured parameters. The default schema dialect
238 is [https://json-schema.org/draft/2020-12/schema](https://json-schema.org/draft/2020-12/schema).
239 The class uses `by_alias` to configure how fields with
240 multiple names are handled and `ref_template` to format reference names.
242 Attributes:
243 schema_dialect: The JSON schema dialect used to generate the schema. See
244 [Declaring a Dialect](https://json-schema.org/understanding-json-schema/reference/schema.html#id4)
245 in the JSON Schema documentation for more information about dialects.
246 ignored_warning_kinds: Warnings to ignore when generating the schema. `self.render_warning_message` will
247 do nothing if its argument `kind` is in `ignored_warning_kinds`;
248 this value can be modified on subclasses to easily control which warnings are emitted.
249 by_alias: Whether to use field aliases when generating the schema.
250 ref_template: The format string used when generating reference names.
251 core_to_json_refs: A mapping of core refs to JSON refs.
252 core_to_defs_refs: A mapping of core refs to definition refs.
253 defs_to_core_refs: A mapping of definition refs to core refs.
254 json_to_defs_refs: A mapping of JSON refs to definition refs.
255 definitions: Definitions in the schema.
257 Args:
258 by_alias: Whether to use field aliases in the generated schemas.
259 ref_template: The format string to use when generating reference names.
261 Raises:
262 JsonSchemaError: If the instance of the class is inadvertently re-used after generating a schema.
263 """
265 schema_dialect = 'https://json-schema.org/draft/2020-12/schema' 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
267 # `self.render_warning_message` will do nothing if its argument `kind` is in `ignored_warning_kinds`;
268 # this value can be modified on subclasses to easily control which warnings are emitted
269 ignored_warning_kinds: set[JsonSchemaWarningKind] = {'skipped-choice'} 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
271 def __init__(self, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE): 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
272 self.by_alias = by_alias 1yCtvadejkpwbEFzAfglmqrBDuxchinos
273 self.ref_template = ref_template 1yCtvadejkpwbEFzAfglmqrBDuxchinos
275 self.core_to_json_refs: dict[CoreModeRef, JsonRef] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
276 self.core_to_defs_refs: dict[CoreModeRef, DefsRef] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
277 self.defs_to_core_refs: dict[DefsRef, CoreModeRef] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
278 self.json_to_defs_refs: dict[JsonRef, DefsRef] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
280 self.definitions: dict[DefsRef, JsonSchemaValue] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
281 self._config_wrapper_stack = _config.ConfigWrapperStack(_config.ConfigWrapper({})) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
283 self._mode: JsonSchemaMode = 'validation' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
285 # The following includes a mapping of a fully-unique defs ref choice to a list of preferred
286 # alternatives, which are generally simpler, such as only including the class name.
287 # At the end of schema generation, we use these to produce a JSON schema with more human-readable
288 # definitions, which would also work better in a generated OpenAPI client, etc.
289 self._prioritized_defsref_choices: dict[DefsRef, list[DefsRef]] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
290 self._collision_counter: dict[str, int] = defaultdict(int) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
291 self._collision_index: dict[str, int] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
293 self._schema_type_to_method = self.build_schema_type_to_method() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
295 # When we encounter definitions we need to try to build them immediately
296 # so that they are available schemas that reference them
297 # But it's possible that CoreSchema was never going to be used
298 # (e.g. because the CoreSchema that references short circuits is JSON schema generation without needing
299 # the reference) so instead of failing altogether if we can't build a definition we
300 # store the error raised and re-throw it if we end up needing that def
301 self._core_defs_invalid_for_json_schema: dict[DefsRef, PydanticInvalidForJsonSchema] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
303 # This changes to True after generating a schema, to prevent issues caused by accidental re-use
304 # of a single instance of a schema generator
305 self._used = False 1yCtvadejkpwbEFzAfglmqrBDuxchinos
307 @property 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
308 def _config(self) -> _config.ConfigWrapper: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
309 return self._config_wrapper_stack.tail 1yCtvadejkpwbEFzAfglmqrBDuxchinos
311 @property 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
312 def mode(self) -> JsonSchemaMode: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
313 if self._config.json_schema_mode_override is not None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
314 return self._config.json_schema_mode_override 1yCtvadejkpwbEFzAfglmqrBDuxchinos
315 else:
316 return self._mode 1yCtvadejkpwbEFzAfglmqrBDuxchinos
318 def build_schema_type_to_method( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
319 self,
320 ) -> dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]]:
321 """Builds a dictionary mapping fields to methods for generating JSON schemas.
323 Returns:
324 A dictionary containing the mapping of `CoreSchemaOrFieldType` to a handler method.
326 Raises:
327 TypeError: If no method has been defined for generating a JSON schema for a given pydantic core schema type.
328 """
329 mapping: dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
330 core_schema_types: list[CoreSchemaOrFieldType] = _typing_extra.all_literal_values( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
331 CoreSchemaOrFieldType # type: ignore
332 )
333 for key in core_schema_types: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
334 method_name = f"{key.replace('-', '_')}_schema" 1yCtvadejkpwbEFzAfglmqrBDuxchinos
335 try: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
336 mapping[key] = getattr(self, method_name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
337 except AttributeError as e: # pragma: no cover
338 raise TypeError(
339 f'No method for generating JsonSchema for core_schema.type={key!r} '
340 f'(expected: {type(self).__name__}.{method_name})'
341 ) from e
342 return mapping 1yCtvadejkpwbEFzAfglmqrBDuxchinos
344 def generate_definitions( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
345 self, inputs: Sequence[tuple[JsonSchemaKeyT, JsonSchemaMode, core_schema.CoreSchema]]
346 ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], dict[DefsRef, JsonSchemaValue]]:
347 """Generates JSON schema definitions from a list of core schemas, pairing the generated definitions with a
348 mapping that links the input keys to the definition references.
350 Args:
351 inputs: A sequence of tuples, where:
353 - The first element is a JSON schema key type.
354 - The second element is the JSON mode: either 'validation' or 'serialization'.
355 - The third element is a core schema.
357 Returns:
358 A tuple where:
360 - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
361 whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
362 JsonRef references to definitions that are defined in the second returned element.)
363 - The second element is a dictionary whose keys are definition references for the JSON schemas
364 from the first returned element, and whose values are the actual JSON schema definitions.
366 Raises:
367 PydanticUserError: Raised if the JSON schema generator has already been used to generate a JSON schema.
368 """
369 if self._used: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
370 raise PydanticUserError( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
371 'This JSON schema generator has already been used to generate a JSON schema. '
372 f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.',
373 code='json-schema-already-used',
374 )
376 for key, mode, schema in inputs: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
377 self._mode = mode 1yCtvadejkpwbEFzAfglmqrBDuxchinos
378 self.generate_inner(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
380 definitions_remapping = self._build_definitions_remapping() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
382 json_schemas_map: dict[tuple[JsonSchemaKeyT, JsonSchemaMode], DefsRef] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
383 for key, mode, schema in inputs: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
384 self._mode = mode 1yCtvadejkpwbEFzAfglmqrBDuxchinos
385 json_schema = self.generate_inner(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
386 json_schemas_map[(key, mode)] = definitions_remapping.remap_json_schema(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
388 json_schema = {'$defs': self.definitions} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
389 json_schema = definitions_remapping.remap_json_schema(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
390 self._used = True 1yCtvadejkpwbEFzAfglmqrBDuxchinos
391 return json_schemas_map, _sort_json_schema(json_schema['$defs']) # type: ignore 1yCtvadejkpwbEFzAfglmqrBDuxchinos
393 def generate(self, schema: CoreSchema, mode: JsonSchemaMode = 'validation') -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
394 """Generates a JSON schema for a specified schema in a specified mode.
396 Args:
397 schema: A Pydantic model.
398 mode: The mode in which to generate the schema. Defaults to 'validation'.
400 Returns:
401 A JSON schema representing the specified schema.
403 Raises:
404 PydanticUserError: If the JSON schema generator has already been used to generate a JSON schema.
405 """
406 self._mode = mode 1yCtvadejkpwbEFzAfglmqrBDuxchinos
407 if self._used: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
408 raise PydanticUserError( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
409 'This JSON schema generator has already been used to generate a JSON schema. '
410 f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.',
411 code='json-schema-already-used',
412 )
414 json_schema: JsonSchemaValue = self.generate_inner(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
415 json_ref_counts = self.get_json_ref_counts(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
417 # Remove the top-level $ref if present; note that the _generate method already ensures there are no sibling keys
418 ref = cast(JsonRef, json_schema.get('$ref')) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
419 while ref is not None: # may need to unpack multiple levels 1yCtvadejkpwbEFzAfglmqrBDuxchinos
420 ref_json_schema = self.get_schema_from_definitions(ref) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
421 if json_ref_counts[ref] > 1 or ref_json_schema is None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
422 # Keep the ref, but use an allOf to remove the top level $ref
423 json_schema = {'allOf': [{'$ref': ref}]} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
424 else:
425 # "Unpack" the ref since this is the only reference
426 json_schema = ref_json_schema.copy() # copy to prevent recursive dict reference 1yCtvadejkpwbEFzAfglmqrBDuxchinos
427 json_ref_counts[ref] -= 1 1yCtvadejkpwbEFzAfglmqrBDuxchinos
428 ref = cast(JsonRef, json_schema.get('$ref')) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
430 self._garbage_collect_definitions(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
431 definitions_remapping = self._build_definitions_remapping() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
433 if self.definitions: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
434 json_schema['$defs'] = self.definitions 1yCtvadejkpwbEFzAfglmqrBDuxchinos
436 json_schema = definitions_remapping.remap_json_schema(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
438 # For now, we will not set the $schema key. However, if desired, this can be easily added by overriding
439 # this method and adding the following line after a call to super().generate(schema):
440 # json_schema['$schema'] = self.schema_dialect
442 self._used = True 1yCtvadejkpwbEFzAfglmqrBDuxchinos
443 return _sort_json_schema(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
445 def generate_inner(self, schema: CoreSchemaOrField) -> JsonSchemaValue: # noqa: C901 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
446 """Generates a JSON schema for a given core schema.
448 Args:
449 schema: The given core schema.
451 Returns:
452 The generated JSON schema.
453 """
454 # If a schema with the same CoreRef has been handled, just return a reference to it
455 # Note that this assumes that it will _never_ be the case that the same CoreRef is used
456 # on types that should have different JSON schemas
457 if 'ref' in schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
458 core_ref = CoreRef(schema['ref']) # type: ignore[typeddict-item] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
459 core_mode_ref = (core_ref, self.mode) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
460 if core_mode_ref in self.core_to_defs_refs and self.core_to_defs_refs[core_mode_ref] in self.definitions: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
461 return {'$ref': self.core_to_json_refs[core_mode_ref]} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
463 # Generate the JSON schema, accounting for the json_schema_override and core_schema_override
464 metadata_handler = _core_metadata.CoreMetadataHandler(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
466 def populate_defs(core_schema: CoreSchema, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
467 if 'ref' in core_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
468 core_ref = CoreRef(core_schema['ref']) # type: ignore[typeddict-item] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
469 defs_ref, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
470 json_ref = JsonRef(ref_json_schema['$ref']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
471 self.json_to_defs_refs[json_ref] = defs_ref 1yCtvadejkpwbEFzAfglmqrBDuxchinos
472 # Replace the schema if it's not a reference to itself
473 # What we want to avoid is having the def be just a ref to itself
474 # which is what would happen if we blindly assigned any
475 if json_schema.get('$ref', None) != json_ref: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
476 self.definitions[defs_ref] = json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
477 self._core_defs_invalid_for_json_schema.pop(defs_ref, None) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
478 json_schema = ref_json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
479 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
481 def convert_to_all_of(json_schema: JsonSchemaValue) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
482 if '$ref' in json_schema and len(json_schema.keys()) > 1: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
483 # technically you can't have any other keys next to a "$ref"
484 # but it's an easy mistake to make and not hard to correct automatically here
485 json_schema = json_schema.copy() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
486 ref = json_schema.pop('$ref') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
487 json_schema = {'allOf': [{'$ref': ref}], **json_schema} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
488 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
490 def handler_func(schema_or_field: CoreSchemaOrField) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
491 """Generate a JSON schema based on the input schema.
493 Args:
494 schema_or_field: The core schema to generate a JSON schema from.
496 Returns:
497 The generated JSON schema.
499 Raises:
500 TypeError: If an unexpected schema type is encountered.
501 """
502 # Generate the core-schema-type-specific bits of the schema generation:
503 json_schema: JsonSchemaValue | None = None 1yCtvadejkpwbEFzAfglmqrBDuxchinos
504 if self.mode == 'serialization' and 'serialization' in schema_or_field: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
505 ser_schema = schema_or_field['serialization'] # type: ignore 1yCtvadejkpwbEFzAfglmqrBDuxchinos
506 json_schema = self.ser_schema(ser_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
507 if json_schema is None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
508 if _core_utils.is_core_schema(schema_or_field) or _core_utils.is_core_schema_field(schema_or_field): 508 ↛ 512line 508 didn't jump to line 512, because the condition on line 508 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
509 generate_for_schema_type = self._schema_type_to_method[schema_or_field['type']] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
510 json_schema = generate_for_schema_type(schema_or_field) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
511 else:
512 raise TypeError(f'Unexpected schema type: schema={schema_or_field}')
513 if _core_utils.is_core_schema(schema_or_field): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
514 json_schema = populate_defs(schema_or_field, json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
515 json_schema = convert_to_all_of(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
516 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
518 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, handler_func) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
520 for js_modify_function in metadata_handler.metadata.get('pydantic_js_functions', ()): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
522 def new_handler_func( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
523 schema_or_field: CoreSchemaOrField,
524 current_handler: GetJsonSchemaHandler = current_handler,
525 js_modify_function: GetJsonSchemaFunction = js_modify_function,
526 ) -> JsonSchemaValue:
527 json_schema = js_modify_function(schema_or_field, current_handler) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
528 if _core_utils.is_core_schema(schema_or_field): 528 ↛ 530line 528 didn't jump to line 530, because the condition on line 528 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
529 json_schema = populate_defs(schema_or_field, json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
530 original_schema = current_handler.resolve_ref_schema(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
531 ref = json_schema.pop('$ref', None) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
532 if ref and json_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
533 original_schema.update(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
534 return original_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
536 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
538 for js_modify_function in metadata_handler.metadata.get('pydantic_js_annotation_functions', ()): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
540 def new_handler_func( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
541 schema_or_field: CoreSchemaOrField,
542 current_handler: GetJsonSchemaHandler = current_handler,
543 js_modify_function: GetJsonSchemaFunction = js_modify_function,
544 ) -> JsonSchemaValue:
545 json_schema = js_modify_function(schema_or_field, current_handler) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
546 if _core_utils.is_core_schema(schema_or_field): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
547 json_schema = populate_defs(schema_or_field, json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
548 json_schema = convert_to_all_of(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
549 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
551 current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
553 json_schema = current_handler(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
554 if _core_utils.is_core_schema(schema): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
555 json_schema = populate_defs(schema, json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
556 json_schema = convert_to_all_of(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
557 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
559 # ### Schema generation methods
560 def any_schema(self, schema: core_schema.AnySchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
561 """Generates a JSON schema that matches any value.
563 Args:
564 schema: The core schema.
566 Returns:
567 The generated JSON schema.
568 """
569 return {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
571 def none_schema(self, schema: core_schema.NoneSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
572 """Generates a JSON schema that matches `None`.
574 Args:
575 schema: The core schema.
577 Returns:
578 The generated JSON schema.
579 """
580 return {'type': 'null'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
582 def bool_schema(self, schema: core_schema.BoolSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
583 """Generates a JSON schema that matches a bool value.
585 Args:
586 schema: The core schema.
588 Returns:
589 The generated JSON schema.
590 """
591 return {'type': 'boolean'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
593 def int_schema(self, schema: core_schema.IntSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
594 """Generates a JSON schema that matches an int value.
596 Args:
597 schema: The core schema.
599 Returns:
600 The generated JSON schema.
601 """
602 json_schema: dict[str, Any] = {'type': 'integer'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
603 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
604 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
605 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
607 def float_schema(self, schema: core_schema.FloatSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
608 """Generates a JSON schema that matches a float value.
610 Args:
611 schema: The core schema.
613 Returns:
614 The generated JSON schema.
615 """
616 json_schema: dict[str, Any] = {'type': 'number'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
617 self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
618 json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
619 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
621 def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
622 """Generates a JSON schema that matches a decimal value.
624 Args:
625 schema: The core schema.
627 Returns:
628 The generated JSON schema.
629 """
630 json_schema = self.str_schema(core_schema.str_schema()) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
631 if self.mode == 'validation': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
632 multiple_of = schema.get('multiple_of') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
633 le = schema.get('le') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
634 ge = schema.get('ge') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
635 lt = schema.get('lt') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
636 gt = schema.get('gt') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
637 json_schema = { 1yCtvadejkpwbEFzAfglmqrBDuxchinos
638 'anyOf': [
639 self.float_schema(
640 core_schema.float_schema(
641 allow_inf_nan=schema.get('allow_inf_nan'),
642 multiple_of=None if multiple_of is None else float(multiple_of),
643 le=None if le is None else float(le),
644 ge=None if ge is None else float(ge),
645 lt=None if lt is None else float(lt),
646 gt=None if gt is None else float(gt),
647 )
648 ),
649 json_schema,
650 ],
651 }
652 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
654 def str_schema(self, schema: core_schema.StringSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
655 """Generates a JSON schema that matches a string value.
657 Args:
658 schema: The core schema.
660 Returns:
661 The generated JSON schema.
662 """
663 json_schema = {'type': 'string'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
664 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
665 if isinstance(json_schema.get('pattern'), Pattern): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
666 # TODO: should we add regex flags to the pattern?
667 json_schema['pattern'] = json_schema.get('pattern').pattern # type: ignore 1yCtvadejkpwbEFzAfglmqrBDuxchinos
668 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
670 def bytes_schema(self, schema: core_schema.BytesSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
671 """Generates a JSON schema that matches a bytes value.
673 Args:
674 schema: The core schema.
676 Returns:
677 The generated JSON schema.
678 """
679 json_schema = {'type': 'string', 'format': 'base64url' if self._config.ser_json_bytes == 'base64' else 'binary'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
680 self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
681 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
683 def date_schema(self, schema: core_schema.DateSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
684 """Generates a JSON schema that matches a date value.
686 Args:
687 schema: The core schema.
689 Returns:
690 The generated JSON schema.
691 """
692 json_schema = {'type': 'string', 'format': 'date'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
693 self.update_with_validations(json_schema, schema, self.ValidationsMapping.date) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
694 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
696 def time_schema(self, schema: core_schema.TimeSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
697 """Generates a JSON schema that matches a time value.
699 Args:
700 schema: The core schema.
702 Returns:
703 The generated JSON schema.
704 """
705 return {'type': 'string', 'format': 'time'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
707 def datetime_schema(self, schema: core_schema.DatetimeSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
708 """Generates a JSON schema that matches a datetime value.
710 Args:
711 schema: The core schema.
713 Returns:
714 The generated JSON schema.
715 """
716 return {'type': 'string', 'format': 'date-time'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
718 def timedelta_schema(self, schema: core_schema.TimedeltaSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
719 """Generates a JSON schema that matches a timedelta value.
721 Args:
722 schema: The core schema.
724 Returns:
725 The generated JSON schema.
726 """
727 if self._config.ser_json_timedelta == 'float': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
728 return {'type': 'number'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
729 return {'type': 'string', 'format': 'duration'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
731 def literal_schema(self, schema: core_schema.LiteralSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
732 """Generates a JSON schema that matches a literal value.
734 Args:
735 schema: The core schema.
737 Returns:
738 The generated JSON schema.
739 """
740 expected = [v.value if isinstance(v, Enum) else v for v in schema['expected']] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
741 # jsonify the expected values
742 expected = [to_jsonable_python(v) for v in expected] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
744 result: dict[str, Any] = {'enum': expected} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
745 if len(expected) == 1: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
746 result['const'] = expected[0] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
748 types = {type(e) for e in expected} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
749 if types == {str}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
750 result['type'] = 'string' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
751 elif types == {int}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
752 result['type'] = 'integer' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
753 elif types == {float}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
754 result['type'] = 'numeric' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
755 elif types == {bool}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
756 result['type'] = 'boolean' 1tvadejkpwbzAfglmqruxchinos
757 elif types == {list}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
758 result['type'] = 'array' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
759 elif types == {type(None)}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
760 result['type'] = 'null' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
761 return result 1yCtvadejkpwbEFzAfglmqrBDuxchinos
763 def enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
764 """Generates a JSON schema that matches an Enum value.
766 Args:
767 schema: The core schema.
769 Returns:
770 The generated JSON schema.
771 """
772 enum_type = schema['cls'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
773 description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
774 if ( 1yCtvwbBDux
775 description == 'An enumeration.'
776 ): # This is the default value provided by enum.EnumMeta.__new__; don't use it
777 description = None 1yCtvadwbEFzAfgBDuxch
778 result: dict[str, Any] = {'title': enum_type.__name__, 'description': description} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
779 result = {k: v for k, v in result.items() if v is not None} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
781 expected = [to_jsonable_python(v.value) for v in schema['members']] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
783 result['enum'] = expected 1yCtvadejkpwbEFzAfglmqrBDuxchinos
784 if len(expected) == 1: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
785 result['const'] = expected[0] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
787 types = {type(e) for e in expected} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
788 if isinstance(enum_type, str) or types == {str}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
789 result['type'] = 'string' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
790 elif isinstance(enum_type, int) or types == {int}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
791 result['type'] = 'integer' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
792 elif isinstance(enum_type, float) or types == {float}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
793 result['type'] = 'numeric' 1tvadejkpwbzAfglmqruxchinos
794 elif types == {bool}: 794 ↛ 795line 794 didn't jump to line 795, because the condition on line 794 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
795 result['type'] = 'boolean'
796 elif types == {list}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
797 result['type'] = 'array' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
799 return result 1yCtvadejkpwbEFzAfglmqrBDuxchinos
801 def is_instance_schema(self, schema: core_schema.IsInstanceSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
802 """Handles JSON schema generation for a core schema that checks if a value is an instance of a class.
804 Unless overridden in a subclass, this raises an error.
806 Args:
807 schema: The core schema.
809 Returns:
810 The generated JSON schema.
811 """
812 return self.handle_invalid_for_json_schema(schema, f'core_schema.IsInstanceSchema ({schema["cls"]})') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
814 def is_subclass_schema(self, schema: core_schema.IsSubclassSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
815 """Handles JSON schema generation for a core schema that checks if a value is a subclass of a class.
817 For backwards compatibility with v1, this does not raise an error, but can be overridden to change this.
819 Args:
820 schema: The core schema.
822 Returns:
823 The generated JSON schema.
824 """
825 # Note: This is for compatibility with V1; you can override if you want different behavior.
826 return {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
828 def callable_schema(self, schema: core_schema.CallableSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
829 """Generates a JSON schema that matches a callable value.
831 Unless overridden in a subclass, this raises an error.
833 Args:
834 schema: The core schema.
836 Returns:
837 The generated JSON schema.
838 """
839 return self.handle_invalid_for_json_schema(schema, 'core_schema.CallableSchema') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
841 def list_schema(self, schema: core_schema.ListSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
842 """Returns a schema that matches a list schema.
844 Args:
845 schema: The core schema.
847 Returns:
848 The generated JSON schema.
849 """
850 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
851 json_schema = {'type': 'array', 'items': items_schema} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
852 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
853 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
855 @deprecated('`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', category=None) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
856 @final 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
857 def tuple_positional_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
858 """Replaced by `tuple_schema`."""
859 warnings.warn(
860 '`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.',
861 PydanticDeprecatedSince26,
862 stacklevel=2,
863 )
864 return self.tuple_schema(schema)
866 @deprecated('`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', category=None) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
867 @final 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
868 def tuple_variable_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
869 """Replaced by `tuple_schema`."""
870 warnings.warn(
871 '`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.',
872 PydanticDeprecatedSince26,
873 stacklevel=2,
874 )
875 return self.tuple_schema(schema)
877 def tuple_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
878 """Generates a JSON schema that matches a tuple schema e.g. `Tuple[int,
879 str, bool]` or `Tuple[int, ...]`.
881 Args:
882 schema: The core schema.
884 Returns:
885 The generated JSON schema.
886 """
887 json_schema: JsonSchemaValue = {'type': 'array'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
888 if 'variadic_item_index' in schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
889 variadic_item_index = schema['variadic_item_index'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
890 if variadic_item_index > 0: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
891 json_schema['minItems'] = variadic_item_index 1yCtvadejkpwbEFzAfglmqrBDuxchinos
892 json_schema['prefixItems'] = [ 1yCtvadejkpwbEFzAfglmqrBDuxchinos
893 self.generate_inner(item) for item in schema['items_schema'][:variadic_item_index]
894 ]
895 if variadic_item_index + 1 == len(schema['items_schema']): 895 ↛ 902line 895 didn't jump to line 902, because the condition on line 895 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
896 # if the variadic item is the last item, then represent it faithfully
897 json_schema['items'] = self.generate_inner(schema['items_schema'][variadic_item_index]) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
898 else:
899 # otherwise, 'items' represents the schema for the variadic
900 # item plus the suffix, so just allow anything for simplicity
901 # for now
902 json_schema['items'] = True
903 else:
904 prefixItems = [self.generate_inner(item) for item in schema['items_schema']] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
905 if prefixItems: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
906 json_schema['prefixItems'] = prefixItems 1yCtvadejkpwbEFzAfglmqrBDuxchinos
907 json_schema['minItems'] = len(prefixItems) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
908 json_schema['maxItems'] = len(prefixItems) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
909 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
910 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
912 def set_schema(self, schema: core_schema.SetSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
913 """Generates a JSON schema that matches a set schema.
915 Args:
916 schema: The core schema.
918 Returns:
919 The generated JSON schema.
920 """
921 return self._common_set_schema(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
923 def frozenset_schema(self, schema: core_schema.FrozenSetSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
924 """Generates a JSON schema that matches a frozenset schema.
926 Args:
927 schema: The core schema.
929 Returns:
930 The generated JSON schema.
931 """
932 return self._common_set_schema(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
934 def _common_set_schema(self, schema: core_schema.SetSchema | core_schema.FrozenSetSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
935 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
936 json_schema = {'type': 'array', 'uniqueItems': True, 'items': items_schema} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
937 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
938 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
940 def generator_schema(self, schema: core_schema.GeneratorSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
941 """Returns a JSON schema that represents the provided GeneratorSchema.
943 Args:
944 schema: The schema.
946 Returns:
947 The generated JSON schema.
948 """
949 items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
950 json_schema = {'type': 'array', 'items': items_schema} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
951 self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
952 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
954 def dict_schema(self, schema: core_schema.DictSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
955 """Generates a JSON schema that matches a dict schema.
957 Args:
958 schema: The core schema.
960 Returns:
961 The generated JSON schema.
962 """
963 json_schema: JsonSchemaValue = {'type': 'object'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
965 keys_schema = self.generate_inner(schema['keys_schema']).copy() if 'keys_schema' in schema else {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
966 keys_pattern = keys_schema.pop('pattern', None) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
968 values_schema = self.generate_inner(schema['values_schema']).copy() if 'values_schema' in schema else {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
969 values_schema.pop('title', None) # don't give a title to the additionalProperties 1yCtvadejkpwbEFzAfglmqrBDuxchinos
970 if values_schema or keys_pattern is not None: # don't add additionalProperties if it's empty 1yCtvadejkpwbEFzAfglmqrBDuxchinos
971 if keys_pattern is None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
972 json_schema['additionalProperties'] = values_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
973 else:
974 json_schema['patternProperties'] = {keys_pattern: values_schema} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
976 self.update_with_validations(json_schema, schema, self.ValidationsMapping.object) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
977 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
979 def _function_schema( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
980 self,
981 schema: _core_utils.AnyFunctionSchema,
982 ) -> JsonSchemaValue:
983 if _core_utils.is_function_with_inner_schema(schema): 983 ↛ 989line 983 didn't jump to line 989, because the condition on line 983 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
984 # This could be wrong if the function's mode is 'before', but in practice will often be right, and when it
985 # isn't, I think it would be hard to automatically infer what the desired schema should be.
986 return self.generate_inner(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
988 # function-plain
989 return self.handle_invalid_for_json_schema(
990 schema, f'core_schema.PlainValidatorFunctionSchema ({schema["function"]})'
991 )
993 def function_before_schema(self, schema: core_schema.BeforeValidatorFunctionSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
994 """Generates a JSON schema that matches a function-before schema.
996 Args:
997 schema: The core schema.
999 Returns:
1000 The generated JSON schema.
1001 """
1002 return self._function_schema(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1004 def function_after_schema(self, schema: core_schema.AfterValidatorFunctionSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1005 """Generates a JSON schema that matches a function-after schema.
1007 Args:
1008 schema: The core schema.
1010 Returns:
1011 The generated JSON schema.
1012 """
1013 return self._function_schema(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1015 def function_plain_schema(self, schema: core_schema.PlainValidatorFunctionSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1016 """Generates a JSON schema that matches a function-plain schema.
1018 Args:
1019 schema: The core schema.
1021 Returns:
1022 The generated JSON schema.
1023 """
1024 return self._function_schema(schema)
1026 def function_wrap_schema(self, schema: core_schema.WrapValidatorFunctionSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1027 """Generates a JSON schema that matches a function-wrap schema.
1029 Args:
1030 schema: The core schema.
1032 Returns:
1033 The generated JSON schema.
1034 """
1035 return self._function_schema(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1037 def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1038 """Generates a JSON schema that matches a schema with a default value.
1040 Args:
1041 schema: The core schema.
1043 Returns:
1044 The generated JSON schema.
1045 """
1046 json_schema = self.generate_inner(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1048 if 'default' not in schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1049 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1050 default = schema['default'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1051 # Note: if you want to include the value returned by the default_factory,
1052 # override this method and replace the code above with:
1053 # if 'default' in schema:
1054 # default = schema['default']
1055 # elif 'default_factory' in schema:
1056 # default = schema['default_factory']()
1057 # else:
1058 # return json_schema
1060 # we reflect the application of custom plain, no-info serializers to defaults for
1061 # json schemas viewed in serialization mode
1062 # TODO: improvements along with https://github.com/pydantic/pydantic/issues/8208
1063 # TODO: improve type safety here
1064 if self.mode == 'serialization': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1065 if ( 1yCtvadejwbEFzAfglmBDuxchin
1066 (ser_schema := schema['schema'].get('serialization', {}))
1067 and (ser_func := ser_schema.get('function'))
1068 and ser_schema.get('type') == 'function-plain' # type: ignore
1069 and ser_schema.get('info_arg') is False # type: ignore
1070 ):
1071 default = ser_func(default) # type: ignore 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1073 try: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1074 encoded_default = self.encode_default(default) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1075 except pydantic_core.PydanticSerializationError: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1076 self.emit_warning( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1077 'non-serializable-default',
1078 f'Default value {default} is not JSON serializable; excluding default from JSON schema',
1079 )
1080 # Return the inner schema, as though there was no default
1081 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1083 if '$ref' in json_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1084 # Since reference schemas do not support child keys, we wrap the reference schema in a single-case allOf:
1085 return {'allOf': [json_schema], 'default': encoded_default} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1086 else:
1087 json_schema['default'] = encoded_default 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1088 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1090 def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1091 """Generates a JSON schema that matches a schema that allows null values.
1093 Args:
1094 schema: The core schema.
1096 Returns:
1097 The generated JSON schema.
1098 """
1099 null_schema = {'type': 'null'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1100 inner_json_schema = self.generate_inner(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1102 if inner_json_schema == null_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1103 return null_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1104 else:
1105 # Thanks to the equality check against `null_schema` above, I think 'oneOf' would also be valid here;
1106 # I'll use 'anyOf' for now, but it could be changed it if it would work better with some external tooling
1107 return self.get_flattened_anyof([inner_json_schema, null_schema]) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1109 def union_schema(self, schema: core_schema.UnionSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1110 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas.
1112 Args:
1113 schema: The core schema.
1115 Returns:
1116 The generated JSON schema.
1117 """
1118 generated: list[JsonSchemaValue] = [] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1120 choices = schema['choices'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1121 for choice in choices: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1122 # choice will be a tuple if an explicit label was provided
1123 choice_schema = choice[0] if isinstance(choice, tuple) else choice 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1124 try: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1125 generated.append(self.generate_inner(choice_schema)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1126 except PydanticOmit: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1127 continue 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1128 except PydanticInvalidForJsonSchema as exc: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1129 self.emit_warning('skipped-choice', exc.message) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1130 if len(generated) == 1: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1131 return generated[0] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1132 return self.get_flattened_anyof(generated) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1134 def tagged_union_schema(self, schema: core_schema.TaggedUnionSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1135 """Generates a JSON schema that matches a schema that allows values matching any of the given schemas, where
1136 the schemas are tagged with a discriminator field that indicates which schema should be used to validate
1137 the value.
1139 Args:
1140 schema: The core schema.
1142 Returns:
1143 The generated JSON schema.
1144 """
1145 generated: dict[str, JsonSchemaValue] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1146 for k, v in schema['choices'].items(): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1147 if isinstance(k, Enum): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1148 k = k.value 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1149 try: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1150 # Use str(k) since keys must be strings for json; while not technically correct,
1151 # it's the closest that can be represented in valid JSON
1152 generated[str(k)] = self.generate_inner(v).copy() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1153 except PydanticOmit:
1154 continue
1155 except PydanticInvalidForJsonSchema as exc:
1156 self.emit_warning('skipped-choice', exc.message)
1158 one_of_choices = _deduplicate_schemas(generated.values()) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1159 json_schema: JsonSchemaValue = {'oneOf': one_of_choices} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1161 # This reflects the v1 behavior; TODO: we should make it possible to exclude OpenAPI stuff from the JSON schema
1162 openapi_discriminator = self._extract_discriminator(schema, one_of_choices) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1163 if openapi_discriminator is not None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1164 json_schema['discriminator'] = { 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1165 'propertyName': openapi_discriminator,
1166 'mapping': {k: v.get('$ref', v) for k, v in generated.items()},
1167 }
1169 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1171 def _extract_discriminator( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1172 self, schema: core_schema.TaggedUnionSchema, one_of_choices: list[JsonDict]
1173 ) -> str | None:
1174 """Extract a compatible OpenAPI discriminator from the schema and one_of choices that end up in the final
1175 schema."""
1176 openapi_discriminator: str | None = None 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1178 if isinstance(schema['discriminator'], str): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1179 return schema['discriminator'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1181 if isinstance(schema['discriminator'], list): 1181 ↛ 1209line 1181 didn't jump to line 1209, because the condition on line 1181 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1182 # If the discriminator is a single item list containing a string, that is equivalent to the string case
1183 if len(schema['discriminator']) == 1 and isinstance(schema['discriminator'][0], str): 1183 ↛ 1184line 1183 didn't jump to line 1184, because the condition on line 1183 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1184 return schema['discriminator'][0]
1185 # When an alias is used that is different from the field name, the discriminator will be a list of single
1186 # str lists, one for the attribute and one for the actual alias. The logic here will work even if there is
1187 # more than one possible attribute, and looks for whether a single alias choice is present as a documented
1188 # property on all choices. If so, that property will be used as the OpenAPI discriminator.
1189 for alias_path in schema['discriminator']: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1190 if not isinstance(alias_path, list): 1190 ↛ 1191line 1190 didn't jump to line 1191, because the condition on line 1190 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1191 break # this means that the discriminator is not a list of alias paths
1192 if len(alias_path) != 1: 1192 ↛ 1193line 1192 didn't jump to line 1193, because the condition on line 1192 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1193 continue # this means that the "alias" does not represent a single field
1194 alias = alias_path[0] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1195 if not isinstance(alias, str): 1195 ↛ 1196line 1195 didn't jump to line 1196, because the condition on line 1195 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1196 continue # this means that the "alias" does not represent a field
1197 alias_is_present_on_all_choices = True 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1198 for choice in one_of_choices: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1199 while '$ref' in choice: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1200 assert isinstance(choice['$ref'], str) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1201 choice = self.get_schema_from_definitions(JsonRef(choice['$ref'])) or {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1202 properties = choice.get('properties', {}) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1203 if not isinstance(properties, dict) or alias not in properties: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1204 alias_is_present_on_all_choices = False 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1205 break 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1206 if alias_is_present_on_all_choices: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1207 openapi_discriminator = alias 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1208 break 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1209 return openapi_discriminator 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1211 def chain_schema(self, schema: core_schema.ChainSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1212 """Generates a JSON schema that matches a core_schema.ChainSchema.
1214 When generating a schema for validation, we return the validation JSON schema for the first step in the chain.
1215 For serialization, we return the serialization JSON schema for the last step in the chain.
1217 Args:
1218 schema: The core schema.
1220 Returns:
1221 The generated JSON schema.
1222 """
1223 step_index = 0 if self.mode == 'validation' else -1 # use first step for validation, last for serialization 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1224 return self.generate_inner(schema['steps'][step_index]) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1226 def lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1227 """Generates a JSON schema that matches a schema that allows values matching either the lax schema or the
1228 strict schema.
1230 Args:
1231 schema: The core schema.
1233 Returns:
1234 The generated JSON schema.
1235 """
1236 # TODO: Need to read the default value off of model config or whatever
1237 use_strict = schema.get('strict', False) # TODO: replace this default False 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1238 # If your JSON schema fails to generate it is probably
1239 # because one of the following two branches failed.
1240 if use_strict: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1241 return self.generate_inner(schema['strict_schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1242 else:
1243 return self.generate_inner(schema['lax_schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1245 def json_or_python_schema(self, schema: core_schema.JsonOrPythonSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1246 """Generates a JSON schema that matches a schema that allows values matching either the JSON schema or the
1247 Python schema.
1249 The JSON schema is used instead of the Python schema. If you want to use the Python schema, you should override
1250 this method.
1252 Args:
1253 schema: The core schema.
1255 Returns:
1256 The generated JSON schema.
1257 """
1258 return self.generate_inner(schema['json_schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1260 def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1261 """Generates a JSON schema that matches a schema that defines a typed dict.
1263 Args:
1264 schema: The core schema.
1266 Returns:
1267 The generated JSON schema.
1268 """
1269 total = schema.get('total', True) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1270 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1271 (name, self.field_is_required(field, total), field)
1272 for name, field in schema['fields'].items()
1273 if self.field_is_present(field)
1274 ]
1275 if self.mode == 'serialization': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1276 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1277 cls = _get_typed_dict_cls(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1278 config = _get_typed_dict_config(cls) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1279 with self._config_wrapper_stack.push(config): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1280 json_schema = self._named_required_fields_schema(named_required_fields) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1282 json_schema_extra = config.get('json_schema_extra') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1283 extra = schema.get('extra_behavior') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1284 if extra is None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1285 extra = config.get('extra', 'ignore') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1287 if cls is not None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1288 title = config.get('title') or cls.__name__ 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1289 json_schema = self._update_class_schema(json_schema, title, extra, cls, json_schema_extra) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1290 else:
1291 if extra == 'forbid': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1292 json_schema['additionalProperties'] = False 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1293 elif extra == 'allow': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1294 json_schema['additionalProperties'] = True 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1296 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1298 @staticmethod 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1299 def _name_required_computed_fields( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1300 computed_fields: list[ComputedField],
1301 ) -> list[tuple[str, bool, core_schema.ComputedField]]:
1302 return [(field['property_name'], True, field) for field in computed_fields] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1304 def _named_required_fields_schema( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1305 self, named_required_fields: Sequence[tuple[str, bool, CoreSchemaField]]
1306 ) -> JsonSchemaValue:
1307 properties: dict[str, JsonSchemaValue] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1308 required_fields: list[str] = [] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1309 for name, required, field in named_required_fields: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1310 if self.by_alias: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1311 name = self._get_alias_name(field, name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1312 try: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1313 field_json_schema = self.generate_inner(field).copy() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1314 except PydanticOmit: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1315 continue 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1316 if 'title' not in field_json_schema and self.field_title_should_be_set(field): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1317 title = self.get_title_from_name(name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1318 field_json_schema['title'] = title 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1319 field_json_schema = self.handle_ref_overrides(field_json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1320 properties[name] = field_json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1321 if required: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1322 required_fields.append(name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1324 json_schema = {'type': 'object', 'properties': properties} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1325 if required_fields: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1326 json_schema['required'] = required_fields 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1327 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1329 def _get_alias_name(self, field: CoreSchemaField, name: str) -> str: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1330 if field['type'] == 'computed-field': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1331 alias: Any = field.get('alias', name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1332 elif self.mode == 'validation': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1333 alias = field.get('validation_alias', name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1334 else:
1335 alias = field.get('serialization_alias', name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1336 if isinstance(alias, str): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1337 name = alias 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1338 elif isinstance(alias, list): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1339 alias = cast('list[str] | str', alias) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1340 for path in alias: 1340 ↛ 1348line 1340 didn't jump to line 1348, because the loop on line 1340 didn't complete1yCtvadejkpwbEFzAfglmqrBDuxchinos
1341 if isinstance(path, list) and len(path) == 1 and isinstance(path[0], str): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1342 # Use the first valid single-item string path; the code that constructs the alias array
1343 # should ensure the first such item is what belongs in the JSON schema
1344 name = path[0] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1345 break 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1346 else:
1347 assert_never(alias)
1348 return name 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1350 def typed_dict_field_schema(self, schema: core_schema.TypedDictField) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1351 """Generates a JSON schema that matches a schema that defines a typed dict field.
1353 Args:
1354 schema: The core schema.
1356 Returns:
1357 The generated JSON schema.
1358 """
1359 return self.generate_inner(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1361 def dataclass_field_schema(self, schema: core_schema.DataclassField) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1362 """Generates a JSON schema that matches a schema that defines a dataclass field.
1364 Args:
1365 schema: The core schema.
1367 Returns:
1368 The generated JSON schema.
1369 """
1370 return self.generate_inner(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1372 def model_field_schema(self, schema: core_schema.ModelField) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1373 """Generates a JSON schema that matches a schema that defines a model field.
1375 Args:
1376 schema: The core schema.
1378 Returns:
1379 The generated JSON schema.
1380 """
1381 return self.generate_inner(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1383 def computed_field_schema(self, schema: core_schema.ComputedField) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1384 """Generates a JSON schema that matches a schema that defines a computed field.
1386 Args:
1387 schema: The core schema.
1389 Returns:
1390 The generated JSON schema.
1391 """
1392 return self.generate_inner(schema['return_schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1394 def model_schema(self, schema: core_schema.ModelSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1395 """Generates a JSON schema that matches a schema that defines a model.
1397 Args:
1398 schema: The core schema.
1400 Returns:
1401 The generated JSON schema.
1402 """
1403 # We do not use schema['model'].model_json_schema() here
1404 # because it could lead to inconsistent refs handling, etc.
1405 cls = cast('type[BaseModel]', schema['cls']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1406 config = cls.model_config 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1407 title = config.get('title') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1409 with self._config_wrapper_stack.push(config): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1410 json_schema = self.generate_inner(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1412 json_schema_extra = config.get('json_schema_extra') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1413 if cls.__pydantic_root_model__: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1414 root_json_schema_extra = cls.model_fields['root'].json_schema_extra 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1415 if json_schema_extra and root_json_schema_extra: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1416 raise ValueError( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1417 '"model_config[\'json_schema_extra\']" and "Field.json_schema_extra" on "RootModel.root"'
1418 ' field must not be set simultaneously'
1419 )
1420 if root_json_schema_extra: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1421 json_schema_extra = root_json_schema_extra 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1423 json_schema = self._update_class_schema(json_schema, title, config.get('extra', None), cls, json_schema_extra) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1425 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1427 def _update_class_schema( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1428 self,
1429 json_schema: JsonSchemaValue,
1430 title: str | None,
1431 extra: Literal['allow', 'ignore', 'forbid'] | None,
1432 cls: type[Any],
1433 json_schema_extra: JsonDict | JsonSchemaExtraCallable | None,
1434 ) -> JsonSchemaValue:
1435 if '$ref' in json_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1436 schema_to_update = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) or json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1437 else:
1438 schema_to_update = json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1440 if title is not None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1441 # referenced_schema['title'] = title
1442 schema_to_update.setdefault('title', title) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1444 if 'additionalProperties' not in schema_to_update: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1445 if extra == 'allow': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1446 schema_to_update['additionalProperties'] = True 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1447 elif extra == 'forbid': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1448 schema_to_update['additionalProperties'] = False 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1450 if isinstance(json_schema_extra, (staticmethod, classmethod)): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1451 # In older versions of python, this is necessary to ensure staticmethod/classmethods are callable
1452 json_schema_extra = json_schema_extra.__get__(cls) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1454 if isinstance(json_schema_extra, dict): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1455 schema_to_update.update(json_schema_extra) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1456 elif callable(json_schema_extra): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1457 if len(inspect.signature(json_schema_extra).parameters) > 1: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1458 json_schema_extra(schema_to_update, cls) # type: ignore 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1459 else:
1460 json_schema_extra(schema_to_update) # type: ignore 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1461 elif json_schema_extra is not None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1462 raise ValueError( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1463 f"model_config['json_schema_extra']={json_schema_extra} should be a dict, callable, or None"
1464 )
1466 if hasattr(cls, '__deprecated__'): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1467 json_schema['deprecated'] = True 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1469 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1471 def resolve_schema_to_update(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1472 """Resolve a JsonSchemaValue to the non-ref schema if it is a $ref schema.
1474 Args:
1475 json_schema: The schema to resolve.
1477 Returns:
1478 The resolved schema.
1479 """
1480 if '$ref' in json_schema: 1480 ↛ 1481line 1480 didn't jump to line 1481, because the condition on line 1480 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1481 schema_to_update = self.get_schema_from_definitions(JsonRef(json_schema['$ref']))
1482 if schema_to_update is None:
1483 raise RuntimeError(f'Cannot update undefined schema for $ref={json_schema["$ref"]}')
1484 return self.resolve_schema_to_update(schema_to_update)
1485 else:
1486 schema_to_update = json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1487 return schema_to_update 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1489 def model_fields_schema(self, schema: core_schema.ModelFieldsSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1490 """Generates a JSON schema that matches a schema that defines a model's fields.
1492 Args:
1493 schema: The core schema.
1495 Returns:
1496 The generated JSON schema.
1497 """
1498 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1499 (name, self.field_is_required(field, total=True), field)
1500 for name, field in schema['fields'].items()
1501 if self.field_is_present(field)
1502 ]
1503 if self.mode == 'serialization': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1504 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1505 json_schema = self._named_required_fields_schema(named_required_fields) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1506 extras_schema = schema.get('extras_schema', None) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1507 if extras_schema is not None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1508 schema_to_update = self.resolve_schema_to_update(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1509 schema_to_update['additionalProperties'] = self.generate_inner(extras_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1510 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1512 def field_is_present(self, field: CoreSchemaField) -> bool: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1513 """Whether the field should be included in the generated JSON schema.
1515 Args:
1516 field: The schema for the field itself.
1518 Returns:
1519 `True` if the field should be included in the generated JSON schema, `False` otherwise.
1520 """
1521 if self.mode == 'serialization': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1522 # If you still want to include the field in the generated JSON schema,
1523 # override this method and return True
1524 return not field.get('serialization_exclude') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1525 elif self.mode == 'validation': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1526 return True 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1527 else:
1528 assert_never(self.mode)
1530 def field_is_required( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1531 self,
1532 field: core_schema.ModelField | core_schema.DataclassField | core_schema.TypedDictField,
1533 total: bool,
1534 ) -> bool:
1535 """Whether the field should be marked as required in the generated JSON schema.
1536 (Note that this is irrelevant if the field is not present in the JSON schema.).
1538 Args:
1539 field: The schema for the field itself.
1540 total: Only applies to `TypedDictField`s.
1541 Indicates if the `TypedDict` this field belongs to is total, in which case any fields that don't
1542 explicitly specify `required=False` are required.
1544 Returns:
1545 `True` if the field should be marked as required in the generated JSON schema, `False` otherwise.
1546 """
1547 if self.mode == 'serialization' and self._config.json_schema_serialization_defaults_required: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1548 return not field.get('serialization_exclude') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1549 else:
1550 if field['type'] == 'typed-dict-field': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1551 return field.get('required', total) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1552 else:
1553 return field['schema']['type'] != 'default' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1555 def dataclass_args_schema(self, schema: core_schema.DataclassArgsSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1556 """Generates a JSON schema that matches a schema that defines a dataclass's constructor arguments.
1558 Args:
1559 schema: The core schema.
1561 Returns:
1562 The generated JSON schema.
1563 """
1564 named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1565 (field['name'], self.field_is_required(field, total=True), field)
1566 for field in schema['fields']
1567 if self.field_is_present(field)
1568 ]
1569 if self.mode == 'serialization': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1570 named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1571 return self._named_required_fields_schema(named_required_fields) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1573 def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1574 """Generates a JSON schema that matches a schema that defines a dataclass.
1576 Args:
1577 schema: The core schema.
1579 Returns:
1580 The generated JSON schema.
1581 """
1582 cls = schema['cls'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1583 config: ConfigDict = getattr(cls, '__pydantic_config__', cast('ConfigDict', {})) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1584 title = config.get('title') or cls.__name__ 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1586 with self._config_wrapper_stack.push(config): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1587 json_schema = self.generate_inner(schema['schema']).copy() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1589 json_schema_extra = config.get('json_schema_extra') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1590 json_schema = self._update_class_schema(json_schema, title, config.get('extra', None), cls, json_schema_extra) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1592 # Dataclass-specific handling of description
1593 if is_dataclass(cls) and not hasattr(cls, '__pydantic_validator__'): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1594 # vanilla dataclass; don't use cls.__doc__ as it will contain the class signature by default
1595 description = None 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1596 else:
1597 description = None if cls.__doc__ is None else inspect.cleandoc(cls.__doc__) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1598 if description: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1599 json_schema['description'] = description 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1601 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1603 def arguments_schema(self, schema: core_schema.ArgumentsSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1604 """Generates a JSON schema that matches a schema that defines a function's arguments.
1606 Args:
1607 schema: The core schema.
1609 Returns:
1610 The generated JSON schema.
1611 """
1612 metadata = _core_metadata.CoreMetadataHandler(schema).metadata 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1613 prefer_positional = metadata.get('pydantic_js_prefer_positional_arguments') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1615 arguments = schema['arguments_schema'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1616 kw_only_arguments = [a for a in arguments if a.get('mode') == 'keyword_only'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1617 kw_or_p_arguments = [a for a in arguments if a.get('mode') in {'positional_or_keyword', None}] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1618 p_only_arguments = [a for a in arguments if a.get('mode') == 'positional_only'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1619 var_args_schema = schema.get('var_args_schema') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1620 var_kwargs_schema = schema.get('var_kwargs_schema') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1622 if prefer_positional: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1623 positional_possible = not kw_only_arguments and not var_kwargs_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1624 if positional_possible: 1624 ↛ 1627line 1624 didn't jump to line 1627, because the condition on line 1624 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1625 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1627 keyword_possible = not p_only_arguments and not var_args_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1628 if keyword_possible: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1629 return self.kw_arguments_schema(kw_or_p_arguments + kw_only_arguments, var_kwargs_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1631 if not prefer_positional: 1631 ↛ 1636line 1631 didn't jump to line 1636, because the condition on line 1631 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1632 positional_possible = not kw_only_arguments and not var_kwargs_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1633 if positional_possible: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1634 return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1636 raise PydanticInvalidForJsonSchema( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1637 'Unable to generate JSON schema for arguments validator with positional-only and keyword-only arguments'
1638 )
1640 def kw_arguments_schema( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1641 self, arguments: list[core_schema.ArgumentsParameter], var_kwargs_schema: CoreSchema | None
1642 ) -> JsonSchemaValue:
1643 """Generates a JSON schema that matches a schema that defines a function's keyword arguments.
1645 Args:
1646 arguments: The core schema.
1648 Returns:
1649 The generated JSON schema.
1650 """
1651 properties: dict[str, JsonSchemaValue] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1652 required: list[str] = [] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1653 for argument in arguments: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1654 name = self.get_argument_name(argument) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1655 argument_schema = self.generate_inner(argument['schema']).copy() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1656 argument_schema['title'] = self.get_title_from_name(name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1657 properties[name] = argument_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1659 if argument['schema']['type'] != 'default': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1660 # This assumes that if the argument has a default value,
1661 # the inner schema must be of type WithDefaultSchema.
1662 # I believe this is true, but I am not 100% sure
1663 required.append(name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1665 json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1666 if required: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1667 json_schema['required'] = required 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1669 if var_kwargs_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1670 additional_properties_schema = self.generate_inner(var_kwargs_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1671 if additional_properties_schema: 1671 ↛ 1675line 1671 didn't jump to line 1675, because the condition on line 1671 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1672 json_schema['additionalProperties'] = additional_properties_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1673 else:
1674 json_schema['additionalProperties'] = False 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1675 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1677 def p_arguments_schema( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1678 self, arguments: list[core_schema.ArgumentsParameter], var_args_schema: CoreSchema | None
1679 ) -> JsonSchemaValue:
1680 """Generates a JSON schema that matches a schema that defines a function's positional arguments.
1682 Args:
1683 arguments: The core schema.
1685 Returns:
1686 The generated JSON schema.
1687 """
1688 prefix_items: list[JsonSchemaValue] = [] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1689 min_items = 0 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1691 for argument in arguments: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1692 name = self.get_argument_name(argument) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1694 argument_schema = self.generate_inner(argument['schema']).copy() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1695 argument_schema['title'] = self.get_title_from_name(name) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1696 prefix_items.append(argument_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1698 if argument['schema']['type'] != 'default': 1698 ↛ 1691line 1698 didn't jump to line 1691, because the condition on line 1698 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1699 # This assumes that if the argument has a default value,
1700 # the inner schema must be of type WithDefaultSchema.
1701 # I believe this is true, but I am not 100% sure
1702 min_items += 1 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1704 json_schema: JsonSchemaValue = {'type': 'array', 'prefixItems': prefix_items} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1705 if min_items: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1706 json_schema['minItems'] = min_items 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1708 if var_args_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1709 items_schema = self.generate_inner(var_args_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1710 if items_schema: 1710 ↛ 1715line 1710 didn't jump to line 1715, because the condition on line 1710 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1711 json_schema['items'] = items_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1712 else:
1713 json_schema['maxItems'] = len(prefix_items) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1715 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1717 def get_argument_name(self, argument: core_schema.ArgumentsParameter) -> str: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1718 """Retrieves the name of an argument.
1720 Args:
1721 argument: The core schema.
1723 Returns:
1724 The name of the argument.
1725 """
1726 name = argument['name'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1727 if self.by_alias: 1727 ↛ 1733line 1727 didn't jump to line 1733, because the condition on line 1727 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1728 alias = argument.get('alias') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1729 if isinstance(alias, str): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1730 name = alias 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1731 else:
1732 pass # might want to do something else? 1adejkpbfglmqrchinos
1733 return name 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1735 def call_schema(self, schema: core_schema.CallSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1736 """Generates a JSON schema that matches a schema that defines a function call.
1738 Args:
1739 schema: The core schema.
1741 Returns:
1742 The generated JSON schema.
1743 """
1744 return self.generate_inner(schema['arguments_schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1746 def custom_error_schema(self, schema: core_schema.CustomErrorSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1747 """Generates a JSON schema that matches a schema that defines a custom error.
1749 Args:
1750 schema: The core schema.
1752 Returns:
1753 The generated JSON schema.
1754 """
1755 return self.generate_inner(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1757 def json_schema(self, schema: core_schema.JsonSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1758 """Generates a JSON schema that matches a schema that defines a JSON object.
1760 Args:
1761 schema: The core schema.
1763 Returns:
1764 The generated JSON schema.
1765 """
1766 content_core_schema = schema.get('schema') or core_schema.any_schema() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1767 content_json_schema = self.generate_inner(content_core_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1768 if self.mode == 'validation': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1769 return {'type': 'string', 'contentMediaType': 'application/json', 'contentSchema': content_json_schema} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1770 else:
1771 # self.mode == 'serialization'
1772 return content_json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1774 def url_schema(self, schema: core_schema.UrlSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1775 """Generates a JSON schema that matches a schema that defines a URL.
1777 Args:
1778 schema: The core schema.
1780 Returns:
1781 The generated JSON schema.
1782 """
1783 json_schema = {'type': 'string', 'format': 'uri', 'minLength': 1} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1784 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1785 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1787 def multi_host_url_schema(self, schema: core_schema.MultiHostUrlSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1788 """Generates a JSON schema that matches a schema that defines a URL that can be used with multiple hosts.
1790 Args:
1791 schema: The core schema.
1793 Returns:
1794 The generated JSON schema.
1795 """
1796 # Note: 'multi-host-uri' is a custom/pydantic-specific format, not part of the JSON Schema spec
1797 json_schema = {'type': 'string', 'format': 'multi-host-uri', 'minLength': 1} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1798 self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1799 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1801 def uuid_schema(self, schema: core_schema.UuidSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1802 """Generates a JSON schema that matches a UUID.
1804 Args:
1805 schema: The core schema.
1807 Returns:
1808 The generated JSON schema.
1809 """
1810 return {'type': 'string', 'format': 'uuid'} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1812 def definitions_schema(self, schema: core_schema.DefinitionsSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1813 """Generates a JSON schema that matches a schema that defines a JSON object with definitions.
1815 Args:
1816 schema: The core schema.
1818 Returns:
1819 The generated JSON schema.
1820 """
1821 for definition in schema['definitions']: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1822 try: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1823 self.generate_inner(definition) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1824 except PydanticInvalidForJsonSchema as e:
1825 core_ref: CoreRef = CoreRef(definition['ref']) # type: ignore
1826 self._core_defs_invalid_for_json_schema[self.get_defs_ref((core_ref, self.mode))] = e
1827 continue
1828 return self.generate_inner(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1830 def definition_ref_schema(self, schema: core_schema.DefinitionReferenceSchema) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1831 """Generates a JSON schema that matches a schema that references a definition.
1833 Args:
1834 schema: The core schema.
1836 Returns:
1837 The generated JSON schema.
1838 """
1839 core_ref = CoreRef(schema['schema_ref']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1840 _, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1841 return ref_json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1843 def ser_schema( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1844 self, schema: core_schema.SerSchema | core_schema.IncExSeqSerSchema | core_schema.IncExDictSerSchema
1845 ) -> JsonSchemaValue | None:
1846 """Generates a JSON schema that matches a schema that defines a serialized object.
1848 Args:
1849 schema: The core schema.
1851 Returns:
1852 The generated JSON schema.
1853 """
1854 schema_type = schema['type'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1855 if schema_type == 'function-plain' or schema_type == 'function-wrap': 1855 ↛ 1860line 1855 didn't jump to line 1860, because the condition on line 1855 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1856 # PlainSerializerFunctionSerSchema or WrapSerializerFunctionSerSchema
1857 return_schema = schema.get('return_schema') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1858 if return_schema is not None: 1858 ↛ 1866line 1858 didn't jump to line 1866, because the condition on line 1858 was always true1yCtvadejkpwbEFzAfglmqrBDuxchinos
1859 return self.generate_inner(return_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1860 elif schema_type == 'format' or schema_type == 'to-string':
1861 # FormatSerSchema or ToStringSerSchema
1862 return self.str_schema(core_schema.str_schema())
1863 elif schema['type'] == 'model':
1864 # ModelSerSchema
1865 return self.generate_inner(schema['schema'])
1866 return None
1868 # ### Utility methods
1870 def get_title_from_name(self, name: str) -> str: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1871 """Retrieves a title from a name.
1873 Args:
1874 name: The name to retrieve a title from.
1876 Returns:
1877 The title.
1878 """
1879 return name.title().replace('_', ' ') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1881 def field_title_should_be_set(self, schema: CoreSchemaOrField) -> bool: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1882 """Returns true if a field with the given schema should have a title set based on the field name.
1884 Intuitively, we want this to return true for schemas that wouldn't otherwise provide their own title
1885 (e.g., int, float, str), and false for those that would (e.g., BaseModel subclasses).
1887 Args:
1888 schema: The schema to check.
1890 Returns:
1891 `True` if the field should have a title set, `False` otherwise.
1892 """
1893 if _core_utils.is_core_schema_field(schema): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1894 if schema['type'] == 'computed-field': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1895 field_schema = schema['return_schema'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1896 else:
1897 field_schema = schema['schema'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1898 return self.field_title_should_be_set(field_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1900 elif _core_utils.is_core_schema(schema): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1901 if schema.get('ref'): # things with refs, such as models and enums, should not have titles set 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1902 return False 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1903 if schema['type'] in {'default', 'nullable', 'definitions'}: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1904 return self.field_title_should_be_set(schema['schema']) # type: ignore[typeddict-item] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1905 if _core_utils.is_function_with_inner_schema(schema): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1906 return self.field_title_should_be_set(schema['schema']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1907 if schema['type'] == 'definition-ref': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1908 # Referenced schemas should not have titles set for the same reason
1909 # schemas with refs should not
1910 return False 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1911 return True # anything else should have title set 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1913 else:
1914 raise PydanticInvalidForJsonSchema(f'Unexpected schema type: schema={schema}') # pragma: no cover
1916 def normalize_name(self, name: str) -> str: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1917 """Normalizes a name to be used as a key in a dictionary.
1919 Args:
1920 name: The name to normalize.
1922 Returns:
1923 The normalized name.
1924 """
1925 return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name).replace('.', '__') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1927 def get_defs_ref(self, core_mode_ref: CoreModeRef) -> DefsRef: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1928 """Override this method to change the way that definitions keys are generated from a core reference.
1930 Args:
1931 core_mode_ref: The core reference.
1933 Returns:
1934 The definitions key.
1935 """
1936 # Split the core ref into "components"; generic origins and arguments are each separate components
1937 core_ref, mode = core_mode_ref 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1938 components = re.split(r'([\][,])', core_ref) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1939 # Remove IDs from each component
1940 components = [x.rsplit(':', 1)[0] for x in components] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1941 core_ref_no_id = ''.join(components) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1942 # Remove everything before the last period from each "component"
1943 components = [re.sub(r'(?:[^.[\]]+\.)+((?:[^.[\]]+))', r'\1', x) for x in components] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1944 short_ref = ''.join(components) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1946 mode_title = _MODE_TITLE_MAPPING[mode] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1948 # It is important that the generated defs_ref values be such that at least one choice will not
1949 # be generated for any other core_ref. Currently, this should be the case because we include
1950 # the id of the source type in the core_ref
1951 name = DefsRef(self.normalize_name(short_ref)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1952 name_mode = DefsRef(self.normalize_name(short_ref) + f'-{mode_title}') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1953 module_qualname = DefsRef(self.normalize_name(core_ref_no_id)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1954 module_qualname_mode = DefsRef(f'{module_qualname}-{mode_title}') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1955 module_qualname_id = DefsRef(self.normalize_name(core_ref)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1956 occurrence_index = self._collision_index.get(module_qualname_id) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1957 if occurrence_index is None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1958 self._collision_counter[module_qualname] += 1 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1959 occurrence_index = self._collision_index[module_qualname_id] = self._collision_counter[module_qualname] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1961 module_qualname_occurrence = DefsRef(f'{module_qualname}__{occurrence_index}') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1962 module_qualname_occurrence_mode = DefsRef(f'{module_qualname_mode}__{occurrence_index}') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1964 self._prioritized_defsref_choices[module_qualname_occurrence_mode] = [ 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1965 name,
1966 name_mode,
1967 module_qualname,
1968 module_qualname_mode,
1969 module_qualname_occurrence,
1970 module_qualname_occurrence_mode,
1971 ]
1973 return module_qualname_occurrence_mode 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1975 def get_cache_defs_ref_schema(self, core_ref: CoreRef) -> tuple[DefsRef, JsonSchemaValue]: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
1976 """This method wraps the get_defs_ref method with some cache-lookup/population logic,
1977 and returns both the produced defs_ref and the JSON schema that will refer to the right definition.
1979 Args:
1980 core_ref: The core reference to get the definitions reference for.
1982 Returns:
1983 A tuple of the definitions reference and the JSON schema that will refer to it.
1984 """
1985 core_mode_ref = (core_ref, self.mode) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1986 maybe_defs_ref = self.core_to_defs_refs.get(core_mode_ref) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1987 if maybe_defs_ref is not None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1988 json_ref = self.core_to_json_refs[core_mode_ref] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1989 return maybe_defs_ref, {'$ref': json_ref} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1991 defs_ref = self.get_defs_ref(core_mode_ref) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1993 # populate the ref translation mappings
1994 self.core_to_defs_refs[core_mode_ref] = defs_ref 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1995 self.defs_to_core_refs[defs_ref] = core_mode_ref 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1997 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1998 self.core_to_json_refs[core_mode_ref] = json_ref 1yCtvadejkpwbEFzAfglmqrBDuxchinos
1999 self.json_to_defs_refs[json_ref] = defs_ref 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2000 ref_json_schema = {'$ref': json_ref} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2001 return defs_ref, ref_json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2003 def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2004 """It is not valid for a schema with a top-level $ref to have sibling keys.
2006 During our own schema generation, we treat sibling keys as overrides to the referenced schema,
2007 but this is not how the official JSON schema spec works.
2009 Because of this, we first remove any sibling keys that are redundant with the referenced schema, then if
2010 any remain, we transform the schema from a top-level '$ref' to use allOf to move the $ref out of the top level.
2011 (See bottom of https://swagger.io/docs/specification/using-ref/ for a reference about this behavior)
2012 """
2013 if '$ref' in json_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2014 # prevent modifications to the input; this copy may be safe to drop if there is significant overhead
2015 json_schema = json_schema.copy() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2017 referenced_json_schema = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2018 if referenced_json_schema is None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2019 # This can happen when building schemas for models with not-yet-defined references.
2020 # It may be a good idea to do a recursive pass at the end of the generation to remove
2021 # any redundant override keys.
2022 if len(json_schema) > 1: 2022 ↛ 2024line 2022 didn't jump to line 2024, because the condition on line 2022 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
2023 # Make it an allOf to at least resolve the sibling keys issue
2024 json_schema = json_schema.copy()
2025 json_schema.setdefault('allOf', [])
2026 json_schema['allOf'].append({'$ref': json_schema['$ref']})
2027 del json_schema['$ref']
2029 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2030 for k, v in list(json_schema.items()): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2031 if k == '$ref': 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2032 continue 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2033 if k in referenced_json_schema and referenced_json_schema[k] == v: 2033 ↛ 2034line 2033 didn't jump to line 2034, because the condition on line 2033 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
2034 del json_schema[k] # redundant key
2035 if len(json_schema) > 1: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2036 # There is a remaining "override" key, so we need to move $ref out of the top level
2037 json_ref = JsonRef(json_schema['$ref']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2038 del json_schema['$ref'] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2039 assert 'allOf' not in json_schema # this should never happen, but just in case 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2040 json_schema['allOf'] = [{'$ref': json_ref}] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2042 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2044 def get_schema_from_definitions(self, json_ref: JsonRef) -> JsonSchemaValue | None: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2045 def_ref = self.json_to_defs_refs[json_ref] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2046 if def_ref in self._core_defs_invalid_for_json_schema: 2046 ↛ 2047line 2046 didn't jump to line 2047, because the condition on line 2046 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
2047 raise self._core_defs_invalid_for_json_schema[def_ref]
2048 return self.definitions.get(def_ref, None) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2050 def encode_default(self, dft: Any) -> Any: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2051 """Encode a default value to a JSON-serializable value.
2053 This is used to encode default values for fields in the generated JSON schema.
2055 Args:
2056 dft: The default value to encode.
2058 Returns:
2059 The encoded default value.
2060 """
2061 from .type_adapter import TypeAdapter, _type_has_config 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2063 config = self._config 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2064 try: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2065 default = ( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2066 dft
2067 if _type_has_config(type(dft))
2068 else TypeAdapter(type(dft), config=config.config_dict).dump_python(dft, mode='json')
2069 )
2070 except PydanticSchemaGenerationError: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2071 raise pydantic_core.PydanticSerializationError(f'Unable to encode default value {dft}') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2073 return pydantic_core.to_jsonable_python( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2074 default,
2075 timedelta_mode=config.ser_json_timedelta,
2076 bytes_mode=config.ser_json_bytes,
2077 )
2079 def update_with_validations( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2080 self, json_schema: JsonSchemaValue, core_schema: CoreSchema, mapping: dict[str, str]
2081 ) -> None:
2082 """Update the json_schema with the corresponding validations specified in the core_schema,
2083 using the provided mapping to translate keys in core_schema to the appropriate keys for a JSON schema.
2085 Args:
2086 json_schema: The JSON schema to update.
2087 core_schema: The core schema to get the validations from.
2088 mapping: A mapping from core_schema attribute names to the corresponding JSON schema attribute names.
2089 """
2090 for core_key, json_schema_key in mapping.items(): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2091 if core_key in core_schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2092 json_schema[json_schema_key] = core_schema[core_key] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2094 class ValidationsMapping: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2095 """This class just contains mappings from core_schema attribute names to the corresponding
2096 JSON schema attribute names. While I suspect it is unlikely to be necessary, you can in
2097 principle override this class in a subclass of GenerateJsonSchema (by inheriting from
2098 GenerateJsonSchema.ValidationsMapping) to change these mappings.
2099 """
2101 numeric = { 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2102 'multiple_of': 'multipleOf',
2103 'le': 'maximum',
2104 'ge': 'minimum',
2105 'lt': 'exclusiveMaximum',
2106 'gt': 'exclusiveMinimum',
2107 }
2108 bytes = { 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2109 'min_length': 'minLength',
2110 'max_length': 'maxLength',
2111 }
2112 string = { 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2113 'min_length': 'minLength',
2114 'max_length': 'maxLength',
2115 'pattern': 'pattern',
2116 }
2117 array = { 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2118 'min_length': 'minItems',
2119 'max_length': 'maxItems',
2120 }
2121 object = { 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2122 'min_length': 'minProperties',
2123 'max_length': 'maxProperties',
2124 }
2125 date = { 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2126 'le': 'maximum',
2127 'ge': 'minimum',
2128 'lt': 'exclusiveMaximum',
2129 'gt': 'exclusiveMinimum',
2130 }
2132 def get_flattened_anyof(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2133 members = [] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2134 for schema in schemas: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2135 if len(schema) == 1 and 'anyOf' in schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2136 members.extend(schema['anyOf']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2137 else:
2138 members.append(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2139 members = _deduplicate_schemas(members) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2140 if len(members) == 1: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2141 return members[0] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2142 return {'anyOf': members} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2144 def get_json_ref_counts(self, json_schema: JsonSchemaValue) -> dict[JsonRef, int]: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2145 """Get all values corresponding to the key '$ref' anywhere in the json_schema."""
2146 json_refs: dict[JsonRef, int] = Counter() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2148 def _add_json_refs(schema: Any) -> None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2149 if isinstance(schema, dict): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2150 if '$ref' in schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2151 json_ref = JsonRef(schema['$ref']) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2152 if not isinstance(json_ref, str): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2153 return # in this case, '$ref' might have been the name of a property 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2154 already_visited = json_ref in json_refs 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2155 json_refs[json_ref] += 1 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2156 if already_visited: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2157 return # prevent recursion on a definition that was already visited 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2158 defs_ref = self.json_to_defs_refs[json_ref] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2159 if defs_ref in self._core_defs_invalid_for_json_schema: 2159 ↛ 2160line 2159 didn't jump to line 2160, because the condition on line 2159 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
2160 raise self._core_defs_invalid_for_json_schema[defs_ref]
2161 _add_json_refs(self.definitions[defs_ref]) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2163 for v in schema.values(): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2164 _add_json_refs(v) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2165 elif isinstance(schema, list): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2166 for v in schema: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2167 _add_json_refs(v) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2169 _add_json_refs(json_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2170 return json_refs 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2172 def handle_invalid_for_json_schema(self, schema: CoreSchemaOrField, error_info: str) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2173 raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2175 def emit_warning(self, kind: JsonSchemaWarningKind, detail: str) -> None: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2176 """This method simply emits PydanticJsonSchemaWarnings based on handling in the `warning_message` method."""
2177 message = self.render_warning_message(kind, detail) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2178 if message is not None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2179 warnings.warn(message, PydanticJsonSchemaWarning) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2181 def render_warning_message(self, kind: JsonSchemaWarningKind, detail: str) -> str | None: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2182 """This method is responsible for ignoring warnings as desired, and for formatting the warning messages.
2184 You can override the value of `ignored_warning_kinds` in a subclass of GenerateJsonSchema
2185 to modify what warnings are generated. If you want more control, you can override this method;
2186 just return None in situations where you don't want warnings to be emitted.
2188 Args:
2189 kind: The kind of warning to render. It can be one of the following:
2191 - 'skipped-choice': A choice field was skipped because it had no valid choices.
2192 - 'non-serializable-default': A default value was skipped because it was not JSON-serializable.
2193 detail: A string with additional details about the warning.
2195 Returns:
2196 The formatted warning message, or `None` if no warning should be emitted.
2197 """
2198 if kind in self.ignored_warning_kinds: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2199 return None 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2200 return f'{detail} [{kind}]' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2202 def _build_definitions_remapping(self) -> _DefinitionsRemapping: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2203 defs_to_json: dict[DefsRef, JsonRef] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2204 for defs_refs in self._prioritized_defsref_choices.values(): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2205 for defs_ref in defs_refs: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2206 json_ref = JsonRef(self.ref_template.format(model=defs_ref)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2207 defs_to_json[defs_ref] = json_ref 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2209 return _DefinitionsRemapping.from_prioritized_choices( 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2210 self._prioritized_defsref_choices, defs_to_json, self.definitions
2211 )
2213 def _garbage_collect_definitions(self, schema: JsonSchemaValue) -> None: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2214 visited_defs_refs: set[DefsRef] = set() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2215 unvisited_json_refs = _get_all_json_refs(schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2216 while unvisited_json_refs: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2217 next_json_ref = unvisited_json_refs.pop() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2218 next_defs_ref = self.json_to_defs_refs[next_json_ref] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2219 if next_defs_ref in visited_defs_refs: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2220 continue 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2221 visited_defs_refs.add(next_defs_ref) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2222 unvisited_json_refs.update(_get_all_json_refs(self.definitions[next_defs_ref])) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2224 self.definitions = {k: v for k, v in self.definitions.items() if k in visited_defs_refs} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2227# ##### Start JSON Schema Generation Functions #####
2230def model_json_schema( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2231 cls: type[BaseModel] | type[PydanticDataclass],
2232 by_alias: bool = True,
2233 ref_template: str = DEFAULT_REF_TEMPLATE,
2234 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
2235 mode: JsonSchemaMode = 'validation',
2236) -> dict[str, Any]:
2237 """Utility function to generate a JSON Schema for a model.
2239 Args:
2240 cls: The model class to generate a JSON Schema for.
2241 by_alias: If `True` (the default), fields will be serialized according to their alias.
2242 If `False`, fields will be serialized according to their attribute name.
2243 ref_template: The template to use for generating JSON Schema references.
2244 schema_generator: The class to use for generating the JSON Schema.
2245 mode: The mode to use for generating the JSON Schema. It can be one of the following:
2247 - 'validation': Generate a JSON Schema for validating data.
2248 - 'serialization': Generate a JSON Schema for serializing data.
2250 Returns:
2251 The generated JSON Schema.
2252 """
2253 from .main import BaseModel 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2255 schema_generator_instance = schema_generator(by_alias=by_alias, ref_template=ref_template) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2257 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2258 cls.__pydantic_core_schema__.rebuild() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2260 if cls is BaseModel: 2260 ↛ 2261line 2260 didn't jump to line 2261, because the condition on line 2260 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
2261 raise AttributeError('model_json_schema() must be called on a subclass of BaseModel, not BaseModel itself.')
2263 assert not isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it' 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2264 return schema_generator_instance.generate(cls.__pydantic_core_schema__, mode=mode) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2267def models_json_schema( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2268 models: Sequence[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode]],
2269 *,
2270 by_alias: bool = True,
2271 title: str | None = None,
2272 description: str | None = None,
2273 ref_template: str = DEFAULT_REF_TEMPLATE,
2274 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
2275) -> tuple[dict[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]:
2276 """Utility function to generate a JSON Schema for multiple models.
2278 Args:
2279 models: A sequence of tuples of the form (model, mode).
2280 by_alias: Whether field aliases should be used as keys in the generated JSON Schema.
2281 title: The title of the generated JSON Schema.
2282 description: The description of the generated JSON Schema.
2283 ref_template: The reference template to use for generating JSON Schema references.
2284 schema_generator: The schema generator to use for generating the JSON Schema.
2286 Returns:
2287 A tuple where:
2288 - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
2289 whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
2290 JsonRef references to definitions that are defined in the second returned element.)
2291 - The second element is a JSON schema containing all definitions referenced in the first returned
2292 element, along with the optional title and description keys.
2293 """
2294 for cls, _ in models: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2295 if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): 2295 ↛ 2296line 2295 didn't jump to line 2296, because the condition on line 2295 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
2296 cls.__pydantic_core_schema__.rebuild()
2298 instance = schema_generator(by_alias=by_alias, ref_template=ref_template) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2299 inputs: list[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode, CoreSchema]] = [ 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2300 (m, mode, m.__pydantic_core_schema__) for m, mode in models
2301 ]
2302 json_schemas_map, definitions = instance.generate_definitions(inputs) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2304 json_schema: dict[str, Any] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2305 if definitions: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2306 json_schema['$defs'] = definitions 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2307 if title: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2308 json_schema['title'] = title 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2309 if description: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2310 json_schema['description'] = description 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2312 return json_schemas_map, json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2315# ##### End JSON Schema Generation Functions #####
2318_HashableJsonValue: TypeAlias = Union[ 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2319 int, float, str, bool, None, Tuple['_HashableJsonValue', ...], Tuple[Tuple[str, '_HashableJsonValue'], ...]
2320]
2323def _deduplicate_schemas(schemas: Iterable[JsonDict]) -> list[JsonDict]: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2324 return list({_make_json_hashable(schema): schema for schema in schemas}.values()) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2327def _make_json_hashable(value: JsonValue) -> _HashableJsonValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2328 if isinstance(value, dict): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2329 return tuple(sorted((k, _make_json_hashable(v)) for k, v in value.items())) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2330 elif isinstance(value, list): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2331 return tuple(_make_json_hashable(v) for v in value) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2332 else:
2333 return value 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2336def _sort_json_schema(value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2337 if isinstance(value, dict): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2338 sorted_dict: dict[str, JsonSchemaValue] = {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2339 keys = value.keys() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2340 if (parent_key != 'properties') and (parent_key != 'default'): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2341 keys = sorted(keys) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2342 for key in keys: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2343 sorted_dict[key] = _sort_json_schema(value[key], parent_key=key) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2344 return sorted_dict 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2345 elif isinstance(value, list): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2346 sorted_list: list[JsonSchemaValue] = [] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2347 for item in value: # type: ignore 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2348 sorted_list.append(_sort_json_schema(item, parent_key)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2349 return sorted_list # type: ignore 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2350 else:
2351 return value 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2354@dataclasses.dataclass(**_internal_dataclass.slots_true) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2355class WithJsonSchema: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2356 """Usage docs: https://docs.pydantic.dev/2.8/concepts/json_schema/#withjsonschema-annotation
2358 Add this as an annotation on a field to override the (base) JSON schema that would be generated for that field.
2359 This provides a way to set a JSON schema for types that would otherwise raise errors when producing a JSON schema,
2360 such as Callable, or types that have an is-instance core schema, without needing to go so far as creating a
2361 custom subclass of pydantic.json_schema.GenerateJsonSchema.
2362 Note that any _modifications_ to the schema that would normally be made (such as setting the title for model fields)
2363 will still be performed.
2365 If `mode` is set this will only apply to that schema generation mode, allowing you
2366 to set different json schemas for validation and serialization.
2367 """
2369 json_schema: JsonSchemaValue | None 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2370 mode: Literal['validation', 'serialization'] | None = None 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2372 def __get_pydantic_json_schema__( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2373 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2374 ) -> JsonSchemaValue:
2375 mode = self.mode or handler.mode 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2376 if mode != handler.mode: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2377 return handler(core_schema) 1ytaekBucio
2378 if self.json_schema is None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2379 # This exception is handled in pydantic.json_schema.GenerateJsonSchema._named_required_fields_schema
2380 raise PydanticOmit 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2381 else:
2382 return self.json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2384 def __hash__(self) -> int: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2385 return hash(type(self.mode)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2388@dataclasses.dataclass(**_internal_dataclass.slots_true) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2389class Examples: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2390 """Add examples to a JSON schema.
2392 Examples should be a map of example names (strings)
2393 to example values (any valid JSON).
2395 If `mode` is set this will only apply to that schema generation mode,
2396 allowing you to add different examples for validation and serialization.
2397 """
2399 examples: dict[str, Any] 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2400 mode: Literal['validation', 'serialization'] | None = None 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2402 def __get_pydantic_json_schema__( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2403 self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
2404 ) -> JsonSchemaValue:
2405 mode = self.mode or handler.mode 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2406 json_schema = handler(core_schema) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2407 if mode != handler.mode: 2407 ↛ 2408line 2407 didn't jump to line 2408, because the condition on line 2407 was never true1yCtvadejkpwbEFzAfglmqrBDuxchinos
2408 return json_schema
2409 examples = json_schema.get('examples', {}) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2410 examples.update(to_jsonable_python(self.examples)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2411 json_schema['examples'] = examples 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2412 return json_schema 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2414 def __hash__(self) -> int: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2415 return hash(type(self.mode)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2418def _get_all_json_refs(item: Any) -> set[JsonRef]: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2419 """Get all the definitions references from a JSON schema."""
2420 refs: set[JsonRef] = set() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2421 stack = [item] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2423 while stack: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2424 current = stack.pop() 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2425 if isinstance(current, dict): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2426 for key, value in current.items(): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2427 if key == '$ref' and isinstance(value, str): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2428 refs.add(JsonRef(value)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2429 elif isinstance(value, dict): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2430 stack.append(value) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2431 elif isinstance(value, list): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2432 stack.extend(value) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2433 elif isinstance(current, list): 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2434 stack.extend(current) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2436 return refs 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2439AnyType = TypeVar('AnyType') 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2441if TYPE_CHECKING: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2442 SkipJsonSchema = Annotated[AnyType, ...]
2443else:
2445 @dataclasses.dataclass(**_internal_dataclass.slots_true) 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2446 class SkipJsonSchema: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2447 """Usage docs: https://docs.pydantic.dev/2.8/concepts/json_schema/#skipjsonschema-annotation
2449 Add this as an annotation on a field to skip generating a JSON schema for that field.
2451 Example:
2452 ```py
2453 from typing import Union
2455 from pydantic import BaseModel
2456 from pydantic.json_schema import SkipJsonSchema
2458 from pprint import pprint
2461 class Model(BaseModel):
2462 a: Union[int, None] = None # (1)!
2463 b: Union[int, SkipJsonSchema[None]] = None # (2)!
2464 c: SkipJsonSchema[Union[int, None]] = None # (3)!
2467 pprint(Model.model_json_schema())
2468 '''
2469 {
2470 'properties': {
2471 'a': {
2472 'anyOf': [
2473 {'type': 'integer'},
2474 {'type': 'null'}
2475 ],
2476 'default': None,
2477 'title': 'A'
2478 },
2479 'b': {
2480 'default': None,
2481 'title': 'B',
2482 'type': 'integer'
2483 }
2484 },
2485 'title': 'Model',
2486 'type': 'object'
2487 }
2488 '''
2489 ```
2491 1. The integer and null types are both included in the schema for `a`.
2492 2. The integer type is the only type included in the schema for `b`.
2493 3. The entirety of the `c` field is omitted from the schema.
2494 """
2496 def __class_getitem__(cls, item: AnyType) -> AnyType: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2497 return Annotated[item, cls()] 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2499 def __get_pydantic_json_schema__( 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2500 self, core_schema: CoreSchema, handler: GetJsonSchemaHandler
2501 ) -> JsonSchemaValue:
2502 raise PydanticOmit 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2504 def __hash__(self) -> int: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2505 return hash(type(self)) 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2508def _get_typed_dict_cls(schema: core_schema.TypedDictSchema) -> type[Any] | None: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2509 metadata = _core_metadata.CoreMetadataHandler(schema).metadata 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2510 cls = metadata.get('pydantic_typed_dict_cls') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2511 return cls 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2514def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict: 1yCtvadejkpwbEFzAfglmqrNOGHIJKLMBDuxchinos
2515 if cls is not None: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2516 try: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2517 return _decorators.get_attribute_from_bases(cls, '__pydantic_config__') 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2518 except AttributeError: 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2519 pass 1yCtvadejkpwbEFzAfglmqrBDuxchinos
2520 return {} 1yCtvadejkpwbEFzAfglmqrBDuxchinos