From 0d5cd3bce5cee043ff43222bf94cbc73e53f7ecb Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Thu, 27 Dec 2018 19:37:53 +0000 Subject: [PATCH] better import errors, fix #309 (#336) --- HISTORY.rst | 1 + pydantic/errors.py | 2 +- pydantic/types.py | 6 ++++-- tests/test_types.py | 19 ++++++++++++++++++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 690eb24..019ce26 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -14,6 +14,7 @@ v0.17.0 (unreleased) * support for passing Config class in dataclasses decorator, #276 by @jarekkar (**breaking change**: this supersedes the ``validate_assignment`` argument with ``config``) * support for nested dataclasses, #334 by @samuelcolvin +* better errors when getting an ``ImportError`` with ``PyObject``, #309 by @samuelcolvin v0.16.1 (2018-12-10) .................... diff --git a/pydantic/errors.py b/pydantic/errors.py index 42a550a..36ac741 100644 --- a/pydantic/errors.py +++ b/pydantic/errors.py @@ -109,7 +109,7 @@ class PathNotADirectoryError(_PathValueError): class PyObjectError(PydanticTypeError): - msg_template = 'ensure this value contains valid import path' + msg_template = 'ensure this value contains valid import path: {error_message}' class ListError(PydanticTypeError): diff --git a/pydantic/types.py b/pydantic/types.py index 6f976ce..b80c2e0 100644 --- a/pydantic/types.py +++ b/pydantic/types.py @@ -6,7 +6,7 @@ from typing import Optional, Pattern, Set, Type, Union from uuid import UUID from . import errors -from .utils import change_exception, import_string, make_dsn, url_regex_generator, validate_email +from .utils import import_string, make_dsn, url_regex_generator, validate_email from .validators import ( anystr_length_validator, anystr_strip_whitespace, @@ -221,8 +221,10 @@ class PyObject: @classmethod def validate(cls, value): if value is not None: - with change_exception(errors.PyObjectError, ImportError): + try: return import_string(value) + except ImportError as e: + raise errors.PyObjectError(error_message=str(e)) class DSN(str): diff --git a/tests/test_types.py b/tests/test_types.py index 0700f34..77e90c9 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -111,10 +111,27 @@ def test_module_import(): m = PyObjectModel() assert m.module == os.path + with pytest.raises(ValidationError) as exc_info: PyObjectModel(module='foobar') assert exc_info.value.errors() == [ - {'loc': ('module',), 'msg': 'ensure this value contains valid import path', 'type': 'type_error.pyobject'} + { + 'loc': ('module',), + 'msg': 'ensure this value contains valid import path: ' '"foobar" doesn\'t look like a module path', + 'type': 'type_error.pyobject', + 'ctx': {'error_message': '"foobar" doesn\'t look like a module path'}, + } + ] + + with pytest.raises(ValidationError) as exc_info: + PyObjectModel(module='os.missing') + assert exc_info.value.errors() == [ + { + 'loc': ('module',), + 'msg': 'ensure this value contains valid import path: ' 'Module "os" does not define a "missing" attribute', + 'type': 'type_error.pyobject', + 'ctx': {'error_message': 'Module "os" does not define a "missing" attribute'}, + } ]