From fc18f8ef3490c5f0940de12268fb45e6ac9642d1 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Mon, 15 Feb 2021 12:17:34 +0000 Subject: [PATCH] make "resolve_annotations" more lenient, allowing for missing modules (#2366) * make "resolve_annotations" more lenient, allowing for missing modules * linting --- changes/2363-samuelcolvin.md | 1 + pydantic/typing.py | 12 +++++++++--- tests/test_edge_cases.py | 20 ++++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 changes/2363-samuelcolvin.md diff --git a/changes/2363-samuelcolvin.md b/changes/2363-samuelcolvin.md new file mode 100644 index 0000000000..68c221ab60 --- /dev/null +++ b/changes/2363-samuelcolvin.md @@ -0,0 +1 @@ +Make `resolve_annotations` more lenient, allowing for missing modules diff --git a/pydantic/typing.py b/pydantic/typing.py index 314faef43b..36cd1fb1d6 100644 --- a/pydantic/typing.py +++ b/pydantic/typing.py @@ -289,10 +289,16 @@ def resolve_annotations(raw_annotations: Dict[str, Type[Any]], module_name: Opti Resolve string or ForwardRef annotations into type objects if possible. """ + base_globals: Optional[Dict[str, Any]] = None if module_name: - base_globals: Optional[Dict[str, Any]] = sys.modules[module_name].__dict__ - else: - base_globals = None + try: + module = sys.modules[module_name] + except KeyError: + # happens occasionally, see https://github.com/samuelcolvin/pydantic/issues/2363 + pass + else: + base_globals = module.__dict__ + annotations = {} for name, value in raw_annotations.items(): if isinstance(value, str): diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py index 5ad7347d16..09e1bac76d 100644 --- a/tests/test_edge_cases.py +++ b/tests/test_edge_cases.py @@ -1,3 +1,4 @@ +import importlib.util import sys from collections.abc import Hashable from decimal import Decimal @@ -1773,3 +1774,22 @@ def get_double_a(self) -> float: assert model.a == 10.2 assert model.b == 10 return model.get_double_a() == 20.2 + + +def test_resolve_annotations_module_missing(tmp_path): + # see https://github.com/samuelcolvin/pydantic/issues/2363 + file_path = tmp_path / 'module_to_load.py' + # language=Python + file_path.write_text( + """ +from pydantic import BaseModel +class User(BaseModel): + id: int + name = 'Jane Doe' +""" + ) + + spec = importlib.util.spec_from_file_location('my_test_module', file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + assert module.User(id=12).dict() == {'id': 12, 'name': 'Jane Doe'}