Skip to content

JSON schema for typed_dict_schema ignores extra when there is no cls #12123

@DouweM

Description

@DouweM

TypedDictSchema has extras_schema and extra_behavior fields, as well as config: CoreConfig with an extra_fields_behavior field, but these are never read in JsonSchemaGenerator.typed_dict_schema. This may make sense if the schema has a cls, but it's valid to create one without, in which case the local config var will be an empty dict, and the extra = config.get('extra') stuff is a no-op.

JsonSchemaGenerator.typed_dict_schema:

def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue:
"""Generates a JSON schema that matches a schema that defines a typed dict.
Args:
schema: The core schema.
Returns:
The generated JSON schema.
"""
total = schema.get('total', True)
named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [
(name, self.field_is_required(field, total), field)
for name, field in schema['fields'].items()
if self.field_is_present(field)
]
if self.mode == 'serialization':
named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', [])))
cls = schema.get('cls')
config = _get_typed_dict_config(cls)
with self._config_wrapper_stack.push(config):
json_schema = self._named_required_fields_schema(named_required_fields)
if cls is not None:
self._update_class_schema(json_schema, cls, config)
else:
extra = config.get('extra')
if extra == 'forbid':
json_schema['additionalProperties'] = False
elif extra == 'allow':
json_schema['additionalProperties'] = True
return json_schema

I ran into this in pydantic/pydantic-ai#2419, where we create a typed_dict_schema like so:

core_config = config_wrapper.core_config(None)
core_config['extra_fields_behavior'] = 'allow' if var_kwargs_schema else 'forbid' 
# ...
td_schema = core_schema.typed_dict_schema(
    fields,
    config=core_config,
    extras_schema=gen_schema.generate_schema(var_kwargs_schema) if var_kwargs_schema else None,
)

I worked around this by using our own generator:

class GenerateToolJsonSchema(GenerateJsonSchema):
    def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue:
        json_schema = super().typed_dict_schema(schema)
        if 'additionalProperties' not in json_schema:
            extra = schema.get('extra_behavior') or schema.get('config', {}).get('extra_fields_behavior')
            if extra == 'allow':
                extras_schema = schema.get('extras_schema', None)
                if extras_schema is not None:
                    json_schema['additionalProperties'] = self.generate_inner(extras_schema) or True
                else:
                    json_schema['additionalProperties'] = True
            elif extra == 'forbid':
                json_schema['additionalProperties'] = False
        return json_schema

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug V2Bug related to Pydantic V2

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions