Skip to content

Commit 1bb05e4

Browse files
committed
Refactor cpyext tests
1 parent 082a496 commit 1bb05e4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+130
-253
lines changed

graalpython/com.oracle.graal.python.test/src/tests/cpyext/__init__.py

Lines changed: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,17 @@
3636
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
39-
import os
39+
4040
from importlib import invalidate_caches
41-
from io import StringIO
42-
from pathlib import Path
43-
from string import Formatter
4441

4542
import gc
43+
import os
4644
import sys
45+
import unittest
46+
from copy import deepcopy
47+
from io import StringIO
48+
from pathlib import Path
49+
from string import Formatter
4750

4851
DIR = Path(__file__).parent.absolute()
4952

@@ -81,23 +84,14 @@ def unhandled_error_compare_with_message(x, y):
8184
else:
8285
return x == y
8386

84-
class CPyExtTestCase():
8587

86-
def setUpClass(self):
87-
for typ in type(self).mro():
88-
for k, v in typ.__dict__.items():
89-
if k.startswith("test_"):
90-
modname = k.replace("test_", "")
91-
if k.startswith("test_graalpython_"):
92-
if not GRAALPYTHON:
93-
continue
94-
else:
95-
modname = k.replace("test_graalpython_", "")
96-
self.compile_module(modname)
88+
class CPyExtTestCase(unittest.TestCase):
89+
pass
9790

9891

9992
compiled_registry = set()
10093

94+
10195
def ccompile(self, name, check_duplicate_name=True):
10296
from distutils.core import setup, Extension
10397
from distutils.sysconfig import get_config_var
@@ -115,7 +109,7 @@ def ccompile(self, name, check_duplicate_name=True):
115109
m.update(block)
116110
cur_checksum = m.hexdigest()
117111

118-
install_dir = _install_dir_for(name)
112+
install_dir = DIR / 'build' / name
119113

120114
# see if there is already a checksum file
121115
checksum_file = install_dir / f'{name}{EXT_SUFFIX}.sha256'
@@ -141,8 +135,11 @@ def ccompile(self, name, check_duplicate_name=True):
141135
# manually deleted the shared library file.
142136
if available_checksum != cur_checksum or not lib_file.exists():
143137
module = Extension(name, sources=[str(source_file)])
144-
verbosity = '--verbose' if sys.flags.verbose else '--quiet'
145-
args = [verbosity, 'build', 'install_lib', '-f', f'--install-dir={install_dir}', 'clean']
138+
args = [
139+
'--verbose' if sys.flags.verbose else '--quiet',
140+
'build', f'--build-base={install_dir}',
141+
'install_lib', '-f', f'--install-dir={install_dir}',
142+
]
146143
setup(
147144
script_name='setup',
148145
script_args=args,
@@ -166,6 +163,8 @@ def ccompile(self, name, check_duplicate_name=True):
166163
if GRAALPYTHON:
167164
file_not_empty(lib_file)
168165

166+
return str(install_dir)
167+
169168

170169
def file_not_empty(path):
171170
for i in range(3):
@@ -342,13 +341,13 @@ def file_not_empty(path):
342341
"""
343342

344343

345-
class CPyExtFunction():
344+
class CPyExtFunction:
346345

347346
def __init__(self, pfunc, parameters, template=c_template, cmpfunc=None, stderr_validator=None, **kwargs):
348347
self.template = template
349348
self.pfunc = pfunc
350349
self.parameters = parameters
351-
kwargs["name"] = kwargs["name"] if "name" in kwargs else None
350+
kwargs.setdefault("name", None)
352351
self.name = kwargs["name"]
353352
if "code" in kwargs:
354353
kwargs["customcode"] = kwargs["code"]
@@ -403,12 +402,28 @@ def _insert(self, d, name, default_value):
403402
def __repr__(self):
404403
return "<CPyExtFunction %s>" % self.name
405404

406-
def test(self):
407-
sys.path.insert(0, str(_install_dir_for(self.name)))
405+
def __set_name__(self, owner, name):
406+
if self.name:
407+
raise RuntimeError(f"{type(self)} already assigned to a test suite. Use copy() method to duplicate a test")
408+
self.name = name.removeprefix('test_')
409+
self.__name__ = name
410+
self.__qualname__ = f'{owner.__qualname__}.{name}'
411+
412+
def copy(self):
413+
inst = deepcopy(self)
414+
inst.name = None
415+
return inst
416+
417+
@property
418+
def __code__(self):
419+
return type(self).__call__.__code__
420+
421+
def __call__(self):
408422
try:
409-
cmodule = __import__(self.name)
410-
finally:
411-
sys.path.pop(0)
423+
self.create_module(self.name)
424+
cmodule = compile_module_from_file(self.name)
425+
except Exception as e:
426+
raise RuntimeError(f"{self.__qualname__}: Failed to create module") from e
412427
ctest = getattr(cmodule, "test_%s" % self.name)
413428
cargs = self.parameters()
414429
pargs = self.parameters()
@@ -418,7 +433,7 @@ def test(self):
418433
sys.stderr = StringIO()
419434
try:
420435
cresult = ctest(cargs[i])
421-
except BaseException as e:
436+
except Exception as e:
422437
cresult = e
423438
else:
424439
if self.stderr_validator:
@@ -432,20 +447,13 @@ def test(self):
432447
except BaseException as e:
433448
presult = e
434449

435-
if not self.cmpfunc:
436-
assert cresult == presult, ("%r == %r in %s(%s)" % (cresult, presult, self.name, pargs[i]))
450+
if self.cmpfunc:
451+
success = self.cmpfunc(cresult, presult)
437452
else:
438-
assert self.cmpfunc(cresult, presult), ("%r == %r in %s(%s)" % (cresult, presult, self.name, pargs[i]))
453+
success = cresult == presult
454+
assert success, f"{self.__qualname__}: C result not equal to python reference implementation result.\n\tArguments: {pargs[i]!r}\n\tExpected result: {presult!r}\n\tActual C result: {cresult!r}"
439455
gc.collect()
440456

441-
def __get__(self, instance, typ=None):
442-
if typ is None:
443-
return self
444-
else:
445-
CPyExtFunction.test.__name__ = self.name
446-
CPyExtFunction.test.__qualname__ = f'{CPyExtFunction.__name__}.test_{self.name}'
447-
return self.test
448-
449457

450458
class CPyExtFunctionOutVars(CPyExtFunction):
451459
'''
@@ -511,25 +519,18 @@ def get_value(self, key, args, kwds):
511519
return Formatter.get_value(key, args, kwds)
512520

513521

514-
def _install_dir_for(name):
515-
return DIR / 'build' / name
516-
517-
518-
def _compile_module(c_source, name):
522+
def compile_module_from_string(c_source: str, name: str):
519523
source_file = DIR / f'{name}.c'
520524
with open(source_file, "wb", buffering=0) as f:
521525
f.write(bytes(c_source, 'utf-8'))
522-
# ensure file was really written
523-
try:
524-
stat_result = os.stat(source_file)
525-
if stat_result[6] == 0:
526-
raise SystemError("empty source file %s" % (source_file,))
527-
except FileNotFoundError:
528-
raise SystemError("source file %s not available" % (source_file,))
529-
ccompile(None, name)
530-
sys.path.insert(0, str(_install_dir_for(name)))
526+
return compile_module_from_file(name)
527+
528+
529+
def compile_module_from_file(module_name: str):
530+
install_dir = ccompile(None, module_name)
531+
sys.path.insert(0, install_dir)
531532
try:
532-
cmodule = __import__(name)
533+
cmodule = __import__(module_name)
533534
finally:
534535
sys.path.pop(0)
535536
return cmodule
@@ -570,7 +571,7 @@ def CPyExtType(name, code='', **kwargs):
570571
kwargs.setdefault("post_ready_code", "")
571572
c_source = type_decl + UnseenFormatter().format(mod_template, **kwargs)
572573

573-
cmodule = _compile_module(c_source, name)
574+
cmodule = compile_module_from_string(c_source, name)
574575
return getattr(cmodule, name)
575576

576577

@@ -787,7 +788,7 @@ def CPyExtHeapType(name, bases=(object), code='', slots=None, **kwargs):
787788
kwargs.setdefault("ready_code", "")
788789
kwargs.setdefault("post_ready_code", "")
789790
code = UnseenFormatter().format(template, **kwargs)
790-
mod = _compile_module(code, name)
791+
mod = compile_module_from_string(code, name)
791792
return mod.create(bases)
792793

793794

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_abstract.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40-
import sys, array, collections
41-
from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, CPyExtType, unhandled_error_compare, GRAALPYTHON
40+
import array
41+
import collections
42+
import sys
43+
44+
from . import CPyExtTestCase, CPyExtFunction, CPyExtType, unhandled_error_compare
45+
4246
__dir__ = __file__.rpartition("/")[0]
4347

4448

@@ -659,10 +663,6 @@ def test___name__(self):
659663

660664
class TestAbstract(CPyExtTestCase):
661665

662-
def compile_module(self, name):
663-
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
664-
super(TestAbstract, self).compile_module(name)
665-
666666
test_PyNumber_Absolute = CPyExtFunction(
667667
lambda args: abs(args[0]),
668668
_default_unarop_args,
@@ -1158,7 +1158,7 @@ def compile_module(self, name):
11581158
)
11591159

11601160
# 'PySequence_Length' is just a redefinition of 'PySequence_Size'
1161-
test_PySequence_Length = test_PySequence_Size
1161+
test_PySequence_Length = test_PySequence_Size.copy()
11621162

11631163
test_PySequence_GetItem = CPyExtFunction(
11641164
_reference_getitem,
@@ -1565,4 +1565,4 @@ def _reference_delslice(args):
15651565
cmpfunc=unhandled_error_compare
15661566
)
15671567

1568-
test_PyObject_Length = test_PyObject_Size
1568+
test_PyObject_Length = test_PyObject_Size.copy()

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_array.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,6 @@ def reference_getbuffer(args):
6161

6262
class TestPyArray(CPyExtTestCase):
6363

64-
def compile_module(self, name):
65-
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
66-
super().compile_module(name)
67-
6864
if sys.implementation.name == 'graalpy':
6965
test__PyArray_Resize = CPyExtFunction(
7066
reference_array_resize,

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_bool.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40-
import sys
41-
from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare, GRAALPYTHON
40+
from . import CPyExtTestCase, CPyExtFunction, unhandled_error_compare
41+
4242
__dir__ = __file__.rpartition("/")[0]
4343

4444

@@ -47,9 +47,6 @@ class DummyNonInt():
4747

4848

4949
class TestPyBool(CPyExtTestCase):
50-
def compile_module(self, name):
51-
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
52-
super(TestPyBool, self).compile_module(name)
5350

5451
# (tfel): This test actually checks that the wrapped booleans that are
5552
# stored as sulong globals are singletons

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_bytes.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ def __iter__(self):
108108

109109
class TestPyBytes(CPyExtTestCase):
110110

111-
def compile_module(self, name):
112-
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
113-
super(TestPyBytes, self).compile_module(name)
114-
115111
# Below are the PyBytes_* identifiers that we know are used in numpy
116112

117113
# PyBytes_FromStringAndSize

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_capsule.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,13 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40-
import sys
41-
from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare, GRAALPYTHON
40+
from . import CPyExtTestCase, CPyExtFunction, unhandled_error_compare
41+
4242
__dir__ = __file__.rpartition("/")[0]
4343

4444

4545
class TestPyCapsule(CPyExtTestCase):
4646

47-
def compile_module(self, name):
48-
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
49-
super(TestPyCapsule, self).compile_module(name)
50-
5147
test_PyCapsule_CheckExact = CPyExtFunction(
5248
lambda args: True,
5349
lambda: (

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_classobject.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
# SOFTWARE.
3939

4040
import sys
41-
from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare, GRAALPYTHON
41+
42+
from . import CPyExtTestCase, CPyExtFunction
43+
4244
__dir__ = __file__.rpartition("/")[0]
4345

4446

@@ -51,9 +53,6 @@ def foo(self):
5153

5254

5355
class TestClassobject(CPyExtTestCase):
54-
def compile_module(self, name):
55-
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
56-
super().compile_module(name)
5756

5857
testmod = type(sys)("foo")
5958

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_codec.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@
4444

4545
class TestPyCodec(CPyExtTestCase):
4646

47-
def compile_module(self, name):
48-
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
49-
super().compile_module(name)
50-
5147
test_PyCodec_Encoder = CPyExtFunction(
5248
lambda args: codecs.lookup(args[0])[0],
5349
lambda: (

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_codeobject.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,6 @@ def foo(self):
7979

8080

8181
class TestCodeobject(CPyExtTestCase):
82-
def compile_module(self, name):
83-
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
84-
super().compile_module(name)
8582

8683
testmod = type(sys)("foo")
8784

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_complex.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,6 @@ class DummyComplexSubclass(complex):
8686

8787
class TestPyComplex(CPyExtTestCase):
8888

89-
def compile_module(self, name):
90-
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
91-
super(TestPyComplex, self).compile_module(name)
92-
9389
test_PyComplex_AsCComplex = CPyExtFunction(
9490
lambda args: True,
9591
lambda: (

0 commit comments

Comments
 (0)