diff --git a/schema/__init__.py b/schema/__init__.py index 10d6e8a..0bc6f6d 100644 --- a/schema/__init__.py +++ b/schema/__init__.py @@ -701,7 +701,16 @@ def _to_schema(s: Any, ignore_extra_keys: bool) -> Schema: if schema.name and not title: return_schema["title"] = schema.name - if flavor == TYPE: + if isinstance(s, Schema): + # A nested Schema instance (e.g. Const, or Schema(Schema(...))) + # carries its own constraints; expand it recursively instead of + # dropping it to an empty (match-anything) schema. + return_schema.update( + _json_schema( + s, is_main_schema=False, allow_reference=allow_reference + ) + ) + elif flavor == TYPE: # Handle type return_schema["type"] = _get_type_name(s) elif flavor == ITERABLE: diff --git a/test_schema.py b/test_schema.py index 4d78456..1b84fae 100644 --- a/test_schema.py +++ b/test_schema.py @@ -1009,6 +1009,38 @@ def test_json_schema_nested_schema(): } +def test_json_schema_const_and_nested_schema(): + # Regression: a Const (or any Schema nested directly inside another Schema) + # wrapping a literal/type was dropped to an empty {} schema, which accepts + # anything and contradicts what Schema.validate() enforces. The generated + # JSON schema must preserve the constraint. + assert Schema(Const("fixed")).json_schema("my-id") == { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "my-id", + "const": "fixed", + } + assert Schema(Const(int)).json_schema("my-id") == { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "my-id", + "type": "integer", + } + # A Schema nested directly inside another Schema must expand, not vanish. + assert Schema(Schema(int)).json_schema("my-id") == { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "my-id", + "type": "integer", + } + # Const used as a dict value keeps its const in the property schema. + assert Schema({"role": Const("admin")}).json_schema("my-id") == { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "my-id", + "type": "object", + "properties": {"role": {"const": "admin"}}, + "required": ["role"], + "additionalProperties": False, + } + + def test_json_schema_optional_key(): s = Schema({Optional("test"): str}) assert s.json_schema("my-id") == {