Commit a664bdef authored by Vitja Makarov's avatar Vitja Makarov

Merge remote branch 'upstream/master'

parents d5c80a3d 7cdfd770
...@@ -2021,6 +2021,13 @@ class IndexNode(ExprNode): ...@@ -2021,6 +2021,13 @@ class IndexNode(ExprNode):
def is_ephemeral(self): def is_ephemeral(self):
return self.base.is_ephemeral() return self.base.is_ephemeral()
def is_simple(self):
if self.is_buffer_access:
return False
base = self.base
return (base.is_simple() and self.index.is_simple()
and base.type and (base.type.is_ptr or base.type.is_array))
def analyse_target_declaration(self, env): def analyse_target_declaration(self, env):
pass pass
...@@ -2966,25 +2973,50 @@ class SimpleCallNode(CallNode): ...@@ -2966,25 +2973,50 @@ class SimpleCallNode(CallNode):
self.is_temp = 1 self.is_temp = 1
# Coerce arguments # Coerce arguments
some_args_in_temps = False some_args_in_temps = False
for i in range(min(max_nargs, actual_nargs)): for i in xrange(min(max_nargs, actual_nargs)):
formal_type = func_type.args[i].type formal_type = func_type.args[i].type
arg = self.args[i].coerce_to(formal_type, env).coerce_to_simple(env) arg = self.args[i].coerce_to(formal_type, env)
if arg.is_temp: if arg.is_temp:
if i > 0:
# first argument in temp doesn't impact subsequent arguments
some_args_in_temps = True some_args_in_temps = True
elif arg.type.is_pyobject and not env.nogil: elif arg.type.is_pyobject and not env.nogil:
if not arg.is_name or arg.entry and (not arg.entry.is_local or arg.entry.in_closure): if i == 0 and self.self is not None:
# a method's cloned "self" argument is ok
pass
elif arg.is_name and arg.entry and arg.entry.is_local and not arg.entry.in_closure:
# plain local variables are ok
pass
else:
# we do not safely own the argument's reference, # we do not safely own the argument's reference,
# but we must make sure it cannot be collected # but we must make sure it cannot be collected
# before we return from the function, so we create # before we return from the function, so we create
# an owned temp reference to it # an owned temp reference to it
if i > 0: # first argument doesn't matter
some_args_in_temps = True some_args_in_temps = True
arg = arg.coerce_to_temp(env) arg = arg.coerce_to_temp(env)
self.args[i] = arg self.args[i] = arg
# handle additional varargs parameters
for i in xrange(max_nargs, actual_nargs):
arg = self.args[i]
if arg.type.is_pyobject:
arg_ctype = arg.type.default_coerced_ctype()
if arg_ctype is None:
error(self.args[i].pos,
"Python object cannot be passed as a varargs parameter")
else:
self.args[i] = arg = arg.coerce_to(arg_ctype, env)
if arg.is_temp and i > 0:
some_args_in_temps = True
if some_args_in_temps: if some_args_in_temps:
# if some args are temps and others are not, they may get # if some args are temps and others are not, they may get
# constructed in the wrong order (temps first) => make # constructed in the wrong order (temps first) => make
# sure they are either all temps or all not temps # sure they are either all temps or all not temps (except
for i in range(min(max_nargs, actual_nargs)-1): # for the last argument, which is evaluated last in any
# case)
for i in xrange(actual_nargs-1):
if i == 0 and self.self is not None:
continue # self is ok
arg = self.args[i] arg = self.args[i]
if arg.is_name and arg.entry and ( if arg.is_name and arg.entry and (
(arg.entry.is_local and not arg.entry.in_closure) (arg.entry.is_local and not arg.entry.in_closure)
...@@ -2998,15 +3030,6 @@ class SimpleCallNode(CallNode): ...@@ -2998,15 +3030,6 @@ class SimpleCallNode(CallNode):
pass pass
else: else:
self.args[i] = arg.coerce_to_temp(env) self.args[i] = arg.coerce_to_temp(env)
for i in range(max_nargs, actual_nargs):
arg = self.args[i]
if arg.type.is_pyobject:
arg_ctype = arg.type.default_coerced_ctype()
if arg_ctype is None:
error(self.args[i].pos,
"Python object cannot be passed as a varargs parameter")
else:
self.args[i] = arg.coerce_to(arg_ctype, env)
# Calc result type and code fragment # Calc result type and code fragment
if isinstance(self.function, NewExprNode): if isinstance(self.function, NewExprNode):
self.type = PyrexTypes.CPtrType(self.function.class_type) self.type = PyrexTypes.CPtrType(self.function.class_type)
...@@ -5447,6 +5470,10 @@ class TypecastNode(ExprNode): ...@@ -5447,6 +5470,10 @@ class TypecastNode(ExprNode):
elif self.type.is_complex and self.operand.type.is_complex: elif self.type.is_complex and self.operand.type.is_complex:
self.operand = self.operand.coerce_to_simple(env) self.operand = self.operand.coerce_to_simple(env)
def is_simple(self):
# either temp or a C cast => no side effects
return True
def nogil_check(self, env): def nogil_check(self, env):
if self.type and self.type.is_pyobject and self.is_temp: if self.type and self.type.is_pyobject and self.is_temp:
self.gil_error() self.gil_error()
...@@ -7103,6 +7130,9 @@ class PyTypeTestNode(CoercionNode): ...@@ -7103,6 +7130,9 @@ class PyTypeTestNode(CoercionNode):
return False return False
return self.arg.may_be_none() return self.arg.may_be_none()
def is_simple(self):
return self.arg.is_simple()
def result_in_temp(self): def result_in_temp(self):
return self.arg.result_in_temp() return self.arg.result_in_temp()
...@@ -7153,6 +7183,9 @@ class NoneCheckNode(CoercionNode): ...@@ -7153,6 +7183,9 @@ class NoneCheckNode(CoercionNode):
def may_be_none(self): def may_be_none(self):
return False return False
def is_simple(self):
return self.arg.is_simple()
def result_in_temp(self): def result_in_temp(self):
return self.arg.result_in_temp() return self.arg.result_in_temp()
...@@ -7453,6 +7486,9 @@ class CloneNode(CoercionNode): ...@@ -7453,6 +7486,9 @@ class CloneNode(CoercionNode):
if hasattr(self.arg, 'entry'): if hasattr(self.arg, 'entry'):
self.entry = self.arg.entry self.entry = self.arg.entry
def is_simple(self):
return True # result is always in a temp (or a name)
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
pass pass
......
...@@ -607,9 +607,6 @@ class CFuncDeclaratorNode(CDeclaratorNode): ...@@ -607,9 +607,6 @@ class CFuncDeclaratorNode(CDeclaratorNode):
error(self.exception_value.pos, error(self.exception_value.pos,
"Exception value incompatible with function return type") "Exception value incompatible with function return type")
exc_check = self.exception_check exc_check = self.exception_check
if return_type.is_array:
error(self.pos,
"Function cannot return an array")
if return_type.is_cfunction: if return_type.is_cfunction:
error(self.pos, error(self.pos,
"Function cannot return a function") "Function cannot return a function")
...@@ -1658,6 +1655,9 @@ class CFuncDefNode(FuncDefNode): ...@@ -1658,6 +1655,9 @@ class CFuncDefNode(FuncDefNode):
api = self.api, modifiers = self.modifiers) api = self.api, modifiers = self.modifiers)
self.entry.inline_func_in_pxd = self.inline_in_pxd self.entry.inline_func_in_pxd = self.inline_in_pxd
self.return_type = type.return_type self.return_type = type.return_type
if self.return_type.is_array and visibility != 'extern':
error(self.pos,
"Function cannot return an array")
if self.overridable and not env.is_module_scope: if self.overridable and not env.is_module_scope:
if len(self.args) < 1 or not self.args[0].type.is_pyobject: if len(self.args) < 1 or not self.args[0].type.is_pyobject:
......
...@@ -1995,14 +1995,15 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1995,14 +1995,15 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
builtin_type = entry.type builtin_type = entry.type
if builtin_type and builtin_type is not Builtin.type_type: if builtin_type and builtin_type is not Builtin.type_type:
type_check_function = entry.type.type_check_function(exact=False) type_check_function = entry.type.type_check_function(exact=False)
if type_check_function in tests:
continue
tests.append(type_check_function)
type_check_args = [arg] type_check_args = [arg]
elif test_type_node.type is Builtin.type_type: elif test_type_node.type is Builtin.type_type:
type_check_function = '__Pyx_TypeCheck' type_check_function = '__Pyx_TypeCheck'
type_check_args = [arg, test_type_node] type_check_args = [arg, test_type_node]
else: else:
return node return node
if type_check_function not in tests:
tests.append(type_check_function)
test_nodes.append( test_nodes.append(
ExprNodes.PythonCapiCallNode( ExprNodes.PythonCapiCallNode(
test_type_node.pos, type_check_function, self.Py_type_check_func_type, test_type_node.pos, type_check_function, self.Py_type_check_func_type,
......
...@@ -29,6 +29,49 @@ with open(codefile) as f: ...@@ -29,6 +29,49 @@ with open(codefile) as f:
# can't access the module anymore. Get it from sys.modules instead. # can't access the module anymore. Get it from sys.modules instead.
build_ext = sys.modules['Cython.Distutils.build_ext'] build_ext = sys.modules['Cython.Distutils.build_ext']
have_gdb = None
def test_gdb():
global have_gdb
if have_gdb is None:
try:
p = subprocess.Popen(['gdb', '-v'], stdout=subprocess.PIPE)
have_gdb = True
except OSError:
# gdb was not installed
have_gdb = False
else:
gdb_version = p.stdout.read().decode('ascii')
p.wait()
p.stdout.close()
if have_gdb:
# Based on Lib/test/test_gdb.py
regex = "^GNU gdb [^\d]*(\d+)\.(\d+)"
gdb_version_number = list(map(int, re.search(regex, gdb_version).groups()))
if gdb_version_number >= [7, 2]:
python_version_script = tempfile.NamedTemporaryFile(mode='w+')
python_version_script.write(
'python import sys; print("%s %s" % sys.version_info[:2])')
python_version_script.flush()
p = subprocess.Popen(['gdb', '-batch', '-x', python_version_script.name],
stdout=subprocess.PIPE)
python_version = p.stdout.read().decode('ascii')
p.wait()
python_version_number = list(map(int, python_version.split()))
# Be Python 3 compatible
if (not have_gdb
or gdb_version_number < [7, 2]
or python_version_number < [2, 6]):
warnings.warn(
'Skipping gdb tests, need gdb >= 7.2 with Python >= 2.6')
have_gdb = False
return have_gdb
class DebuggerTestCase(unittest.TestCase): class DebuggerTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
...@@ -36,6 +79,9 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -36,6 +79,9 @@ class DebuggerTestCase(unittest.TestCase):
Run gdb and have cygdb import the debug information from the code Run gdb and have cygdb import the debug information from the code
defined in TestParseTreeTransforms's setUp method defined in TestParseTreeTransforms's setUp method
""" """
if not test_gdb():
return
self.tempdir = tempfile.mkdtemp() self.tempdir = tempfile.mkdtemp()
self.destfile = os.path.join(self.tempdir, 'codefile.pyx') self.destfile = os.path.join(self.tempdir, 'codefile.pyx')
self.debug_dest = os.path.join(self.tempdir, self.debug_dest = os.path.join(self.tempdir,
...@@ -44,6 +90,7 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -44,6 +90,7 @@ class DebuggerTestCase(unittest.TestCase):
self.cfuncs_destfile = os.path.join(self.tempdir, 'cfuncs') self.cfuncs_destfile = os.path.join(self.tempdir, 'cfuncs')
self.cwd = os.getcwd() self.cwd = os.getcwd()
try:
os.chdir(self.tempdir) os.chdir(self.tempdir)
shutil.copy(codefile, self.destfile) shutil.copy(codefile, self.destfile)
...@@ -58,7 +105,6 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -58,7 +105,6 @@ class DebuggerTestCase(unittest.TestCase):
) )
optimization_disabler = build_ext.Optimization() optimization_disabler = build_ext.Optimization()
optimization_disabler.disable_optimization()
cython_compile_testcase = runtests.CythonCompileTestCase( cython_compile_testcase = runtests.CythonCompileTestCase(
workdir=self.tempdir, workdir=self.tempdir,
...@@ -67,6 +113,14 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -67,6 +113,14 @@ class DebuggerTestCase(unittest.TestCase):
**opts **opts
) )
new_stderr = open(os.devnull, 'w')
stderr = sys.stderr
sys.stderr = new_stderr
optimization_disabler.disable_optimization()
try:
cython_compile_testcase.run_cython( cython_compile_testcase.run_cython(
targetdir=self.tempdir, targetdir=self.tempdir,
incdir=None, incdir=None,
...@@ -84,8 +138,9 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -84,8 +138,9 @@ class DebuggerTestCase(unittest.TestCase):
extra_extension_args={'extra_objects':['cfuncs.o']}, extra_extension_args={'extra_objects':['cfuncs.o']},
**opts **opts
) )
finally:
optimization_disabler.restore_state() optimization_disabler.restore_state()
sys.stderr = stderr
# ext = Cython.Distutils.extension.Extension( # ext = Cython.Distutils.extension.Extension(
# 'codefile', # 'codefile',
...@@ -99,7 +154,13 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -99,7 +154,13 @@ class DebuggerTestCase(unittest.TestCase):
# cmdclass=dict(build_ext=Cython.Distutils.build_ext) # cmdclass=dict(build_ext=Cython.Distutils.build_ext)
# ) # )
except:
os.chdir(self.cwd)
raise
def tearDown(self): def tearDown(self):
if not test_gdb():
return
os.chdir(self.cwd) os.chdir(self.cwd)
shutil.rmtree(self.tempdir) shutil.rmtree(self.tempdir)
...@@ -107,6 +168,9 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -107,6 +168,9 @@ class DebuggerTestCase(unittest.TestCase):
class GdbDebuggerTestCase(DebuggerTestCase): class GdbDebuggerTestCase(DebuggerTestCase):
def setUp(self): def setUp(self):
if not test_gdb():
return
super(GdbDebuggerTestCase, self).setUp() super(GdbDebuggerTestCase, self).setUp()
prefix_code = textwrap.dedent('''\ prefix_code = textwrap.dedent('''\
...@@ -167,6 +231,11 @@ class GdbDebuggerTestCase(DebuggerTestCase): ...@@ -167,6 +231,11 @@ class GdbDebuggerTestCase(DebuggerTestCase):
p.stdout.close() p.stdout.close()
if have_gdb: if have_gdb:
# Based on Lib/test/test_gdb.py
regex = "^GNU gdb [^\d]*(\d+)\.(\d+)"
gdb_version_number = list(map(int, re.search(regex, gdb_version).groups()))
if gdb_version_number >= [7, 2]:
python_version_script = tempfile.NamedTemporaryFile(mode='w+') python_version_script = tempfile.NamedTemporaryFile(mode='w+')
python_version_script.write( python_version_script.write(
'python import sys; print("%s %s" % sys.version_info[:2])') 'python import sys; print("%s %s" % sys.version_info[:2])')
...@@ -175,16 +244,12 @@ class GdbDebuggerTestCase(DebuggerTestCase): ...@@ -175,16 +244,12 @@ class GdbDebuggerTestCase(DebuggerTestCase):
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
python_version = p.stdout.read().decode('ascii') python_version = p.stdout.read().decode('ascii')
p.wait() p.wait()
python_version_number = [int(a) for a in python_version.split()] python_version_number = list(map(int, python_version.split()))
if have_gdb:
# Based on Lib/test/test_gdb.py
regex = "^GNU gdb [^\d]*(\d+)\.(\d+)"
gdb_version_number = re.search(regex, gdb_version).groups()
# Be Python 3 compatible # Be Python 3 compatible
if (not have_gdb if (not have_gdb
or list(map(int, gdb_version_number)) < [7, 2] or gdb_version_number < [7, 2]
or python_version_number < [2, 6]): or python_version_number < [2, 6]):
self.p = None self.p = None
warnings.warn( warnings.warn(
...@@ -197,6 +262,9 @@ class GdbDebuggerTestCase(DebuggerTestCase): ...@@ -197,6 +262,9 @@ class GdbDebuggerTestCase(DebuggerTestCase):
env=env) env=env)
def tearDown(self): def tearDown(self):
if not test_gdb():
return
super(GdbDebuggerTestCase, self).tearDown() super(GdbDebuggerTestCase, self).tearDown()
if self.p: if self.p:
self.p.stderr.close() self.p.stderr.close()
...@@ -207,17 +275,24 @@ class GdbDebuggerTestCase(DebuggerTestCase): ...@@ -207,17 +275,24 @@ class GdbDebuggerTestCase(DebuggerTestCase):
class TestAll(GdbDebuggerTestCase): class TestAll(GdbDebuggerTestCase):
def test_all(self): def test_all(self):
if self.p is None: if not test_gdb():
return return
out, err = self.p.communicate() out, err = self.p.communicate()
err = err.decode('UTF-8') err = err.decode('UTF-8')
exit_status = self.p.wait()
if exit_status == 1:
sys.stderr.write(err)
elif exit_status >= 2:
border = '*' * 30 border = '*' * 30
start = '%s v INSIDE GDB v %s' % (border, border) start = '%s v INSIDE GDB v %s' % (border, border)
end = '%s ^ INSIDE GDB ^ %s' % (border, border) end = '%s ^ INSIDE GDB ^ %s' % (border, border)
errmsg = '\n%s\n%s%s' % (start, err, end) errmsg = '\n%s\n%s%s' % (start, err, end)
self.assertEquals(0, self.p.wait(), errmsg)
sys.stderr.write(err) sys.stderr.write(errmsg)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -428,7 +428,7 @@ def run_unittest_in_module(modulename): ...@@ -428,7 +428,7 @@ def run_unittest_in_module(modulename):
"debugging information. Either compile python with " "debugging information. Either compile python with "
"-g or get a debug build (configure with --with-pydebug).") "-g or get a debug build (configure with --with-pydebug).")
warnings.warn(msg) warnings.warn(msg)
os._exit(0) os._exit(1)
else: else:
m = __import__(modulename, fromlist=['']) m = __import__(modulename, fromlist=[''])
tests = inspect.getmembers(m, inspect.isclass) tests = inspect.getmembers(m, inspect.isclass)
...@@ -453,7 +453,7 @@ def runtests(): ...@@ -453,7 +453,7 @@ def runtests():
success_libpython = run_unittest_in_module(test_libpython_in_gdb.__name__) success_libpython = run_unittest_in_module(test_libpython_in_gdb.__name__)
if not success_libcython or not success_libpython: if not success_libcython or not success_libpython:
sys.exit(1) sys.exit(2)
def main(version, trace_code=False): def main(version, trace_code=False):
global inferior_python_version global inferior_python_version
......
...@@ -194,7 +194,7 @@ class _XMLTestResult(_TextTestResult): ...@@ -194,7 +194,7 @@ class _XMLTestResult(_TextTestResult):
module = '' module = ''
testcase_name = module + testcase.__name__ testcase_name = module + testcase.__name__
if not tests_by_testcase.has_key(testcase_name): if testcase_name not in tests_by_testcase:
tests_by_testcase[testcase_name] = [] tests_by_testcase[testcase_name] = []
tests_by_testcase[testcase_name].append(test_info) tests_by_testcase[testcase_name].append(test_info)
......
__version__ = "0.14.1rc2" __version__ = "0.14.1rc3"
# Void cython.* directives (for case insensitive operating systems). # Void cython.* directives (for case insensitive operating systems).
from Cython.Shadow import * from Cython.Shadow import *
...@@ -26,6 +26,18 @@ try: ...@@ -26,6 +26,18 @@ try:
except ImportError: # No threads, no problems except ImportError: # No threads, no problems
threading = None threading = None
if sys.platform == 'win32':
# TODO: Figure out why this hackery (see http://thread.gmane.org/gmane.comp.python.cython.devel/8280/).
config_files = distutils_distro.find_config_files()
try: config_files.remove('setup.cfg')
except ValueError: pass
distutils_distro.parse_config_files(config_files)
cfgfiles = distutils_distro.find_config_files()
try: cfgfiles.remove('setup.cfg')
except ValueError: pass
distutils_distro.parse_config_files(cfgfiles)
WITH_CYTHON = True WITH_CYTHON = True
...@@ -981,6 +993,9 @@ def check_thread_termination(ignore_seen=True): ...@@ -981,6 +993,9 @@ def check_thread_termination(ignore_seen=True):
raise PendingThreadsError("left-over threads found after running test") raise PendingThreadsError("left-over threads found after running test")
def main(): def main():
DISTDIR = os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]))
from optparse import OptionParser from optparse import OptionParser
parser = OptionParser() parser = OptionParser()
parser.add_option("--no-cleanup", dest="cleanup_workdir", parser.add_option("--no-cleanup", dest="cleanup_workdir",
...@@ -1051,12 +1066,15 @@ def main(): ...@@ -1051,12 +1066,15 @@ def main():
parser.add_option("--exit-ok", dest="exit_ok", default=False, parser.add_option("--exit-ok", dest="exit_ok", default=False,
action="store_true", action="store_true",
help="exit without error code even on test failures") help="exit without error code even on test failures")
parser.add_option("--root-dir", dest="root_dir", default=os.path.join(DISTDIR, 'tests'),
help="working directory")
parser.add_option("--work-dir", dest="work_dir", default=os.path.join(os.getcwd(), 'BUILD'),
help="working directory")
options, cmd_args = parser.parse_args() options, cmd_args = parser.parse_args()
DISTDIR = os.path.join(os.getcwd(), os.path.dirname(sys.argv[0])) ROOTDIR = os.path.abspath(options.root_dir)
ROOTDIR = os.path.join(DISTDIR, 'tests') WORKDIR = os.path.abspath(options.work_dir)
WORKDIR = os.path.join(os.getcwd(), 'BUILD')
if sys.version_info[0] >= 3: if sys.version_info[0] >= 3:
options.doctests = False options.doctests = False
...@@ -1167,7 +1185,7 @@ def main(): ...@@ -1167,7 +1185,7 @@ def main():
exclude_selectors += [ re.compile(r, re.I|re.U).search for r in options.exclude ] exclude_selectors += [ re.compile(r, re.I|re.U).search for r in options.exclude ]
if not test_bugs: if not test_bugs:
exclude_selectors += [ FileListExcluder("tests/bugs.txt") ] exclude_selectors += [ FileListExcluder(os.path.join(ROOTDIR, "bugs.txt")) ]
if sys.platform in ['win32', 'cygwin'] and sys.version_info < (2,6): if sys.platform in ['win32', 'cygwin'] and sys.version_info < (2,6):
exclude_selectors += [ lambda x: x == "run.specialfloat" ] exclude_selectors += [ lambda x: x == "run.specialfloat" ]
......
...@@ -77,6 +77,23 @@ def test_custom(): ...@@ -77,6 +77,23 @@ def test_custom():
assert isinstance(A(), A) assert isinstance(A(), A)
return True return True
cdef class B:
pass
cdef class C:
pass
def test_custom_tuple(obj):
"""
>>> test_custom_tuple(A())
True
>>> test_custom_tuple(B())
True
>>> test_custom_tuple(C())
False
"""
return isinstance(obj, (A,B))
def test_nested(x): def test_nested(x):
""" """
>>> test_nested(1) >>> test_nested(1)
......
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