From b5568a7cba6f90323ed2d93cdd883d8fce363c4c Mon Sep 17 00:00:00 2001 From: Simon Cross Date: Fri, 11 Dec 2020 12:27:17 +0200 Subject: [PATCH 1/4] Assert that the module imported also ended up in sys.modules. --- test/support.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/support.py b/test/support.py index 417320a54..367e9d6db 100644 --- a/test/support.py +++ b/test/support.py @@ -216,9 +216,10 @@ def load_module(self, name, mod_filename): "Test module {!r} already present in sys.modules".format(name)) importlib.invalidate_caches() mod_dir = os.path.dirname(mod_filename) + sys.path.insert(0, mod_dir) try: - sys.path.insert(0, mod_dir) module = importlib.import_module(name) + assert sys.modules[name] is module finally: # assert that the module import didn't change the sys.path entry # that was added above, then remove the entry. From bf29da88c3b81871457bee539c0d40ae2a409007 Mon Sep 17 00:00:00 2001 From: Simon Cross Date: Fri, 11 Dec 2020 12:27:48 +0200 Subject: [PATCH 2/4] Copy the loader, spec and package across from the stub module. --- hpy/devel/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hpy/devel/__init__.py b/hpy/devel/__init__.py index 33be1a011..733a6f219 100644 --- a/hpy/devel/__init__.py +++ b/hpy/devel/__init__.py @@ -112,6 +112,9 @@ def __bootstrap__(): ext_filepath = pkg_resources.resource_filename(__name__, {ext_file!r}) m = load_from_spec(Spec({module_name!r}, ext_filepath)) m.__file__ = ext_filepath + m.__loader__ = __loader__ + m.__package__ = __package__ + m.__spec__ = __spec__ sys.modules[__name__] = m __bootstrap__() From 41183d2fc1b98e69609319e9f6f9022ffcde78ce Mon Sep 17 00:00:00 2001 From: Simon Cross Date: Fri, 11 Dec 2020 12:28:18 +0200 Subject: [PATCH 3/4] Reactivate the importing attribute test. --- test/test_importing.py | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/test/test_importing.py b/test/test_importing.py index 7d0e7ceca..69e9beb5c 100644 --- a/test/test_importing.py +++ b/test/test_importing.py @@ -1,31 +1,20 @@ -import pytest as pytest_collecting - from .support import HPyTest -# this function should probably goes somewhere into hpy.universal and/or and -# hpy package and/or an import hook, or whatever. I do not want to think about -# this now. -def import_module_properly(mod): - raise NotImplementedError("fix me eventually") - -# this was moved from support.py, where it did not belong -## class HPyLoader(ExtensionFileLoader): -## def create_module(self, spec): -## import hpy.universal -## return hpy.universal.load_from_spec(spec) - class TestImporting(HPyTest): - @pytest_collecting.mark.xfail def test_importing_attributes(self): + import pytest import sys - modname = 'mytest' - so_filename = self.compile_module(""" + if not hasattr(sys, "executable"): + pytest.skip() + mod = self.make_module(""" @INIT - """, name=modname) - mod = import_module_properly(so_filename, modname) - assert mod in sys.modules + """, name='mytest') + assert mod.__name__ == 'mytest' + assert mod.__package__ == '' + assert mod.__doc__ == 'some test for hpy' assert mod.__loader__.name == 'mytest' assert mod.__spec__.loader is mod.__loader__ + assert mod.__spec__.name == 'mytest' assert mod.__file__ From 7d39dc2916f0e78fc16d95cf5d12089000bcef5a Mon Sep 17 00:00:00 2001 From: Simon Cross Date: Fri, 18 Dec 2020 15:37:21 +0200 Subject: [PATCH 4/4] Neaten up the capability checking provided by the test support infrastructure. --- test/support.py | 31 ++++++++++++++++++++++++++++--- test/test_cpy_compat.py | 8 ++++---- test/test_hpyerr.py | 2 +- test/test_hpytype.py | 2 +- test/test_importing.py | 3 +-- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/test/support.py b/test/support.py index 367e9d6db..3d2b6cd1e 100644 --- a/test/support.py +++ b/test/support.py @@ -244,9 +244,34 @@ def make_module(self, main_src, name='mytest', extra_sources=()): return self.compiler.make_module(ExtensionTemplate, main_src, name, extra_sources) - def should_check_refcount(self): - # defaults to True on CPython, but is set to False by e.g. PyPy - return sys.implementation.name == 'cpython' + def supports_refcounts(self): + """ Returns True if the underlying Python implementation supports + reference counts. + + By default returns True on CPython and False on other + implementations. + """ + return sys.implementation.name == "cpython" + + def supports_ordinary_make_module_imports(self): + """ Returns True if `.make_module(...)` loads modules using a + standard Python import mechanism (e.g. `importlib.import_module`). + + By default returns True because the base implementation of + `.make_module(...)` uses an ordinary import. Sub-classes that + override `.make_module(...)` may also want to override this + method. + """ + return True + + def supports_sys_executable(self): + """ Returns True is `sys.executable` is set to a value that allows + a Python equivalent to the current Python to be launched via, e.g., + `subprocess.run(...)`. + + By default returns `True` if sys.executable is set to a true value. + """ + return bool(getattr(sys, "executable", None)) # the few functions below are copied and adapted from cffi/ffiplatform.py diff --git a/test/test_cpy_compat.py b/test/test_cpy_compat.py index 4ebc8b40a..95f6ea918 100644 --- a/test/test_cpy_compat.py +++ b/test/test_cpy_compat.py @@ -3,7 +3,7 @@ class TestCPythonCompatibility(HPyTest): - # One note about the should_check_refcount() in the tests below: on + # One note about the supports_refcounts() in the tests below: on # CPython, handles are actually implemented as INCREF/DECREF, so we can # check e.g. after an HPy_Dup the refcnt is += 1. However, on PyPy they # are implemented in a completely different way which is unrelated to the @@ -35,7 +35,7 @@ def test_frompyobject(self): x = mod.f() assert x[0] == 1234 assert len(x) == 2 - if self.should_check_refcount(): + if self.supports_refcounts(): assert x == [1234, +1] def test_aspyobject(self): @@ -97,7 +97,7 @@ def test_hpy_close(self): @INIT """) x = mod.f() - if self.should_check_refcount(): + if self.supports_refcounts(): assert x == -1 def test_hpy_dup(self): @@ -123,7 +123,7 @@ def test_hpy_dup(self): @INIT """) x = mod.f() - if self.should_check_refcount(): + if self.supports_refcounts(): assert x == +1 def test_many_handles(self): diff --git a/test/test_hpyerr.py b/test/test_hpyerr.py index 58a054f9b..f00273c1a 100644 --- a/test/test_hpyerr.py +++ b/test/test_hpyerr.py @@ -35,7 +35,7 @@ def test_FatalError(self): @EXPORT(f) @INIT """) - if not hasattr(sys, "executable"): + if not self.supports_sys_executable(): # if sys.executable is not available (e.g. inside pypy app-level) # tests, then skip the rest of this test return diff --git a/test/test_hpytype.py b/test/test_hpytype.py index 8ccec4e19..4edb58aaf 100644 --- a/test/test_hpytype.py +++ b/test/test_hpytype.py @@ -192,7 +192,7 @@ def test_HPy_New(self): def test_refcount(self): import pytest import sys - if not self.should_check_refcount(): + if not self.supports_refcounts(): pytest.skip() mod = self.make_module(""" @DEFINE_PointObject diff --git a/test/test_importing.py b/test/test_importing.py index 69e9beb5c..94de0b11d 100644 --- a/test/test_importing.py +++ b/test/test_importing.py @@ -5,8 +5,7 @@ class TestImporting(HPyTest): def test_importing_attributes(self): import pytest - import sys - if not hasattr(sys, "executable"): + if not self.supports_ordinary_make_module_imports(): pytest.skip() mod = self.make_module(""" @INIT