Skip to content

Commit 0356b51

Browse files
committed
nullable implementation based on clarified OAS 3.0.3 definition
1 parent e213079 commit 0356b51

File tree

3 files changed

+35
-37
lines changed

3 files changed

+35
-37
lines changed

openapi_schema_validator/_validators.py

+8-35
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,6 @@
1818
from jsonschema.protocols import Validator
1919

2020

21-
def include_nullable_validator(
22-
schema: Dict[Hashable, Any]
23-
) -> ItemsView[Hashable, Any]:
24-
"""
25-
Include ``nullable`` validator always.
26-
Suitable for use with `create`'s ``applicable_validators`` argument.
27-
"""
28-
_schema = deepcopy(schema)
29-
30-
# append defaults to trigger nullable validator, except where $ref in the schema checks null
31-
if "nullable" not in _schema \
32-
and "$ref" not in _schema \
33-
and "oneOf" not in _schema \
34-
and "anyOf" not in _schema \
35-
and "allOf" not in _schema:
36-
_schema.update(
37-
{
38-
"nullable": False,
39-
}
40-
)
41-
42-
return _schema.items()
43-
44-
4521
def handle_discriminator(
4622
validator: Validator, _: Any, instance: Any, schema: Mapping[Hashable, Any]
4723
) -> Iterator[ValidationError]:
@@ -131,7 +107,14 @@ def type(
131107
schema: Mapping[Hashable, Any],
132108
) -> Iterator[ValidationError]:
133109
if instance is None:
134-
return
110+
# nullable implementation based on OAS 3.0.3l
111+
# * nullable is only meaningful if its value is true
112+
# * nullable: true is only meaningful in combination with a type
113+
# assertion specified in the same Schema Object.
114+
# * nullable: true operates within a single Schema Object
115+
if "nullable" in schema and schema["nullable"] == True:
116+
return
117+
yield ValidationError("None for not nullable")
135118

136119
if not validator.is_type(instance, data_type):
137120
data_repr = repr(data_type)
@@ -167,16 +150,6 @@ def items(
167150
yield from validator.descend(item, items, path=index)
168151

169152

170-
def nullable(
171-
validator: Validator,
172-
is_nullable: bool,
173-
instance: Any,
174-
schema: Mapping[Hashable, Any],
175-
) -> Iterator[ValidationError]:
176-
if instance is None and not is_nullable:
177-
yield ValidationError("None for not nullable")
178-
179-
180153
def required(
181154
validator: Validator,
182155
required: List[str],

openapi_schema_validator/validators.py

-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
# TODO: adjust default
4646
"$ref": _validators.ref,
4747
# fixed OAS fields
48-
"nullable": oas_validators.nullable,
4948
"discriminator": oas_validators.not_implemented,
5049
"readOnly": oas_validators.readOnly,
5150
"writeOnly": oas_validators.writeOnly,
@@ -59,7 +58,6 @@
5958
# See https://github.com/p1c2u/openapi-schema-validator/pull/12
6059
# version="oas30",
6160
id_of=lambda schema: schema.get("id", ""),
62-
applicable_validators=oas_validators.include_nullable_validator,
6361
)
6462

6563
OAS31Validator = extend(

tests/integration/test_validators.py

+27
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ def test_null(self, schema_type):
3131
with pytest.raises(ValidationError):
3232
validator.validate(value)
3333

34+
@pytest.mark.parametrize("is_nullable", [True, False])
35+
def test_nullable_untyped(self, is_nullable):
36+
schema = {"nullable": is_nullable}
37+
validator = OAS30Validator(schema)
38+
value = None
39+
40+
result = validator.validate(value)
41+
42+
assert result is None
43+
3444
@pytest.mark.parametrize(
3545
"schema_type",
3646
[
@@ -50,6 +60,23 @@ def test_nullable(self, schema_type):
5060

5161
assert result is None
5262

63+
def test_nullable_enum_without_none(self):
64+
schema = {"type": "integer", "nullable": True, "enum": [1, 2, 3]}
65+
validator = OAS30Validator(schema)
66+
value = None
67+
68+
with pytest.raises(ValidationError):
69+
validator.validate(value)
70+
71+
def test_nullable_enum_with_none(self):
72+
schema = {"type": "integer", "nullable": True, "enum": [1, 2, 3, None]}
73+
validator = OAS30Validator(schema)
74+
value = None
75+
76+
result = validator.validate(value)
77+
78+
assert result is None
79+
5380
@pytest.mark.parametrize(
5481
"value",
5582
[

0 commit comments

Comments
 (0)