36
36
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
37
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
38
# SOFTWARE.
39
- import os
39
+
40
40
from importlib import invalidate_caches
41
- from io import StringIO
42
- from pathlib import Path
43
- from string import Formatter
44
41
45
42
import gc
43
+ import os
46
44
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
47
50
48
51
DIR = Path (__file__ ).parent .absolute ()
49
52
@@ -81,23 +84,14 @@ def unhandled_error_compare_with_message(x, y):
81
84
else :
82
85
return x == y
83
86
84
- class CPyExtTestCase ():
85
87
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
97
90
98
91
99
92
compiled_registry = set ()
100
93
94
+
101
95
def ccompile (self , name , check_duplicate_name = True ):
102
96
from distutils .core import setup , Extension
103
97
from distutils .sysconfig import get_config_var
@@ -115,7 +109,7 @@ def ccompile(self, name, check_duplicate_name=True):
115
109
m .update (block )
116
110
cur_checksum = m .hexdigest ()
117
111
118
- install_dir = _install_dir_for ( name )
112
+ install_dir = DIR / 'build' / name
119
113
120
114
# see if there is already a checksum file
121
115
checksum_file = install_dir / f'{ name } { EXT_SUFFIX } .sha256'
@@ -141,8 +135,11 @@ def ccompile(self, name, check_duplicate_name=True):
141
135
# manually deleted the shared library file.
142
136
if available_checksum != cur_checksum or not lib_file .exists ():
143
137
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
+ ]
146
143
setup (
147
144
script_name = 'setup' ,
148
145
script_args = args ,
@@ -166,6 +163,8 @@ def ccompile(self, name, check_duplicate_name=True):
166
163
if GRAALPYTHON :
167
164
file_not_empty (lib_file )
168
165
166
+ return str (install_dir )
167
+
169
168
170
169
def file_not_empty (path ):
171
170
for i in range (3 ):
@@ -342,13 +341,13 @@ def file_not_empty(path):
342
341
"""
343
342
344
343
345
- class CPyExtFunction () :
344
+ class CPyExtFunction :
346
345
347
346
def __init__ (self , pfunc , parameters , template = c_template , cmpfunc = None , stderr_validator = None , ** kwargs ):
348
347
self .template = template
349
348
self .pfunc = pfunc
350
349
self .parameters = parameters
351
- kwargs [ "name" ] = kwargs [ "name" ] if "name" in kwargs else None
350
+ kwargs . setdefault ( "name" , None )
352
351
self .name = kwargs ["name" ]
353
352
if "code" in kwargs :
354
353
kwargs ["customcode" ] = kwargs ["code" ]
@@ -403,12 +402,28 @@ def _insert(self, d, name, default_value):
403
402
def __repr__ (self ):
404
403
return "<CPyExtFunction %s>" % self .name
405
404
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 ):
408
422
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
412
427
ctest = getattr (cmodule , "test_%s" % self .name )
413
428
cargs = self .parameters ()
414
429
pargs = self .parameters ()
@@ -418,7 +433,7 @@ def test(self):
418
433
sys .stderr = StringIO ()
419
434
try :
420
435
cresult = ctest (cargs [i ])
421
- except BaseException as e :
436
+ except Exception as e :
422
437
cresult = e
423
438
else :
424
439
if self .stderr_validator :
@@ -432,20 +447,13 @@ def test(self):
432
447
except BaseException as e :
433
448
presult = e
434
449
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 )
437
452
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 \t Arguments: { pargs [i ]!r} \n \t Expected result: { presult !r} \n \t Actual C result: { cresult !r} "
439
455
gc .collect ()
440
456
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
-
449
457
450
458
class CPyExtFunctionOutVars (CPyExtFunction ):
451
459
'''
@@ -511,25 +519,18 @@ def get_value(self, key, args, kwds):
511
519
return Formatter .get_value (key , args , kwds )
512
520
513
521
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 ):
519
523
source_file = DIR / f'{ name } .c'
520
524
with open (source_file , "wb" , buffering = 0 ) as f :
521
525
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 )
531
532
try :
532
- cmodule = __import__ (name )
533
+ cmodule = __import__ (module_name )
533
534
finally :
534
535
sys .path .pop (0 )
535
536
return cmodule
@@ -570,7 +571,7 @@ def CPyExtType(name, code='', **kwargs):
570
571
kwargs .setdefault ("post_ready_code" , "" )
571
572
c_source = type_decl + UnseenFormatter ().format (mod_template , ** kwargs )
572
573
573
- cmodule = _compile_module (c_source , name )
574
+ cmodule = compile_module_from_string (c_source , name )
574
575
return getattr (cmodule , name )
575
576
576
577
@@ -787,7 +788,7 @@ def CPyExtHeapType(name, bases=(object), code='', slots=None, **kwargs):
787
788
kwargs .setdefault ("ready_code" , "" )
788
789
kwargs .setdefault ("post_ready_code" , "" )
789
790
code = UnseenFormatter ().format (template , ** kwargs )
790
- mod = _compile_module (code , name )
791
+ mod = compile_module_from_string (code , name )
791
792
return mod .create (bases )
792
793
793
794
0 commit comments