From e9f7f9a4bc38fb490a43b46d3d70234a9ee44039 Mon Sep 17 00:00:00 2001 From: Adriane Boyd Date: Mon, 1 Mar 2021 16:32:31 +0100 Subject: [PATCH] Fix is_cython_func for additional imported code * Fix `is_cython_func` for imported code loaded under `python_code` module name * Add `make_named_tempfile` context manager to test utils to test loading of imported code * Add test for validation of `initialize` params in custom module --- spacy/tests/test_misc.py | 35 +++++++++++++++++++++++++++++++++-- spacy/tests/util.py | 7 +++++++ spacy/util.py | 7 ++++--- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/spacy/tests/test_misc.py b/spacy/tests/test_misc.py index e694baa40..99816d40f 100644 --- a/spacy/tests/test_misc.py +++ b/spacy/tests/test_misc.py @@ -7,7 +7,7 @@ from spacy import util from spacy import prefer_gpu, require_gpu, require_cpu from spacy.ml._precomputable_affine import PrecomputableAffine from spacy.ml._precomputable_affine import _backprop_precomputable_affine_padding -from spacy.util import dot_to_object, SimpleFrozenList +from spacy.util import dot_to_object, SimpleFrozenList, import_file from thinc.api import Config, Optimizer, ConfigValidationError from spacy.training.batchers import minibatch_by_words from spacy.lang.en import English @@ -17,7 +17,7 @@ from spacy.schemas import ConfigSchemaTraining from thinc.api import get_current_ops, NumpyOps, CupyOps -from .util import get_random_doc +from .util import get_random_doc, make_named_tempfile @pytest.fixture @@ -347,3 +347,34 @@ def test_resolve_dot_names(): errors = e.value.errors assert len(errors) == 1 assert errors[0]["loc"] == ["training", "xyz"] + + +def test_import_code(): + code_str = """ +from spacy import Language + +class DummyComponent: + def __init__(self, vocab, name): + pass + + def initialize(self, get_examples, *, nlp, dummy_param: int): + pass + +@Language.factory( + "dummy_component", +) +def make_dummy_component( + nlp: Language, name: str +): + return DummyComponent(nlp.vocab, name) +""" + + with make_named_tempfile(mode="w", suffix=".py") as fileh: + fileh.write(code_str) + fileh.flush() + + import_file("python_code", fileh.name) + config = {"initialize": {"components": {"dummy_component": {"dummy_param": 1}}}} + nlp = English.from_config(config) + nlp.add_pipe("dummy_component") + nlp.initialize() diff --git a/spacy/tests/util.py b/spacy/tests/util.py index ef7b4d00d..475db4453 100644 --- a/spacy/tests/util.py +++ b/spacy/tests/util.py @@ -14,6 +14,13 @@ def make_tempfile(mode="r"): f.close() +@contextlib.contextmanager +def make_named_tempfile(mode="r", suffix=None): + f = tempfile.NamedTemporaryFile(mode=mode, suffix=suffix) + yield f + f.close() + + def get_batch(batch_size): vocab = Vocab() docs = [] diff --git a/spacy/util.py b/spacy/util.py index ce1022d25..bcb51fe7d 100644 --- a/spacy/util.py +++ b/spacy/util.py @@ -1454,9 +1454,10 @@ def is_cython_func(func: Callable) -> bool: if hasattr(func, attr): # function or class instance return True # https://stackoverflow.com/a/55767059 - if hasattr(func, "__qualname__") and hasattr(func, "__module__"): # method - cls_func = vars(sys.modules[func.__module__])[func.__qualname__.split(".")[0]] - return hasattr(cls_func, attr) + if hasattr(func, "__qualname__") and hasattr(func, "__module__") \ + and func.__module__ in sys.modules: # method + cls_func = vars(sys.modules[func.__module__])[func.__qualname__.split(".")[0]] + return hasattr(cls_func, attr) return False