Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -696,25 +696,35 @@ Implemented

``{'type': 'object', 'properties': {'test': {'type': 'string'}}, 'required': [], 'additionalProperties': False}``

additionalProperties is set to true when at least one of the conditions is met:
- ignore_extra_keys is True
- at least one key is `str` or `object`
additionalProperties is set to True when ignore_extra_keys is True.

additionalProperties is set to a schema when the following conditions are met:
- ignore_extra_keys is False
- only one key is `str`, `Optional(str)` or `Literal(str)`

For example:

``Schema({str: str})`` and ``Schema({}, ignore_extra_keys=True)``
``Schema({}, ignore_extra_keys=True)``

both becomes
becomes

``{'type': 'object', 'properties' : {}, 'required': [], 'additionalProperties': True}``
``{'type': 'object', 'properties': {}, 'required': [], 'additionalProperties': True}``

and

``Schema({str: int})``

becomes

``{'type': 'object', 'properties': {}, 'required': [], 'additionalProperties': {'type': 'integer'}}``

while

``Schema({})``

becomes

``{'type': 'object', 'properties' : {}, 'required': [], 'additionalProperties': False}``
``{'type': 'object', 'properties': {}, 'required': [], 'additionalProperties': False}``

Types
Use the Python type name directly. It will be converted to the JSON name:
Expand Down Expand Up @@ -834,7 +844,6 @@ The following JSON schema validations cannot be generated from this library.
- `Combining schemas with oneOf <https://json-schema.org/understanding-json-schema/reference/combining.html#oneof>`_
- `Not <https://json-schema.org/understanding-json-schema/reference/combining.html#not>`_
- `Object size <https://json-schema.org/understanding-json-schema/reference/object.html#size>`_
- `additionalProperties having a different schema (true and false is supported)`


JSON: Minimizing output size
Expand Down
56 changes: 55 additions & 1 deletion schema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,19 @@ def _to_schema(s: Any, ignore_extra_keys: bool) -> Schema:
)

return_schema["$ref"] = "#/definitions/" + cast(str, schema.name)

# NOTE: Any application parsing the draft-07 dialect must ignore any other properties when $ref is present.
# See: https://json-schema.org/draft-07/draft-handrews-json-schema-01#rfc.section.8.3
# Starting with draft-2019, applications *may* allow overriding the referenced properties in these cases.
# See: https://json-schema.org/draft/2019-09/draft-handrews-json-schema-02#rfc.section.7.7.1.1

# Remove description key when the referenced description is the same
if (
return_description
and definitions_by_name[schema.name].get("description")
== return_description
):
del return_schema["description"]
else:
if schema.name and not title:
return_schema["title"] = schema.name
Expand Down Expand Up @@ -768,6 +781,7 @@ def _to_schema(s: Any, ignore_extra_keys: bool) -> Schema:
required_keys = []
expanded_schema = {}
additional_properties = i
pattern_properties = {}
for key in s:
if isinstance(key, Hook):
continue
Expand All @@ -777,7 +791,10 @@ def _key_allows_additional_properties(key: Any) -> bool:
if isinstance(key, Optional):
return _key_allows_additional_properties(key.schema)

return key == str or key == object
if isinstance(key, Literal):
return _key_allows_additional_properties(key.schema)

return key == str

def _get_key_title(key: Any) -> Union[str, None]:
"""Get the title associated to a key (as specified in a Literal object). Return None if not a Literal"""
Expand Down Expand Up @@ -836,11 +853,45 @@ def _get_key_name(key: Any) -> Any:
# This is less strict because we cannot enforce that one or the other is required

for or_key in key_name.args:
if isinstance(or_key, Regex):
or_key_name = re.sub(
r"\(\?P<[a-z\d_]+>", "(", or_key.pattern_str
).replace("/", r"\/")
pattern_properties[or_key_name] = _json_schema(
sub_schema,
is_main_schema=False,
description=_get_key_description(or_key),
)
continue
expanded_schema[_get_key_name(or_key)] = _json_schema(
sub_schema,
is_main_schema=False,
description=_get_key_description(or_key),
)
elif isinstance(key_name, Regex):
key_name = re.sub(
r"\(\?P<[a-z\d_]+>", "(", key_name.pattern_str
).replace("/", r"\/")
pattern_properties[key_name] = _json_schema(
sub_schema,
is_main_schema=False,
description=_get_key_description(key),
)
elif _key_allows_additional_properties(key):
if i:
# Don't generate sub-schema when extra keys are already ignored
continue
if isinstance(additional_properties, dict):
raise TypeError(
"For JSON schema generation only one key can be str."
)

additional_properties = _json_schema(
sub_schema,
is_main_schema=False,
title=_get_key_title(key),
description=_get_key_description(key),
)

return_schema.update(
{
Expand All @@ -851,6 +902,9 @@ def _get_key_name(key: Any) -> Any:
}
)

if len(pattern_properties) > 0:
return_schema["patternProperties"] = pattern_properties

if is_main_schema:
return_schema.update(
{
Expand Down
Loading