Skip to content

Commit 15c0ab8

Browse files
authored
Relax strict checking foreign properties (#90)
* Separate strictness of checking foreign properties from strict checking of primary properties. * Bump version. * Fix early exit check. * Tweak foreign property warning. * follow two spaces before a comment rule in this case the comments are type definitions
1 parent 3a7cf29 commit 15c0ab8

File tree

2 files changed

+52
-30
lines changed

2 files changed

+52
-30
lines changed

schema_salad/validate.py

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import sys
55
import urlparse
66
import re
7+
import logging
8+
79
from typing import Any, Union
810
from .sourceline import SourceLine, lineno_re, bullets, indent
911

12+
_logger = logging.getLogger("salad")
1013

1114
class ValidationException(Exception):
1215
pass
@@ -54,12 +57,14 @@ def vpformat(datum): # type: (Any) -> str
5457
return a
5558

5659

57-
def validate_ex(expected_schema, # type: Schema
58-
datum, # type: Any
59-
identifiers=None, # type: Set[unicode]
60-
strict=False, # type: bool
61-
foreign_properties=None, # type: Set[unicode]
62-
raise_ex=True # type: bool
60+
def validate_ex(expected_schema, # type: Schema
61+
datum, # type: Any
62+
identifiers=None, # type: Set[unicode]
63+
strict=False, # type: bool
64+
foreign_properties=None, # type: Set[unicode]
65+
raise_ex=True, # type: bool
66+
strict_foreign_properties=False, # type: bool
67+
logger=_logger # type: logging.Logger
6368
):
6469
# type: (...) -> bool
6570
"""Determine if a python datum is an instance of a schema."""
@@ -167,9 +172,11 @@ def validate_ex(expected_schema, # type: Schema
167172
for i, d in enumerate(datum):
168173
try:
169174
sl = SourceLine(datum, i, ValidationException)
170-
if not validate_ex(expected_schema.items, d, identifiers, strict=strict,
175+
if not validate_ex(expected_schema.items, d, identifiers,
176+
strict=strict,
171177
foreign_properties=foreign_properties,
172-
raise_ex=raise_ex):
178+
raise_ex=raise_ex,
179+
strict_foreign_properties=strict_foreign_properties):
173180
return False
174181
except ValidationException as v:
175182
if raise_ex:
@@ -186,7 +193,8 @@ def validate_ex(expected_schema, # type: Schema
186193
return False
187194
elif isinstance(expected_schema, avro.schema.UnionSchema):
188195
for s in expected_schema.schemas:
189-
if validate_ex(s, datum, identifiers, strict=strict, raise_ex=False):
196+
if validate_ex(s, datum, identifiers, strict=strict, raise_ex=False,
197+
strict_foreign_properties=strict_foreign_properties):
190198
return True
191199

192200
if not raise_ex:
@@ -207,7 +215,9 @@ def validate_ex(expected_schema, # type: Schema
207215
checked.append(s)
208216
try:
209217
validate_ex(s, datum, identifiers, strict=strict,
210-
foreign_properties=foreign_properties, raise_ex=True)
218+
foreign_properties=foreign_properties,
219+
raise_ex=True,
220+
strict_foreign_properties=strict_foreign_properties)
211221
except ClassValidationException as e:
212222
raise
213223
except ValidationException as e:
@@ -256,8 +266,10 @@ def validate_ex(expected_schema, # type: Schema
256266

257267
try:
258268
sl = SourceLine(datum, f.name, unicode)
259-
if not validate_ex(f.type, fieldval, identifiers, strict=strict, foreign_properties=foreign_properties,
260-
raise_ex=raise_ex):
269+
if not validate_ex(f.type, fieldval, identifiers, strict=strict,
270+
foreign_properties=foreign_properties,
271+
raise_ex=raise_ex,
272+
strict_foreign_properties=strict_foreign_properties):
261273
return False
262274
except ValidationException as v:
263275
if f.name not in datum:
@@ -266,24 +278,34 @@ def validate_ex(expected_schema, # type: Schema
266278
errors.append(sl.makeError(u"the `%s` field is not valid because\n%s" % (
267279
f.name, indent(str(v)))))
268280

269-
if strict:
270-
for d in datum:
271-
found = False
272-
for f in expected_schema.fields:
273-
if d == f.name:
274-
found = True
275-
if not found:
276-
sl = SourceLine(datum, d, unicode)
277-
if d not in identifiers and d not in foreign_properties and d[0] not in ("@", "$"):
278-
if not raise_ex:
279-
return False
280-
split = urlparse.urlsplit(d)
281-
if split.scheme:
282-
errors.append(sl.makeError(
283-
u"unrecognized extension field `%s` and strict is True. Did you include a $schemas section?" % (d)))
281+
for d in datum:
282+
found = False
283+
for f in expected_schema.fields:
284+
if d == f.name:
285+
found = True
286+
if not found:
287+
sl = SourceLine(datum, d, unicode)
288+
if d not in identifiers and d not in foreign_properties and d[0] not in ("@", "$"):
289+
if (d not in identifiers and strict) and (
290+
d not in foreign_properties and strict_foreign_properties) and not raise_ex:
291+
return False
292+
split = urlparse.urlsplit(d)
293+
if split.scheme:
294+
err = sl.makeError(u"unrecognized extension field `%s`%s."
295+
" Did you include "
296+
"a $schemas section?" % (
297+
d, " and strict_foreign_properties is True" if strict_foreign_properties else ""))
298+
if strict_foreign_properties:
299+
errors.append(err)
300+
else:
301+
logger.warn(err)
302+
else:
303+
err = sl.makeError(u"invalid field `%s`, expected one of: %s" % (
304+
d, ", ".join("'%s'" % fn.name for fn in expected_schema.fields)))
305+
if strict:
306+
errors.append(err)
284307
else:
285-
errors.append(sl.makeError(u"invalid field `%s`, expected one of: %s" % (
286-
d, ", ".join("'%s'" % fn.name for fn in expected_schema.fields))))
308+
logger.warn(err)
287309

288310
if errors:
289311
if raise_ex:

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
extras_require = {} # TODO: to be removed when the above is added
4848

4949
setup(name='schema-salad',
50-
version='2.2',
50+
version='2.3',
5151
description='Schema Annotations for Linked Avro Data (SALAD)',
5252
long_description=open(README).read(),
5353
author='Common workflow language working group',

0 commit comments

Comments
 (0)