Commit b15b4c3f authored by Robert Bradshaw's avatar Robert Bradshaw

Add preparser tag to test runner.

This allows one to avoid duplicating code, motivated in particular
by the many closure tests.
parent 6a65938a
...@@ -115,6 +115,77 @@ def unpatch_inspect_isfunction(): ...@@ -115,6 +115,77 @@ def unpatch_inspect_isfunction():
else: else:
inspect.isfunction = orig_isfunction inspect.isfunction = orig_isfunction
def def_to_cdef(source):
'''
Converts the module-level def methods into cdef methods, i.e.
@decorator
def foo([args]):
"""
[tests]
"""
[body]
becomes
def foo([args]):
"""
[tests]
"""
return foo_c([args])
cdef foo_c([args]):
[body]
'''
output = []
skip = False
def_node = re.compile(r'def (\w+)\(([^()*]*)\):').match
lines = iter(source.split('\n'))
for line in lines:
if not line.strip():
output.append(line)
continue
if skip:
if line[0] != ' ':
skip = False
else:
continue
if line[0] == '@':
skip = True
continue
m = def_node(line)
if m:
name = m.group(1)
args = m.group(2)
if args:
args_no_types = ", ".join(arg.split()[-1] for arg in args.split(','))
else:
args_no_types = ""
output.append("def %s(%s):" % (name, args_no_types))
line = lines.next()
if '"""' in line:
has_docstring = True
output.append(line)
for line in lines:
output.append(line)
if '"""' in line:
break
else:
has_docstring = False
output.append(" return %s_c(%s)" % (name, args_no_types))
output.append('')
output.append("cdef %s_c(%s):" % (name, args))
if not has_docstring:
output.append(line)
else:
output.append(line)
return '\n'.join(output)
def update_linetrace_extension(ext): def update_linetrace_extension(ext):
ext.define_macros.append(('CYTHON_TRACE', 1)) ext.define_macros.append(('CYTHON_TRACE', 1))
return ext return ext
...@@ -331,7 +402,7 @@ def parse_tags(filepath): ...@@ -331,7 +402,7 @@ def parse_tags(filepath):
if tag == 'tags': if tag == 'tags':
tag = 'tag' tag = 'tag'
print("WARNING: test tags use the 'tag' directive, not 'tags' (%s)" % filepath) print("WARNING: test tags use the 'tag' directive, not 'tags' (%s)" % filepath)
if tag not in ('mode', 'tag', 'ticket', 'cython', 'distutils'): if tag not in ('mode', 'tag', 'ticket', 'cython', 'distutils', 'preparse'):
print("WARNING: unknown test directive '%s' found (%s)" % (tag, filepath)) print("WARNING: unknown test directive '%s' found (%s)" % (tag, filepath))
values = values.split(',') values = values.split(',')
tags[tag].extend(filter(None, [value.strip() for value in values])) tags[tag].extend(filter(None, [value.strip() for value in values]))
...@@ -532,19 +603,25 @@ class TestBuilder(object): ...@@ -532,19 +603,25 @@ class TestBuilder(object):
elif 'no-cpp' in tags['tag'] and 'cpp' in self.languages: elif 'no-cpp' in tags['tag'] and 'cpp' in self.languages:
languages = list(languages) languages = list(languages)
languages.remove('cpp') languages.remove('cpp')
preparse_list = tags.get('preparse', ['id'])
tests = [ self.build_test(test_class, path, workdir, module, tags, tests = [ self.build_test(test_class, path, workdir, module, tags,
language, expect_errors, warning_errors) language, expect_errors, warning_errors, preparse)
for language in languages ] for language in languages
for preparse in preparse_list ]
return tests return tests
def build_test(self, test_class, path, workdir, module, tags, def build_test(self, test_class, path, workdir, module, tags,
language, expect_errors, warning_errors): language, expect_errors, warning_errors, preparse):
language_workdir = os.path.join(workdir, language) language_workdir = os.path.join(workdir, language)
if not os.path.exists(language_workdir): if not os.path.exists(language_workdir):
os.makedirs(language_workdir) os.makedirs(language_workdir)
workdir = os.path.join(language_workdir, module) workdir = os.path.join(language_workdir, module)
if preparse != 'id':
workdir += '_%s' % str(preparse)
return test_class(path, workdir, module, tags, return test_class(path, workdir, module, tags,
language=language, language=language,
preparse=preparse,
expect_errors=expect_errors, expect_errors=expect_errors,
annotate=self.annotate, annotate=self.annotate,
cleanup_workdir=self.cleanup_workdir, cleanup_workdir=self.cleanup_workdir,
...@@ -556,7 +633,7 @@ class TestBuilder(object): ...@@ -556,7 +633,7 @@ class TestBuilder(object):
warning_errors=warning_errors) warning_errors=warning_errors)
class CythonCompileTestCase(unittest.TestCase): class CythonCompileTestCase(unittest.TestCase):
def __init__(self, test_directory, workdir, module, tags, language='c', def __init__(self, test_directory, workdir, module, tags, language='c', preparse='id',
expect_errors=False, annotate=False, cleanup_workdir=True, expect_errors=False, annotate=False, cleanup_workdir=True,
cleanup_sharedlibs=True, cleanup_failures=True, cython_only=False, cleanup_sharedlibs=True, cleanup_failures=True, cython_only=False,
fork=True, language_level=2, warning_errors=False): fork=True, language_level=2, warning_errors=False):
...@@ -565,6 +642,8 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -565,6 +642,8 @@ class CythonCompileTestCase(unittest.TestCase):
self.workdir = workdir self.workdir = workdir
self.module = module self.module = module
self.language = language self.language = language
self.preparse = preparse
self.name = module if self.preparse == "id" else "%s_%s" % (module, preparse)
self.expect_errors = expect_errors self.expect_errors = expect_errors
self.annotate = annotate self.annotate = annotate
self.cleanup_workdir = cleanup_workdir self.cleanup_workdir = cleanup_workdir
...@@ -577,7 +656,7 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -577,7 +656,7 @@ class CythonCompileTestCase(unittest.TestCase):
unittest.TestCase.__init__(self) unittest.TestCase.__init__(self)
def shortDescription(self): def shortDescription(self):
return "compiling (%s) %s" % (self.language, self.module) return "compiling (%s) %s" % (self.language, self.name)
def setUp(self): def setUp(self):
from Cython.Compiler import Options from Cython.Compiler import Options
...@@ -660,11 +739,16 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -660,11 +739,16 @@ class CythonCompileTestCase(unittest.TestCase):
if is_related(filename)] if is_related(filename)]
def copy_files(self, test_directory, target_directory, file_list): def copy_files(self, test_directory, target_directory, file_list):
# use symlink on Unix, copy on Windows if self.preparse and self.preparse != 'id':
try: preparse_func = globals()[self.preparse]
copy = os.symlink def copy(src, dest):
except AttributeError: open(dest, 'wb').write(preparse_func(open(src).read()))
copy = shutil.copy else:
# use symlink on Unix, copy on Windows
try:
copy = os.symlink
except AttributeError:
copy = shutil.copy
join = os.path.join join = os.path.join
for filename in file_list: for filename in file_list:
...@@ -707,6 +791,12 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -707,6 +791,12 @@ class CythonCompileTestCase(unittest.TestCase):
include_dirs.append(incdir) include_dirs.append(incdir)
source = self.find_module_source_file( source = self.find_module_source_file(
os.path.join(test_directory, module + '.pyx')) os.path.join(test_directory, module + '.pyx'))
if self.preparse == 'id':
source = self.find_module_source_file(
os.path.join(test_directory, module + '.pyx'))
else:
self.copy_files(test_directory, targetdir, [module + '.pyx'])
source = os.path.join(targetdir, module + '.pyx')
target = os.path.join(targetdir, self.build_target_filename(module)) target = os.path.join(targetdir, self.build_target_filename(module))
if extra_compile_options is None: if extra_compile_options is None:
...@@ -903,7 +993,7 @@ class CythonRunTestCase(CythonCompileTestCase): ...@@ -903,7 +993,7 @@ class CythonRunTestCase(CythonCompileTestCase):
if self.cython_only: if self.cython_only:
return CythonCompileTestCase.shortDescription(self) return CythonCompileTestCase.shortDescription(self)
else: else:
return "compiling (%s) and running %s" % (self.language, self.module) return "compiling (%s) and running %s" % (self.language, self.name)
def run(self, result=None): def run(self, result=None):
if result is None: if result is None:
...@@ -1105,7 +1195,7 @@ class PartialTestResult(_TextTestResult): ...@@ -1105,7 +1195,7 @@ class PartialTestResult(_TextTestResult):
class CythonUnitTestCase(CythonRunTestCase): class CythonUnitTestCase(CythonRunTestCase):
def shortDescription(self): def shortDescription(self):
return "compiling (%s) tests in %s" % (self.language, self.module) return "compiling (%s) tests in %s" % (self.language, self.name)
def run_tests(self, result, ext_so_path): def run_tests(self, result, ext_so_path):
module = import_ext(self.module, ext_so_path) module = import_ext(self.module, ext_so_path)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment