Skip to content

Commit

Permalink
Merge pull request #5 from vitorbaptista/improve-exception-handling
Browse files Browse the repository at this point in the history
[#4] Use exceptions to represent errors, not strings
  • Loading branch information
pwalsh committed Nov 24, 2015
2 parents a7ca6e9 + d4c13b7 commit c559cbe
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 39 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ The `schema` can be a json string, python object, or a schema id corresponding w

`valid` is a boolean determining whether the datapackage validates against the schema.

`errors` is an array of error string messages. Empty if `valid` is True.
`errors` is a list of exceptions found during validation. Empty if `valid` is True.

[Data Package Profiles]: https://github.com/dataprotocols/registry
14 changes: 14 additions & 0 deletions datapackage_validate/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class DataPackageValidateException(Exception):
pass


class SchemaError(DataPackageValidateException):
pass


class ValidationError(DataPackageValidateException):
pass


class RegistryError(DataPackageValidateException):
pass
25 changes: 15 additions & 10 deletions datapackage_validate/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import requests

from . import compat
from . import exceptions


def _get_schema_url_from_registry(id, registry):
Expand All @@ -34,7 +35,8 @@ def validate(datapackage, schema='base'):
`valid` - a boolean to determine whether the datapackage validates against
the schema.
`errors` - an array of error string messages. Empty if `valid` is True.
`errors` - a list of exceptions found during validation. Empty if `valid`
is True.
'''

valid = False
Expand All @@ -48,9 +50,12 @@ def validate(datapackage, schema='base'):
try:
datapackage_obj = json.loads(datapackage)
except ValueError as e:
errors.append('Invalid JSON: {0}'.format(e))
errors.append(exceptions.DataPackageValidateException(e))
elif not (isinstance(datapackage, dict) or isinstance(datapackage, list)):
errors.append('Invalid Data Package: not a string or object')
msg = 'Data Package must be a dict or JSON string, but was a \'{0}\''
dp_type = type(datapackage).__name__
error = exceptions.DataPackageValidateException(msg.format(dp_type))
errors.append(error)
else:
datapackage_obj = datapackage

Expand All @@ -66,20 +71,20 @@ def validate(datapackage, schema='base'):
try:
registry = datapackage_registry.get()
except requests.HTTPError as e:
errors.append('Registry Error: {0}'.format(e))
errors.append(exceptions.RegistryError(e))
else:
schema_url = _get_schema_url_from_registry(schema, registry)
if schema_url is None:
errors.append(
'Registry Error: no schema with id "{0}"'
.format(schema))
msg = 'No schema with id \'{0}\''.format(e)
errors.append(exceptions.RegistryError(msg))
else:
try:
schema_obj = _fetch_schema_obj_from_url(schema_url)
except requests.HTTPError as e:
errors.append('Registry Error: {0}'.format(e))
errors.append(exceptions.RegistryError(e))
elif not isinstance(schema, dict):
errors.append('Invalid Schema: not a string or object')
msg = 'Schema must be a string or dict'
errors.append(exceptions.SchemaError(msg))
else:
schema_obj = schema

Expand All @@ -88,7 +93,7 @@ def validate(datapackage, schema='base'):
try:
jsonschema.validate(datapackage_obj, schema_obj)
except jsonschema.ValidationError as e:
errors.append('Schema ValidationError: {0}'.format(e.message))
errors.append(exceptions.ValidationError(e.message))
else:
valid = True

Expand Down
41 changes: 13 additions & 28 deletions tests/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import unittest

from nose.tools import (assert_true,
assert_false)
assert_false,
assert_is_instance)
import httpretty

import datapackage_validate
import datapackage_validate.exceptions as exceptions


REGISTRY_BACKEND_URL = \
Expand Down Expand Up @@ -73,7 +75,7 @@ def test_invalid_json_string(self):

assert_false(valid)
assert_true(errors)
assert_true('Invalid JSON:' in errors[0])
assert_is_instance(errors[0], exceptions.DataPackageValidateException)

@httpretty.activate
def test_invalid_json_not_string(self):
Expand All @@ -92,8 +94,7 @@ def test_invalid_json_not_string(self):

assert_false(valid)
assert_true(errors)
assert_true('Invalid Data Package: not a string or object'
in errors[0])
assert_is_instance(errors[0], exceptions.DataPackageValidateException)

@httpretty.activate
def test_valid_json_obj(self):
Expand Down Expand Up @@ -135,12 +136,8 @@ def test_invalid_json_obj(self):

assert_false(valid)
assert_true(errors)
# Py2 returns u'name', Py3 doesn't. Test for either.
assert_true("Schema ValidationError: u'name' is a required property"
in errors[0]
or
"Schema ValidationError: 'name' is a required property"
in errors[0])
assert_is_instance(errors[0], exceptions.ValidationError)
assert_true("'name' is a required property" in str(errors[0]))


class TestValidateWithSchemaAsArgument(unittest.TestCase):
Expand Down Expand Up @@ -172,8 +169,7 @@ def test_schema_as_wrong_object_type(self):

assert_false(valid)
assert_true(errors)
assert_true('Invalid Schema: not a string or object'
in errors[0])
assert_is_instance(errors[0], exceptions.SchemaError)

def test_schema_as_dict(self):
'''Pass schema as python dict to validate()'''
Expand Down Expand Up @@ -212,8 +208,7 @@ def test_schema_as_invalid_id(self):

assert_false(valid)
assert_true(errors)
assert_true('Registry Error: no schema with id "not-a-valid-id"'
in errors[0])
assert_is_instance(errors[0], exceptions.RegistryError)

@httpretty.activate
def test_schema_404_raises_error(self):
Expand All @@ -228,9 +223,7 @@ def test_schema_404_raises_error(self):

assert_false(valid)
assert_true(errors)
assert_true('Registry Error: 404 Client Error: '
'Not Found for url: https://example.com/base_schema.json'
in errors[0])
assert_is_instance(errors[0], exceptions.RegistryError)

@httpretty.activate
def test_schema_500_raises_error(self):
Expand All @@ -245,10 +238,7 @@ def test_schema_500_raises_error(self):

assert_false(valid)
assert_true(errors)
assert_true('Registry Error: 500 Server Error: '
'Internal Server Error for url: '
'https://example.com/base_schema.json'
in errors[0])
assert_is_instance(errors[0], exceptions.RegistryError)

@httpretty.activate
def test_registry_404_raises_error(self):
Expand All @@ -260,9 +250,7 @@ def test_registry_404_raises_error(self):

assert_false(valid)
assert_true(errors)
assert_true('Registry Error: 404 Client Error: Not Found for url: '
'https://rawgit.com/dataprotocols/registry/master/registry.csv'
in errors[0])
assert_is_instance(errors[0], exceptions.RegistryError)

@httpretty.activate
def test_registry_500_raises_error(self):
Expand All @@ -274,7 +262,4 @@ def test_registry_500_raises_error(self):

assert_false(valid)
assert_true(errors)
assert_true('Registry Error: 500 Server Error: '
'Internal Server Error for url: '
'https://rawgit.com/dataprotocols/registry/master/registry.csv'
in errors[0])
assert_is_instance(errors[0], exceptions.RegistryError)

0 comments on commit c559cbe

Please sign in to comment.