Commit 0edf4568 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'master' into 0.20.x

Conflicts:
	CHANGES.rst
parents 5e08f83c e40d032f
...@@ -6,7 +6,6 @@ Cython Changelog ...@@ -6,7 +6,6 @@ Cython Changelog
Latest Latest
======= =======
Features added Features added
-------------- --------------
...@@ -16,6 +15,12 @@ Bugs fixed ...@@ -16,6 +15,12 @@ Bugs fixed
* List/Tuple literals multiplied by more than one factor were only multiplied * List/Tuple literals multiplied by more than one factor were only multiplied
by the last factor instead of all. by the last factor instead of all.
* Lookups of special methods (specifically for context managers) could fail
in Python <= 2.6/3.1.
* Local variables were erroneously appended to the signature introspection
of Cython implemented functions with keyword-only arguments under Python 3.
* In-place assignments to variables with inferred Python builtin/extension * In-place assignments to variables with inferred Python builtin/extension
types could fail with type errors if the result value type was incompatible types could fail with type errors if the result value type was incompatible
with the type of the previous value. with the type of the previous value.
......
...@@ -91,7 +91,14 @@ def file_hash(filename): ...@@ -91,7 +91,14 @@ def file_hash(filename):
path = os.path.normpath(filename.encode("UTF-8")) path = os.path.normpath(filename.encode("UTF-8"))
m = hashlib.md5(str(len(path)) + ":") m = hashlib.md5(str(len(path)) + ":")
m.update(path) m.update(path)
m.update(open(filename).read()) f = open(filename, 'rb')
try:
data = f.read(65000)
while data:
m.update(data)
data = f.read(65000)
finally:
f.close()
return m.hexdigest() return m.hexdigest()
def parse_list(s): def parse_list(s):
...@@ -671,7 +678,7 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo ...@@ -671,7 +678,7 @@ def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, fo
options['include_path'] = ['.'] options['include_path'] = ['.']
if 'common_utility_include_dir' in options: if 'common_utility_include_dir' in options:
if options.get('cache'): if options.get('cache'):
raise NotImplementedError, "common_utility_include_dir does not yet work with caching" raise NotImplementedError("common_utility_include_dir does not yet work with caching")
if not os.path.exists(options['common_utility_include_dir']): if not os.path.exists(options['common_utility_include_dir']):
os.makedirs(options['common_utility_include_dir']) os.makedirs(options['common_utility_include_dir'])
c_options = CompilationOptions(**options) c_options = CompilationOptions(**options)
......
...@@ -267,12 +267,12 @@ except ImportError: ...@@ -267,12 +267,12 @@ except ImportError:
for name, value in kwd_values.items(): for name, value in kwd_values.items():
if name in args: if name in args:
if name in all: if name in all:
raise TypeError, "Duplicate argument %s" % name raise TypeError("Duplicate argument %s" % name)
all[name] = kwd_values.pop(name) all[name] = kwd_values.pop(name)
if kwds is not None: if kwds is not None:
all[kwds] = kwd_values all[kwds] = kwd_values
elif kwd_values: elif kwd_values:
raise TypeError, "Unexpected keyword arguments: %s" % kwd_values.keys() raise TypeError("Unexpected keyword arguments: %s" % kwd_values.keys())
if defaults is None: if defaults is None:
defaults = () defaults = ()
first_default = len(args) - len(defaults) first_default = len(args) - len(defaults)
...@@ -281,7 +281,7 @@ except ImportError: ...@@ -281,7 +281,7 @@ except ImportError:
if ix >= first_default: if ix >= first_default:
all[name] = defaults[ix - first_default] all[name] = defaults[ix - first_default]
else: else:
raise TypeError, "Missing argument: %s" % name raise TypeError("Missing argument: %s" % name)
return all return all
def get_body(source): def get_body(source):
......
...@@ -17,7 +17,6 @@ special_chars = [ ...@@ -17,7 +17,6 @@ special_chars = [
(u'>', u'\xF1', u'&gt;'), (u'>', u'\xF1', u'&gt;'),
] ]
line_pos_comment = re.compile(r'/\*.*?<<<<<<<<<<<<<<.*?\*/\n*', re.DOTALL)
class AnnotationCCodeWriter(CCodeWriter): class AnnotationCCodeWriter(CCodeWriter):
...@@ -141,6 +140,7 @@ function toggleDiv(id) { ...@@ -141,6 +140,7 @@ function toggleDiv(id) {
return ur"<span class='%s'>%s</span>" % ( return ur"<span class='%s'>%s</span>" % (
group_name, match.group(group_name)) group_name, match.group(group_name))
pos_comment_marker = u'/* \N{HORIZONTAL ELLIPSIS} */\n'
k = 0 k = 0
code_source_file = self.code.get(source_filename, {}) code_source_file = self.code.get(source_filename, {})
for line in lines: for line in lines:
...@@ -150,6 +150,9 @@ function toggleDiv(id) { ...@@ -150,6 +150,9 @@ function toggleDiv(id) {
except KeyError: except KeyError:
code = '' code = ''
else: else:
code = _replace_pos_comment(pos_comment_marker, code)
if code.startswith(pos_comment_marker):
code = code[len(pos_comment_marker):]
code = html_escape(code) code = html_escape(code)
calls = zero_calls.copy() calls = zero_calls.copy()
...@@ -165,7 +168,6 @@ function toggleDiv(id) { ...@@ -165,7 +168,6 @@ function toggleDiv(id) {
f.write(line.rstrip()) f.write(line.rstrip())
f.write(u'</pre>\n') f.write(u'</pre>\n')
code = re.sub(line_pos_comment, '', code) # inline annotations are redundant
f.write(u"<pre id='line%s' class='code' style='background-color: #%s'>%s</pre>" % (k, color, code)) f.write(u"<pre id='line%s' class='code' style='background-color: #%s'>%s</pre>" % (k, color, code))
f.write(u'</body></html>\n') f.write(u'</body></html>\n')
f.close() f.close()
...@@ -183,6 +185,13 @@ _parse_code = re.compile( ...@@ -183,6 +185,13 @@ _parse_code = re.compile(
).sub ).sub
_replace_pos_comment = re.compile(
# this matches what Cython generates as code line marker comment
ur'^\s*/\*(?:(?:[^*]|\*[^/])*\n)+\s*\*/\s*\n',
re.M
).sub
class AnnotationItem(object): class AnnotationItem(object):
def __init__(self, style, text, tag="", size=0): def __init__(self, style, text, tag="", size=0):
......
...@@ -249,7 +249,13 @@ class UtilityCodeBase(object): ...@@ -249,7 +249,13 @@ class UtilityCodeBase(object):
continue continue
# only pass lists when we have to: most argument expect one value or None # only pass lists when we have to: most argument expect one value or None
if name == 'requires': if name == 'requires':
values = [ cls.load(dep, from_file, **orig_kwargs) for dep in values ] if orig_kwargs:
values = [cls.load(dep, from_file, **orig_kwargs)
for dep in sorted(values)]
else:
# dependencies are rarely unique, so use load_cached() when we can
values = [cls.load_cached(dep, from_file)
for dep in sorted(values)]
elif not values: elif not values:
values = None values = None
elif len(values) == 1: elif len(values) == 1:
...@@ -269,16 +275,16 @@ class UtilityCodeBase(object): ...@@ -269,16 +275,16 @@ class UtilityCodeBase(object):
return cls(**kwargs) return cls(**kwargs)
@classmethod @classmethod
def load_cached(cls, utility_code_name, from_file=None, _cache={}): def load_cached(cls, utility_code_name, from_file=None, __cache={}):
""" """
Calls .load(), but using a per-type cache based on utility name and file name. Calls .load(), but using a per-type cache based on utility name and file name.
""" """
key = (cls, from_file, utility_code_name) key = (cls, from_file, utility_code_name)
try: try:
return _cache[key] return __cache[key]
except KeyError: except KeyError:
pass pass
code = _cache[key] = cls.load(utility_code_name, from_file) code = __cache[key] = cls.load(utility_code_name, from_file)
return code return code
@classmethod @classmethod
......
...@@ -159,7 +159,7 @@ def report_error(err): ...@@ -159,7 +159,7 @@ def report_error(err):
echo_file.write(line.encode('ASCII', 'replace')) echo_file.write(line.encode('ASCII', 'replace'))
num_errors = num_errors + 1 num_errors = num_errors + 1
if Options.fast_fail: if Options.fast_fail:
raise AbortError, "fatal errors" raise AbortError("fatal errors")
def error(position, message): def error(position, message):
#print "Errors.error:", repr(position), repr(message) ### #print "Errors.error:", repr(position), repr(message) ###
......
...@@ -1690,7 +1690,7 @@ class NameNode(AtomicExprNode): ...@@ -1690,7 +1690,7 @@ class NameNode(AtomicExprNode):
return self return self
def analyse_target_types(self, env): def analyse_target_types(self, env):
self.analyse_entry(env) self.analyse_entry(env, is_target=True)
if (not self.is_lvalue() and self.entry.is_cfunction and if (not self.is_lvalue() and self.entry.is_cfunction and
self.entry.fused_cfunction and self.entry.as_variable): self.entry.fused_cfunction and self.entry.as_variable):
...@@ -1750,12 +1750,12 @@ class NameNode(AtomicExprNode): ...@@ -1750,12 +1750,12 @@ class NameNode(AtomicExprNode):
gil_message = "Accessing Python global or builtin" gil_message = "Accessing Python global or builtin"
def analyse_entry(self, env): def analyse_entry(self, env, is_target=False):
#print "NameNode.analyse_entry:", self.name ### #print "NameNode.analyse_entry:", self.name ###
self.check_identifier_kind() self.check_identifier_kind()
entry = self.entry entry = self.entry
type = entry.type type = entry.type
if (type.is_pyobject and self.inferred_type and if (not is_target and type.is_pyobject and self.inferred_type and
self.inferred_type.is_builtin_type): self.inferred_type.is_builtin_type):
# assume that type inference is smarter than the static entry # assume that type inference is smarter than the static entry
type = self.inferred_type type = self.inferred_type
...@@ -2536,7 +2536,9 @@ class WithExitCallNode(ExprNode): ...@@ -2536,7 +2536,9 @@ class WithExitCallNode(ExprNode):
result_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False) result_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False)
code.mark_pos(self.pos) code.mark_pos(self.pos)
code.putln("%s = PyObject_Call(%s, %s, NULL);" % ( code.globalstate.use_utility_code(UtilityCode.load_cached(
"PyObjectCall", "ObjectHandling.c"))
code.putln("%s = __Pyx_PyObject_Call(%s, %s, NULL);" % (
result_var, result_var,
self.with_stat.exit_var, self.with_stat.exit_var,
self.args.result())) self.args.result()))
...@@ -4657,8 +4659,10 @@ class SimpleCallNode(CallNode): ...@@ -4657,8 +4659,10 @@ class SimpleCallNode(CallNode):
code.globalstate.use_utility_code(self.function.entry.utility_code) code.globalstate.use_utility_code(self.function.entry.utility_code)
if func_type.is_pyobject: if func_type.is_pyobject:
arg_code = self.arg_tuple.py_result() arg_code = self.arg_tuple.py_result()
code.globalstate.use_utility_code(UtilityCode.load_cached(
"PyObjectCall", "ObjectHandling.c"))
code.putln( code.putln(
"%s = PyObject_Call(%s, %s, NULL); %s" % ( "%s = __Pyx_PyObject_Call(%s, %s, NULL); %s" % (
self.result(), self.result(),
self.function.py_result(), self.function.py_result(),
arg_code, arg_code,
...@@ -5087,8 +5091,10 @@ class GeneralCallNode(CallNode): ...@@ -5087,8 +5091,10 @@ class GeneralCallNode(CallNode):
kwargs = self.keyword_args.py_result() kwargs = self.keyword_args.py_result()
else: else:
kwargs = 'NULL' kwargs = 'NULL'
code.globalstate.use_utility_code(UtilityCode.load_cached(
"PyObjectCall", "ObjectHandling.c"))
code.putln( code.putln(
"%s = PyObject_Call(%s, %s, %s); %s" % ( "%s = __Pyx_PyObject_Call(%s, %s, %s); %s" % (
self.result(), self.result(),
self.function.py_result(), self.function.py_result(),
self.positional_args.py_result(), self.positional_args.py_result(),
...@@ -7604,18 +7610,14 @@ class CodeObjectNode(ExprNode): ...@@ -7604,18 +7610,14 @@ class CodeObjectNode(ExprNode):
def __init__(self, def_node): def __init__(self, def_node):
ExprNode.__init__(self, def_node.pos, def_node=def_node) ExprNode.__init__(self, def_node.pos, def_node=def_node)
args = list(def_node.args) args = list(def_node.args)
if def_node.star_arg: # if we have args/kwargs, then the first two in var_entries are those
args.append(def_node.star_arg) local_vars = [arg for arg in def_node.local_scope.var_entries if arg.name]
if def_node.starstar_arg:
args.append(def_node.starstar_arg)
local_vars = [ arg for arg in def_node.local_scope.var_entries
if arg.name ]
self.varnames = TupleNode( self.varnames = TupleNode(
def_node.pos, def_node.pos,
args = [ IdentifierStringNode(arg.pos, value=arg.name) args=[IdentifierStringNode(arg.pos, value=arg.name)
for arg in args + local_vars ], for arg in args + local_vars],
is_temp = 0, is_temp=0,
is_literal = 1) is_literal=1)
def may_be_none(self): def may_be_none(self):
return False return False
...@@ -7635,11 +7637,18 @@ class CodeObjectNode(ExprNode): ...@@ -7635,11 +7637,18 @@ class CodeObjectNode(ExprNode):
file_path = StringEncoding.BytesLiteral(func.pos[0].get_filenametable_entry().encode('utf8')) file_path = StringEncoding.BytesLiteral(func.pos[0].get_filenametable_entry().encode('utf8'))
file_path_const = code.get_py_string_const(file_path, identifier=False, is_str=True) file_path_const = code.get_py_string_const(file_path, identifier=False, is_str=True)
code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, 0, 0, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % ( flags = []
if self.def_node.star_arg:
flags.append('CO_VARARGS')
if self.def_node.starstar_arg:
flags.append('CO_VARKEYWORDS')
code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % (
self.result_code, self.result_code,
len(func.args), # argcount len(func.args) - func.num_kwonly_args, # argcount
func.num_kwonly_args, # kwonlyargcount (Py3 only) func.num_kwonly_args, # kwonlyargcount (Py3 only)
len(self.varnames.args), # nlocals len(self.varnames.args), # nlocals
'|'.join(flags) or '0', # flags
Naming.empty_bytes, # code Naming.empty_bytes, # code
Naming.empty_tuple, # consts Naming.empty_tuple, # consts
Naming.empty_tuple, # names (FIXME) Naming.empty_tuple, # names (FIXME)
...@@ -7950,8 +7959,8 @@ class LocalsDictItemNode(DictItemNode): ...@@ -7950,8 +7959,8 @@ class LocalsDictItemNode(DictItemNode):
class FuncLocalsExprNode(DictNode): class FuncLocalsExprNode(DictNode):
def __init__(self, pos, env): def __init__(self, pos, env):
local_vars = [entry.name for entry in env.entries.values() local_vars = sorted([
if entry.name] entry.name for entry in env.entries.values() if entry.name])
items = [LocalsDictItemNode( items = [LocalsDictItemNode(
pos, key=IdentifierStringNode(pos, value=var), pos, key=IdentifierStringNode(pos, value=var),
value=NameNode(pos, name=var, allow_null=True)) value=NameNode(pos, name=var, allow_null=True))
...@@ -8373,6 +8382,9 @@ class TypecastNode(ExprNode): ...@@ -8373,6 +8382,9 @@ class TypecastNode(ExprNode):
"Cannot cast to a function type") "Cannot cast to a function type")
self.type = PyrexTypes.error_type self.type = PyrexTypes.error_type
self.operand = self.operand.analyse_types(env) self.operand = self.operand.analyse_types(env)
if self.type is PyrexTypes.c_bint_type:
# short circuit this to a coercion
return self.operand.coerce_to_boolean(env)
to_py = self.type.is_pyobject to_py = self.type.is_pyobject
from_py = self.operand.type.is_pyobject from_py = self.operand.type.is_pyobject
if from_py and not to_py and self.operand.is_ephemeral(): if from_py and not to_py and self.operand.is_ephemeral():
...@@ -8380,10 +8392,7 @@ class TypecastNode(ExprNode): ...@@ -8380,10 +8392,7 @@ class TypecastNode(ExprNode):
error(self.pos, "Casting temporary Python object to non-numeric non-Python type") error(self.pos, "Casting temporary Python object to non-numeric non-Python type")
if to_py and not from_py: if to_py and not from_py:
if self.type is bytes_type and self.operand.type.is_int: if self.type is bytes_type and self.operand.type.is_int:
# FIXME: the type cast node isn't needed in this case return CoerceIntToBytesNode(self.operand, env)
# and can be dropped once analyse_types() can return a
# different node
self.operand = CoerceIntToBytesNode(self.operand, env)
elif self.operand.type.can_coerce_to_pyobject(env): elif self.operand.type.can_coerce_to_pyobject(env):
self.result_ctype = py_object_type self.result_ctype = py_object_type
base_type = self.base_type.analyse(env) base_type = self.base_type.analyse(env)
...@@ -8405,7 +8414,7 @@ class TypecastNode(ExprNode): ...@@ -8405,7 +8414,7 @@ class TypecastNode(ExprNode):
else: else:
warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.type, self.operand.type)) warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.type, self.operand.type))
elif from_py and to_py: elif from_py and to_py:
if self.typecheck and self.type.is_pyobject: if self.typecheck:
self.operand = PyTypeTestNode(self.operand, self.type, env, notnone=True) self.operand = PyTypeTestNode(self.operand, self.type, env, notnone=True)
elif isinstance(self.operand, SliceIndexNode): elif isinstance(self.operand, SliceIndexNode):
# This cast can influence the created type of string slices. # This cast can influence the created type of string slices.
...@@ -9214,9 +9223,9 @@ class AddNode(NumBinopNode): ...@@ -9214,9 +9223,9 @@ class AddNode(NumBinopNode):
if type1 is unicode_type or type2 is unicode_type: if type1 is unicode_type or type2 is unicode_type:
if type1.is_builtin_type and type2.is_builtin_type: if type1.is_builtin_type and type2.is_builtin_type:
if self.operand1.may_be_none() or self.operand2.may_be_none(): if self.operand1.may_be_none() or self.operand2.may_be_none():
return '__Pyx_PyUnicode_Concat' return '__Pyx_PyUnicode_ConcatSafe'
else: else:
return 'PyUnicode_Concat' return '__Pyx_PyUnicode_Concat'
return super(AddNode, self).py_operation_function() return super(AddNode, self).py_operation_function()
......
...@@ -193,9 +193,11 @@ class ControlFlow(object): ...@@ -193,9 +193,11 @@ class ControlFlow(object):
def mark_reference(self, node, entry): def mark_reference(self, node, entry):
if self.block and self.is_tracked(entry): if self.block and self.is_tracked(entry):
self.block.stats.append(NameReference(node, entry)) self.block.stats.append(NameReference(node, entry))
# Local variable is definitely bound after this reference ## XXX: We don't track expression evaluation order so we can't use
if not node.allow_null: ## XXX: successful reference as initialization sign.
self.block.bounded.add(entry) ## # Local variable is definitely bound after this reference
## if not node.allow_null:
## self.block.bounded.add(entry)
self.entries.add(entry) self.entries.add(entry)
def normalize(self): def normalize(self):
...@@ -548,9 +550,9 @@ def check_definitions(flow, compiler_directives): ...@@ -548,9 +550,9 @@ def check_definitions(flow, compiler_directives):
references[stat.node] = stat.entry references[stat.node] = stat.entry
stat.entry.cf_references.append(stat) stat.entry.cf_references.append(stat)
stat.node.cf_state.update(state) stat.node.cf_state.update(state)
if not stat.node.allow_null: ## if not stat.node.allow_null:
i_state &= ~i_assmts.bit ## i_state &= ~i_assmts.bit
# after successful read, the state is known to be initialised ## # after successful read, the state is known to be initialised
state.discard(Uninitialized) state.discard(Uninitialized)
state.discard(Unknown) state.discard(Unknown)
for assmt in state: for assmt in state:
...@@ -798,7 +800,7 @@ class ControlFlowAnalysis(CythonTransform): ...@@ -798,7 +800,7 @@ class ControlFlowAnalysis(CythonTransform):
return node return node
def visit_AssignmentNode(self, node): def visit_AssignmentNode(self, node):
raise InternalError, "Unhandled assignment node" raise InternalError("Unhandled assignment node")
def visit_SingleAssignmentNode(self, node): def visit_SingleAssignmentNode(self, node):
self._visit(node.rhs) self._visit(node.rhs)
...@@ -1097,7 +1099,7 @@ class ControlFlowAnalysis(CythonTransform): ...@@ -1097,7 +1099,7 @@ class ControlFlowAnalysis(CythonTransform):
return node return node
def visit_LoopNode(self, node): def visit_LoopNode(self, node):
raise InternalError, "Generic loops are not supported" raise InternalError("Generic loops are not supported")
def visit_WithTargetAssignmentStatNode(self, node): def visit_WithTargetAssignmentStatNode(self, node):
self.mark_assignment(node.lhs, node.rhs) self.mark_assignment(node.lhs, node.rhs)
...@@ -1121,6 +1123,7 @@ class ControlFlowAnalysis(CythonTransform): ...@@ -1121,6 +1123,7 @@ class ControlFlowAnalysis(CythonTransform):
## XXX: links to exception handling point should be added by ## XXX: links to exception handling point should be added by
## XXX: children nodes ## XXX: children nodes
self.flow.block.add_child(entry_point) self.flow.block.add_child(entry_point)
self.flow.nextblock()
self._visit(node.body) self._visit(node.body)
self.flow.exceptions.pop() self.flow.exceptions.pop()
...@@ -1181,6 +1184,7 @@ class ControlFlowAnalysis(CythonTransform): ...@@ -1181,6 +1184,7 @@ class ControlFlowAnalysis(CythonTransform):
self.flow.block = body_block self.flow.block = body_block
## XXX: Is it still required ## XXX: Is it still required
body_block.add_child(entry_point) body_block.add_child(entry_point)
self.flow.nextblock()
self._visit(node.body) self._visit(node.body)
self.flow.exceptions.pop() self.flow.exceptions.pop()
if self.flow.loops: if self.flow.loops:
......
...@@ -309,7 +309,14 @@ class Context(object): ...@@ -309,7 +309,14 @@ class Context(object):
position = e.args[2] position = e.args[2]
encoding = e.args[0] encoding = e.args[0]
for idx, c in enumerate(open(source_filename, "rb").read()): f = open(source_filename, "rb")
try:
byte_data = f.read()
finally:
f.close()
# FIXME: make this at least a little less inefficient
for idx, c in enumerate(byte_data):
if c in (ord('\n'), '\n'): if c in (ord('\n'), '\n'):
line += 1 line += 1
column = 0 column = 0
......
...@@ -346,15 +346,44 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -346,15 +346,44 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
globalstate.finalize_main_c_code() globalstate.finalize_main_c_code()
f = open_new_file(result.c_file) f = open_new_file(result.c_file)
try:
rootwriter.copyto(f) rootwriter.copyto(f)
if options.gdb_debug: finally:
self._serialize_lineno_map(env, rootwriter)
f.close() f.close()
result.c_file_generated = 1 result.c_file_generated = 1
if options.gdb_debug:
self._serialize_lineno_map(env, rootwriter)
if Options.annotate or options.annotate: if Options.annotate or options.annotate:
self._generate_annotations(rootwriter, result)
def _generate_annotations(self, rootwriter, result):
self.annotate(rootwriter) self.annotate(rootwriter)
rootwriter.save_annotation(result.main_source_file, result.c_file) rootwriter.save_annotation(result.main_source_file, result.c_file)
# if we included files, additionally generate one annotation file for each
if not self.scope.included_files:
return
search_include_file = self.scope.context.search_include_directories
target_dir = os.path.abspath(os.path.dirname(result.c_file))
for included_file in self.scope.included_files:
target_file = os.path.abspath(os.path.join(target_dir, included_file))
target_file_dir = os.path.dirname(target_file)
if not target_file_dir.startswith(target_dir):
# any other directories may not be writable => avoid trying
continue
source_file = search_include_file(included_file, "", self.pos, include=True)
if not source_file:
continue
if target_file_dir != target_dir and not os.path.exists(target_file_dir):
try:
os.makedirs(target_file_dir)
except OSError, e:
import errno
if e.errno != errno.EEXIST:
raise
rootwriter.save_annotation(source_file, target_file)
def _serialize_lineno_map(self, env, ccodewriter): def _serialize_lineno_map(self, env, ccodewriter):
tb = env.context.gdb_debug_outputwriter tb = env.context.gdb_debug_outputwriter
markers = ccodewriter.buffer.allmarkers() markers = ccodewriter.buffer.allmarkers()
...@@ -382,13 +411,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -382,13 +411,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.find_referenced_modules(imported_module, module_list, modules_seen) self.find_referenced_modules(imported_module, module_list, modules_seen)
module_list.append(env) module_list.append(env)
def sort_types_by_inheritance(self, type_dict, getkey): def sort_types_by_inheritance(self, type_dict, type_order, getkey):
# copy the types into a list moving each parent type before # copy the types into a list moving each parent type before
# its first child # its first child
type_items = type_dict.items()
type_list = [] type_list = []
for i, item in enumerate(type_items): for i, key in enumerate(type_order):
key, new_entry = item new_entry = type_dict[key]
# collect all base classes to check for children # collect all base classes to check for children
hierarchy = set() hierarchy = set()
...@@ -413,43 +441,59 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -413,43 +441,59 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
return type_list return type_list
def sort_type_hierarchy(self, module_list, env): def sort_type_hierarchy(self, module_list, env):
vtab_dict = {} # poor developer's OrderedDict
vtabslot_dict = {} vtab_dict, vtab_dict_order = {}, []
vtabslot_dict, vtabslot_dict_order = {}, []
for module in module_list: for module in module_list:
for entry in module.c_class_entries: for entry in module.c_class_entries:
if not entry.in_cinclude: if entry.used and not entry.in_cinclude:
type = entry.type type = entry.type
if type.vtabstruct_cname: key = type.vtabstruct_cname
vtab_dict[type.vtabstruct_cname] = entry if not key:
continue
if key in vtab_dict:
# FIXME: this should *never* happen, but apparently it does
# for Cython generated utility code
from Cython.Compiler.UtilityCode import NonManglingModuleScope
assert isinstance(entry.scope, NonManglingModuleScope), str(entry.scope)
assert isinstance(vtab_dict[key].scope, NonManglingModuleScope), str(vtab_dict[key].scope)
else:
vtab_dict[key] = entry
vtab_dict_order.append(key)
all_defined_here = module is env all_defined_here = module is env
for entry in module.type_entries: for entry in module.type_entries:
if all_defined_here or entry.defined_in_pxd: if entry.used and (all_defined_here or entry.defined_in_pxd):
type = entry.type type = entry.type
if type.is_extension_type and not entry.in_cinclude: if type.is_extension_type and not entry.in_cinclude:
type = entry.type type = entry.type
vtabslot_dict[type.objstruct_cname] = entry key = type.objstruct_cname
assert key not in vtabslot_dict, key
vtabslot_dict[key] = entry
vtabslot_dict_order.append(key)
def vtabstruct_cname(entry_type): def vtabstruct_cname(entry_type):
return entry_type.vtabstruct_cname return entry_type.vtabstruct_cname
vtab_list = self.sort_types_by_inheritance( vtab_list = self.sort_types_by_inheritance(
vtab_dict, vtabstruct_cname) vtab_dict, vtab_dict_order, vtabstruct_cname)
def objstruct_cname(entry_type): def objstruct_cname(entry_type):
return entry_type.objstruct_cname return entry_type.objstruct_cname
vtabslot_list = self.sort_types_by_inheritance( vtabslot_list = self.sort_types_by_inheritance(
vtabslot_dict, objstruct_cname) vtabslot_dict, vtabslot_dict_order, objstruct_cname)
return (vtab_list, vtabslot_list) return (vtab_list, vtabslot_list)
def sort_cdef_classes(self, env): def sort_cdef_classes(self, env):
key_func = operator.attrgetter('objstruct_cname') key_func = operator.attrgetter('objstruct_cname')
entry_dict = {} entry_dict, entry_order = {}, []
for entry in env.c_class_entries: for entry in env.c_class_entries:
key = key_func(entry.type) key = key_func(entry.type)
assert key not in entry_dict assert key not in entry_dict, key
entry_dict[key] = entry entry_dict[key] = entry
entry_order.append(key)
env.c_class_entries[:] = self.sort_types_by_inheritance( env.c_class_entries[:] = self.sort_types_by_inheritance(
entry_dict, key_func) entry_dict, entry_order, key_func)
def generate_type_definitions(self, env, modules, vtab_list, vtabslot_list, code): def generate_type_definitions(self, env, modules, vtab_list, vtabslot_list, code):
# TODO: Why are these separated out? # TODO: Why are these separated out?
...@@ -1892,7 +1936,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1892,7 +1936,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
env.use_utility_code(streq_utility_code) env.use_utility_code(streq_utility_code)
code.putln() code.putln()
code.putln("static char* %s_type_names[] = {" % Naming.import_star) code.putln("static char* %s_type_names[] = {" % Naming.import_star)
for name, entry in env.entries.items(): for name, entry in sorted(env.entries.items()):
if entry.is_type: if entry.is_type:
code.putln('"%s",' % name) code.putln('"%s",' % name)
code.putln("0") code.putln("0")
......
...@@ -1554,6 +1554,9 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform): ...@@ -1554,6 +1554,9 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
"""Replace min(a,b,...) and max(a,b,...) by explicit comparison code. """Replace min(a,b,...) and max(a,b,...) by explicit comparison code.
""" """
if len(args) <= 1: if len(args) <= 1:
if len(args) == 1 and args[0].is_sequence_constructor:
args = args[0].args
else:
# leave this to Python # leave this to Python
return node return node
...@@ -2187,6 +2190,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform): ...@@ -2187,6 +2190,7 @@ class OptimizeBuiltinCalls(Visitor.MethodDispatcherTransform):
temp = None temp = None
if isinstance(types, ExprNodes.TupleNode): if isinstance(types, ExprNodes.TupleNode):
types = types.args types = types.args
if arg.is_attribute or not arg.is_simple():
arg = temp = UtilNodes.ResultRefNode(arg) arg = temp = UtilNodes.ResultRefNode(arg)
elif types.type is Builtin.type_type: elif types.type is Builtin.type_type:
types = [types] types = [types]
......
...@@ -1970,7 +1970,7 @@ class ExpandInplaceOperators(EnvTransform): ...@@ -1970,7 +1970,7 @@ class ExpandInplaceOperators(EnvTransform):
return node, [node] return node, [node]
elif isinstance(node, ExprNodes.IndexNode): elif isinstance(node, ExprNodes.IndexNode):
if node.is_buffer_access: if node.is_buffer_access:
raise ValueError, "Buffer access" raise ValueError("Buffer access")
base, temps = side_effect_free_reference(node.base) base, temps = side_effect_free_reference(node.base)
index = LetRefNode(node.index) index = LetRefNode(node.index)
return ExprNodes.IndexNode(node.pos, base=base, index=index), temps + [index] return ExprNodes.IndexNode(node.pos, base=base, index=index), temps + [index]
...@@ -2304,7 +2304,7 @@ class CreateClosureClasses(CythonTransform): ...@@ -2304,7 +2304,7 @@ class CreateClosureClasses(CythonTransform):
if not from_closure and (self.path or inner_node): if not from_closure and (self.path or inner_node):
if not inner_node: if not inner_node:
if not node.py_cfunc_node: if not node.py_cfunc_node:
raise InternalError, "DefNode does not have assignment node" raise InternalError("DefNode does not have assignment node")
inner_node = node.py_cfunc_node inner_node = node.py_cfunc_node
inner_node.needs_self_code = False inner_node.needs_self_code = False
node.needs_outer_scope = False node.needs_outer_scope = False
......
...@@ -19,7 +19,7 @@ def dumptree(t): ...@@ -19,7 +19,7 @@ def dumptree(t):
def abort_on_errors(node): def abort_on_errors(node):
# Stop the pipeline if there are any errors. # Stop the pipeline if there are any errors.
if Errors.num_errors != 0: if Errors.num_errors != 0:
raise AbortError, "pipeline break" raise AbortError("pipeline break")
return node return node
def parse_stage_factory(context): def parse_stage_factory(context):
......
...@@ -1542,7 +1542,7 @@ class LocalScope(Scope): ...@@ -1542,7 +1542,7 @@ class LocalScope(Scope):
if entry is not None: if entry is not None:
if entry.scope is not self and entry.scope.is_closure_scope: if entry.scope is not self and entry.scope.is_closure_scope:
if hasattr(entry.scope, "scope_class"): if hasattr(entry.scope, "scope_class"):
raise InternalError, "lookup() after scope class created." raise InternalError("lookup() after scope class created.")
# The actual c fragment for the different scopes differs # The actual c fragment for the different scopes differs
# on the outside and inside, so we make a new entry # on the outside and inside, so we make a new entry
entry.in_closure = True entry.in_closure = True
......
...@@ -271,13 +271,15 @@ class TestDebugTransform(DebuggerTestCase): ...@@ -271,13 +271,15 @@ class TestDebugTransform(DebuggerTestCase):
assert 'puts' in spam_stepinto assert 'puts' in spam_stepinto
assert 'some_c_function' in spam_stepinto assert 'some_c_function' in spam_stepinto
except: except:
print open(self.debug_dest).read() f = open(self.debug_dest)
try:
print(f.read())
finally:
f.close()
raise raise
if __name__ == "__main__": if __name__ == "__main__":
import unittest import unittest
unittest.main() unittest.main()
...@@ -383,6 +383,7 @@ class SimpleAssignmentTypeInferer(object): ...@@ -383,6 +383,7 @@ class SimpleAssignmentTypeInferer(object):
if not types: if not types:
node_type = py_object_type node_type = py_object_type
else: else:
entry = node.entry
node_type = spanning_type( node_type = spanning_type(
types, entry.might_overflow, entry.pos) types, entry.might_overflow, entry.pos)
node.inferred_type = node_type node.inferred_type = node_type
...@@ -392,6 +393,7 @@ class SimpleAssignmentTypeInferer(object): ...@@ -392,6 +393,7 @@ class SimpleAssignmentTypeInferer(object):
if assmt.inferred_type is not None] if assmt.inferred_type is not None]
if not types: if not types:
return return
entry = node.entry
return spanning_type(types, entry.might_overflow, entry.pos) return spanning_type(types, entry.might_overflow, entry.pos)
def resolve_assignments(assignments): def resolve_assignments(assignments):
...@@ -404,10 +406,9 @@ class SimpleAssignmentTypeInferer(object): ...@@ -404,10 +406,9 @@ class SimpleAssignmentTypeInferer(object):
infer_name_node_type(node) infer_name_node_type(node)
# Resolve assmt # Resolve assmt
inferred_type = assmt.infer_type() inferred_type = assmt.infer_type()
done = False
assmts_resolved.add(assmt) assmts_resolved.add(assmt)
resolved.add(assmt) resolved.add(assmt)
assignments -= resolved assignments.difference_update(resolved)
return resolved return resolved
def partial_infer(assmt): def partial_infer(assmt):
...@@ -427,10 +428,8 @@ class SimpleAssignmentTypeInferer(object): ...@@ -427,10 +428,8 @@ class SimpleAssignmentTypeInferer(object):
# try to handle circular references # try to handle circular references
partials = set() partials = set()
for assmt in assignments: for assmt in assignments:
partial_types = []
if assmt in partial_assmts: if assmt in partial_assmts:
continue continue
for node in assmt_to_names[assmt]:
if partial_infer(assmt): if partial_infer(assmt):
partials.add(assmt) partials.add(assmt)
assmts_resolved.add(assmt) assmts_resolved.add(assmt)
...@@ -542,7 +541,7 @@ def safe_spanning_type(types, might_overflow, pos): ...@@ -542,7 +541,7 @@ def safe_spanning_type(types, might_overflow, pos):
return result_type return result_type
# TODO: double complex should be OK as well, but we need # TODO: double complex should be OK as well, but we need
# to make sure everything is supported. # to make sure everything is supported.
elif result_type.is_int and not might_overflow: elif (result_type.is_int or result_type.is_enum) and not might_overflow:
return result_type return result_type
return py_object_type return py_object_type
......
...@@ -35,6 +35,7 @@ def make_command_file(path_to_debug_info, prefix_code='', no_import=False): ...@@ -35,6 +35,7 @@ def make_command_file(path_to_debug_info, prefix_code='', no_import=False):
fd, tempfilename = tempfile.mkstemp() fd, tempfilename = tempfile.mkstemp()
f = os.fdopen(fd, 'w') f = os.fdopen(fd, 'w')
try:
f.write(prefix_code) f.write(prefix_code)
f.write('set breakpoint pending on\n') f.write('set breakpoint pending on\n')
f.write("set print pretty on\n") f.write("set print pretty on\n")
...@@ -46,7 +47,11 @@ def make_command_file(path_to_debug_info, prefix_code='', no_import=False): ...@@ -46,7 +47,11 @@ def make_command_file(path_to_debug_info, prefix_code='', no_import=False):
pass pass
else: else:
path = os.path.join(path_to_debug_info, "cython_debug", "interpreter") path = os.path.join(path_to_debug_info, "cython_debug", "interpreter")
interpreter = open(path).read() interpreter_file = open(path)
try:
interpreter = interpreter_file.read()
finally:
interpreter_file.close()
f.write("file %s\n" % interpreter) f.write("file %s\n" % interpreter)
f.write('\n'.join('cy import %s\n' % fn for fn in debug_files)) f.write('\n'.join('cy import %s\n' % fn for fn in debug_files))
f.write(textwrap.dedent('''\ f.write(textwrap.dedent('''\
...@@ -62,7 +67,7 @@ def make_command_file(path_to_debug_info, prefix_code='', no_import=False): ...@@ -62,7 +67,7 @@ def make_command_file(path_to_debug_info, prefix_code='', no_import=False):
source .cygdbinit source .cygdbinit
''')) '''))
finally:
f.close() f.close()
return tempfilename return tempfilename
......
...@@ -35,7 +35,9 @@ build_ext = sys.modules['Cython.Distutils.build_ext'] ...@@ -35,7 +35,9 @@ build_ext = sys.modules['Cython.Distutils.build_ext']
have_gdb = None have_gdb = None
def test_gdb(): def test_gdb():
global have_gdb global have_gdb
if have_gdb is None: if have_gdb is not None:
return have_gdb
try: try:
p = subprocess.Popen(['gdb', '-v'], stdout=subprocess.PIPE) p = subprocess.Popen(['gdb', '-v'], stdout=subprocess.PIPE)
have_gdb = True have_gdb = True
...@@ -43,7 +45,7 @@ def test_gdb(): ...@@ -43,7 +45,7 @@ def test_gdb():
# gdb was not installed # gdb was not installed
have_gdb = False have_gdb = False
else: else:
gdb_version = p.stdout.read().decode('ascii') gdb_version = p.stdout.read().decode('ascii', 'ignore')
p.wait() p.wait()
p.stdout.close() p.stdout.close()
...@@ -54,17 +56,23 @@ def test_gdb(): ...@@ -54,17 +56,23 @@ def test_gdb():
if gdb_version_number >= [7, 2]: if gdb_version_number >= [7, 2]:
python_version_script = tempfile.NamedTemporaryFile(mode='w+') python_version_script = tempfile.NamedTemporaryFile(mode='w+')
try:
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])')
python_version_script.flush() python_version_script.flush()
p = subprocess.Popen(['gdb', '-batch', '-x', python_version_script.name], p = subprocess.Popen(['gdb', '-batch', '-x', python_version_script.name],
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
try:
python_version = p.stdout.read().decode('ascii') python_version = p.stdout.read().decode('ascii')
p.wait() p.wait()
finally:
p.stdout.close()
try: try:
python_version_number = list(map(int, python_version.split())) python_version_number = list(map(int, python_version.split()))
except ValueError: except ValueError:
have_gdb = False have_gdb = False
finally:
python_version_script.close()
# Be Python 3 compatible # Be Python 3 compatible
if (not have_gdb if (not have_gdb
...@@ -146,6 +154,7 @@ class DebuggerTestCase(unittest.TestCase): ...@@ -146,6 +154,7 @@ class DebuggerTestCase(unittest.TestCase):
finally: finally:
optimization_disabler.restore_state() optimization_disabler.restore_state()
sys.stderr = stderr sys.stderr = stderr
new_stderr.close()
# ext = Cython.Distutils.extension.Extension( # ext = Cython.Distutils.extension.Extension(
# 'codefile', # 'codefile',
...@@ -227,45 +236,6 @@ class GdbDebuggerTestCase(DebuggerTestCase): ...@@ -227,45 +236,6 @@ class GdbDebuggerTestCase(DebuggerTestCase):
os.path.abspath(Cython.__file__)))) os.path.abspath(Cython.__file__))))
env = dict(os.environ, PYTHONPATH=os.pathsep.join(paths)) env = dict(os.environ, PYTHONPATH=os.pathsep.join(paths))
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()
try:
python_version_number = list(map(int, python_version.split()))
except ValueError:
have_gdb = False
# Be Python 3 compatible
if (not have_gdb
or gdb_version_number < [7, 2]
or python_version_number < [2, 6]):
self.p = None
warnings.warn(
'Skipping gdb tests, need gdb >= 7.2 with Python >= 2.6')
else:
self.p = subprocess.Popen( self.p = subprocess.Popen(
args, args,
stdout=open(os.devnull, 'w'), stdout=open(os.devnull, 'w'),
...@@ -276,10 +246,15 @@ class GdbDebuggerTestCase(DebuggerTestCase): ...@@ -276,10 +246,15 @@ class GdbDebuggerTestCase(DebuggerTestCase):
if not test_gdb(): if not test_gdb():
return return
try:
super(GdbDebuggerTestCase, self).tearDown() super(GdbDebuggerTestCase, self).tearDown()
if self.p: if self.p:
self.p.stderr.close() try: self.p.stdout.close()
except: pass
try: self.p.stderr.close()
except: pass
self.p.wait() self.p.wait()
finally:
os.remove(self.gdb_command_file) os.remove(self.gdb_command_file)
...@@ -292,15 +267,15 @@ class TestAll(GdbDebuggerTestCase): ...@@ -292,15 +267,15 @@ class TestAll(GdbDebuggerTestCase):
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() exit_status = self.p.returncode
if exit_status == 1: if exit_status == 1:
sys.stderr.write(err) sys.stderr.write(err)
elif exit_status >= 2: elif exit_status >= 2:
border = '*' * 30 border = u'*' * 30
start = '%s v INSIDE GDB v %s' % (border, border) start = u'%s v INSIDE GDB v %s' % (border, border)
end = '%s ^ INSIDE GDB ^ %s' % (border, border) end = u'%s ^ INSIDE GDB ^ %s' % (border, border)
errmsg = '\n%s\n%s%s' % (start, err, end) errmsg = u'\n%s\n%s%s' % (start, err, end)
sys.stderr.write(errmsg) sys.stderr.write(errmsg)
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
# #
# Author: Lars Buitinck # Author: Lars Buitinck
cdef extern from "numpy/npy_math.h": cdef extern from "numpy/npy_math.h" nogil:
# Floating-point classification # Floating-point classification
long double NAN "NPY_NAN" long double NAN "NPY_NAN"
long double INFINITY "NPY_INFINITY" long double INFINITY "NPY_INFINITY"
...@@ -30,8 +30,6 @@ cdef extern from "numpy/npy_math.h": ...@@ -30,8 +30,6 @@ cdef extern from "numpy/npy_math.h":
bint isnan "npy_isnan"(long double) bint isnan "npy_isnan"(long double)
bint signbit "npy_signbit"(long double) bint signbit "npy_signbit"(long double)
double copysign "npy_copysign"(double, double)
# Math constants # Math constants
long double E "NPY_E" long double E "NPY_E"
long double LOG2E "NPY_LOG2E" # ln(e) / ln(2) long double LOG2E "NPY_LOG2E" # ln(e) / ln(2)
...@@ -46,5 +44,90 @@ cdef extern from "numpy/npy_math.h": ...@@ -46,5 +44,90 @@ cdef extern from "numpy/npy_math.h":
long double EULER "NPY_EULER" # Euler constant (gamma, 0.57721) long double EULER "NPY_EULER" # Euler constant (gamma, 0.57721)
# Low-level floating point manipulation (NumPy >=1.4) # Low-level floating point manipulation (NumPy >=1.4)
float copysignf "npy_copysignf"(float, float)
float nextafterf "npy_nextafterf"(float x, float y)
float spacingf "npy_spacingf"(float x)
double copysign "npy_copysign"(double, double)
double nextafter "npy_nextafter"(double x, double y) double nextafter "npy_nextafter"(double x, double y)
double spacing "npy_spacing"(double x) double spacing "npy_spacing"(double x)
long double copysignl "npy_copysignl"(long double, long double)
long double nextafterl "npy_nextafterl"(long double x, long double y)
long double spacingl "npy_spacingl"(long double x)
# Float C99 functions
float sinf "npy_sinf"(float x)
float cosf "npy_cosf"(float x)
float tanf "npy_tanf"(float x)
float sinhf "npy_sinhf"(float x)
float coshf "npy_coshf"(float x)
float tanhf "npy_tanhf"(float x)
float fabsf "npy_fabsf"(float x)
float floorf "npy_floorf"(float x)
float ceilf "npy_ceilf"(float x)
float rintf "npy_rintf"(float x)
float sqrtf "npy_sqrtf"(float x)
float log10f "npy_log10f"(float x)
float logf "npy_logf"(float x)
float expf "npy_expf"(float x)
float expm1f "npy_expm1f"(float x)
float asinf "npy_asinf"(float x)
float acosf "npy_acosf"(float x)
float atanf "npy_atanf"(float x)
float asinhf "npy_asinhf"(float x)
float acoshf "npy_acoshf"(float x)
float atanhf "npy_atanhf"(float x)
float log1pf "npy_log1pf"(float x)
float exp2f "npy_exp2f"(float x)
float log2f "npy_log2f"(float x)
float atan2f "npy_atan2f"(float x)
float hypotf "npy_hypotf"(float x)
float powf "npy_powf"(float x)
float fmodf "npy_fmodf"(float x)
float modff "npy_modff"(float x)
# Long double C99 functions
long double sinl "npy_sinl"(long double x)
long double cosl "npy_cosl"(long double x)
long double tanl "npy_tanl"(long double x)
long double sinhl "npy_sinhl"(long double x)
long double coshl "npy_coshl"(long double x)
long double tanhl "npy_tanhl"(long double x)
long double fabsl "npy_fabsl"(long double x)
long double floorl "npy_floorl"(long double x)
long double ceill "npy_ceill"(long double x)
long double rintl "npy_rintl"(long double x)
long double sqrtl "npy_sqrtl"(long double x)
long double log10l "npy_log10l"(long double x)
long double logl "npy_logl"(long double x)
long double expl "npy_expl"(long double x)
long double expm1l "npy_expm1l"(long double x)
long double asinl "npy_asinl"(long double x)
long double acosl "npy_acosl"(long double x)
long double atanl "npy_atanl"(long double x)
long double asinhl "npy_asinhl"(long double x)
long double acoshl "npy_acoshl"(long double x)
long double atanhl "npy_atanhl"(long double x)
long double log1pl "npy_log1pl"(long double x)
long double exp2l "npy_exp2l"(long double x)
long double log2l "npy_log2l"(long double x)
long double atan2l "npy_atan2l"(long double x)
long double hypotl "npy_hypotl"(long double x)
long double powl "npy_powl"(long double x)
long double fmodl "npy_fmodl"(long double x)
long double modfl "npy_modfl"(long double x)
# NumPy extensions
float deg2radf "npy_deg2radf"(float x)
float rad2degf "npy_rad2degf"(float x)
float logaddexpf "npy_logaddexpf"(float x)
float logaddexp2f "npy_logaddexp2f"(float x)
double deg2rad "npy_deg2rad"(double x)
double rad2deg "npy_rad2deg"(double x)
double logaddexp "npy_logaddexp"(double x)
double logaddexp2 "npy_logaddexp2"(double x)
long double deg2radl "npy_deg2radl"(long double x)
long double rad2degl "npy_rad2degl"(long double x)
long double logaddexpl "npy_logaddexpl"(long double x)
long double logaddexp2l "npy_logaddexp2l"(long double x)
# cython.* namespace for pure mode. # cython.* namespace for pure mode.
__version__ = "0.20" __version__ = "0.20.post0"
# BEGIN shameless copy from Cython/minivect/minitypes.py # BEGIN shameless copy from Cython/minivect/minitypes.py
......
...@@ -8,13 +8,15 @@ import unittest ...@@ -8,13 +8,15 @@ import unittest
import os, sys import os, sys
import tempfile import tempfile
class NodeTypeWriter(TreeVisitor): class NodeTypeWriter(TreeVisitor):
def __init__(self): def __init__(self):
super(NodeTypeWriter, self).__init__() super(NodeTypeWriter, self).__init__()
self._indents = 0 self._indents = 0
self.result = [] self.result = []
def visit_Node(self, node): def visit_Node(self, node):
if len(self.access_path) == 0: if not self.access_path:
name = u"(root)" name = u"(root)"
else: else:
tip = self.access_path[-1] tip = self.access_path[-1]
...@@ -29,6 +31,7 @@ class NodeTypeWriter(TreeVisitor): ...@@ -29,6 +31,7 @@ class NodeTypeWriter(TreeVisitor):
self.visitchildren(node) self.visitchildren(node)
self._indents -= 1 self._indents -= 1
def treetypes(root): def treetypes(root):
"""Returns a string representing the tree by class names. """Returns a string representing the tree by class names.
There's a leading and trailing whitespace so that it can be There's a leading and trailing whitespace so that it can be
...@@ -38,6 +41,7 @@ def treetypes(root): ...@@ -38,6 +41,7 @@ def treetypes(root):
w.visit(root) w.visit(root)
return u"\n".join([u""] + w.result + [u""]) return u"\n".join([u""] + w.result + [u""])
class CythonTest(unittest.TestCase): class CythonTest(unittest.TestCase):
def setUp(self): def setUp(self):
...@@ -110,6 +114,7 @@ class CythonTest(unittest.TestCase): ...@@ -110,6 +114,7 @@ class CythonTest(unittest.TestCase):
except: except:
self.fail(str(sys.exc_info()[1])) self.fail(str(sys.exc_info()[1]))
class TransformTest(CythonTest): class TransformTest(CythonTest):
""" """
Utility base class for transform unit tests. It is based around constructing Utility base class for transform unit tests. It is based around constructing
...@@ -134,7 +139,6 @@ class TransformTest(CythonTest): ...@@ -134,7 +139,6 @@ class TransformTest(CythonTest):
Plans: One could have a pxd dictionary parameter to run_pipeline. Plans: One could have a pxd dictionary parameter to run_pipeline.
""" """
def run_pipeline(self, pipeline, pyx, pxds={}): def run_pipeline(self, pipeline, pyx, pxds={}):
tree = self.fragment(pyx, pxds).root tree = self.fragment(pyx, pxds).root
# Run pipeline # Run pipeline
...@@ -166,6 +170,7 @@ class TreeAssertVisitor(VisitorTransform): ...@@ -166,6 +170,7 @@ class TreeAssertVisitor(VisitorTransform):
visit_Node = VisitorTransform.recurse_to_children visit_Node = VisitorTransform.recurse_to_children
def unpack_source_tree(tree_file, dir=None): def unpack_source_tree(tree_file, dir=None):
if dir is None: if dir is None:
dir = tempfile.mkdtemp() dir = tempfile.mkdtemp()
...@@ -176,7 +181,8 @@ def unpack_source_tree(tree_file, dir=None): ...@@ -176,7 +181,8 @@ def unpack_source_tree(tree_file, dir=None):
lines = f.readlines() lines = f.readlines()
finally: finally:
f.close() f.close()
f = None del f
try:
for line in lines: for line in lines:
if line[:5] == '#####': if line[:5] == '#####':
filename = line.strip().strip('#').strip().replace('/', os.path.sep) filename = line.strip().strip('#').strip().replace('/', os.path.sep)
...@@ -184,13 +190,15 @@ def unpack_source_tree(tree_file, dir=None): ...@@ -184,13 +190,15 @@ def unpack_source_tree(tree_file, dir=None):
if not os.path.exists(os.path.dirname(path)): if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path)) os.makedirs(os.path.dirname(path))
if cur_file is not None: if cur_file is not None:
cur_file.close() f, cur_file = cur_file, None
f.close()
cur_file = open(path, 'w') cur_file = open(path, 'w')
elif cur_file is not None: elif cur_file is not None:
cur_file.write(line) cur_file.write(line)
elif line.strip() and not line.lstrip().startswith('#'): elif line.strip() and not line.lstrip().startswith('#'):
if line.strip() not in ('"""', "'''"): if line.strip() not in ('"""', "'''"):
header.append(line) header.append(line)
finally:
if cur_file is not None: if cur_file is not None:
cur_file.close() cur_file.close()
return dir, ''.join(header) return dir, ''.join(header)
...@@ -222,14 +222,14 @@ class _XMLTestResult(_TextTestResult): ...@@ -222,14 +222,14 @@ class _XMLTestResult(_TextTestResult):
testsuite.setAttribute('name', str(suite_name)) testsuite.setAttribute('name', str(suite_name))
testsuite.setAttribute('tests', str(len(tests))) testsuite.setAttribute('tests', str(len(tests)))
testsuite.setAttribute('time', '%.3f' % \ testsuite.setAttribute('time', '%.3f' %
sum(map(lambda e: e.get_elapsed_time(), tests))) sum([e.get_elapsed_time() for e in tests]))
failures = filter(lambda e: e.outcome==_TestInfo.FAILURE, tests) failures = len([1 for e in tests if e.outcome == _TestInfo.FAILURE])
testsuite.setAttribute('failures', str(len(failures))) testsuite.setAttribute('failures', str(failures))
errors = filter(lambda e: e.outcome==_TestInfo.ERROR, tests) errors = len([1 for e in tests if e.outcome == _TestInfo.ERROR])
testsuite.setAttribute('errors', str(len(errors))) testsuite.setAttribute('errors', str(errors))
return testsuite return testsuite
......
...@@ -593,8 +593,11 @@ __pyx_buffmt_parse_array(__Pyx_BufFmt_Context* ctx, const char** tsp) ...@@ -593,8 +593,11 @@ __pyx_buffmt_parse_array(__Pyx_BufFmt_Context* ctx, const char** tsp)
/* Parse all numbers in the format string */ /* Parse all numbers in the format string */
while (*ts && *ts != ')') { while (*ts && *ts != ')') {
if (isspace(*ts)) // ignore space characters (not using isspace() due to C/C++ problem on MacOS-X)
continue; switch (*ts) {
case ' ': case '\f': case '\r': case '\n': case '\t': case '\v': continue;
default: break; /* not a 'break' in the loop */
}
number = __Pyx_BufFmt_ExpectNumber(&ts); number = __Pyx_BufFmt_ExpectNumber(&ts);
if (number == -1) return NULL; if (number == -1) return NULL;
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
typedef struct { typedef struct {
PyCFunctionObject func; PyCFunctionObject func;
int flags;
PyObject *func_dict; PyObject *func_dict;
PyObject *func_weakreflist; PyObject *func_weakreflist;
PyObject *func_name; PyObject *func_name;
...@@ -35,6 +34,7 @@ typedef struct { ...@@ -35,6 +34,7 @@ typedef struct {
/* Dynamic default args and annotations */ /* Dynamic default args and annotations */
void *defaults; void *defaults;
int defaults_pyobjects; int defaults_pyobjects;
int flags;
/* Defaults info */ /* Defaults info */
PyObject *defaults_tuple; /* Const defaults tuple */ PyObject *defaults_tuple; /* Const defaults tuple */
......
...@@ -108,7 +108,7 @@ ...@@ -108,7 +108,7 @@
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
#define __Pyx_BUILTIN_MODULE_NAME "__builtin__" #define __Pyx_BUILTIN_MODULE_NAME "__builtin__"
#define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \ #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \
PyCode_New(a, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) PyCode_New(a+k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
#define __Pyx_DefaultClassType PyClass_Type #define __Pyx_DefaultClassType PyClass_Type
#else #else
#define __Pyx_BUILTIN_MODULE_NAME "builtins" #define __Pyx_BUILTIN_MODULE_NAME "builtins"
...@@ -161,10 +161,17 @@ ...@@ -161,10 +161,17 @@
#define __Pyx_PyUnicode_READ(k, d, i) ((void)(k), (Py_UCS4)(((Py_UNICODE*)d)[i])) #define __Pyx_PyUnicode_READ(k, d, i) ((void)(k), (Py_UCS4)(((Py_UNICODE*)d)[i]))
#endif #endif
#if CYTHON_COMPILING_IN_PYPY
#define __Pyx_PyUnicode_Concat(a, b) PyNumber_Add(a, b)
#define __Pyx_PyUnicode_ConcatSafe(a, b) PyNumber_Add(a, b)
#else
#define __Pyx_PyUnicode_Concat(a, b) PyUnicode_Concat(a, b)
#define __Pyx_PyUnicode_ConcatSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ? \
PyNumber_Add(a, b) : __Pyx_PyUnicode_Concat(a, b))
#endif
#define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b)) #define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b))
#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b)) #define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b))
#define __Pyx_PyUnicode_Concat(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ? \
PyNumber_Add(a, b) : PyUnicode_Concat(a, b))
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
#define __Pyx_PyString_Format(a, b) PyUnicode_Format(a, b) #define __Pyx_PyString_Format(a, b) PyUnicode_Format(a, b)
...@@ -240,7 +247,7 @@ ...@@ -240,7 +247,7 @@
#define PyBoolObject PyLongObject #define PyBoolObject PyLongObject
#endif #endif
#if PY_VERSION_HEX < 0x03020000 #if PY_VERSION_HEX < 0x030200A4
typedef long Py_hash_t; typedef long Py_hash_t;
#define __Pyx_PyInt_FromHash_t PyInt_FromLong #define __Pyx_PyInt_FromHash_t PyInt_FromLong
#define __Pyx_PyInt_AsHash_t PyInt_AsLong #define __Pyx_PyInt_AsHash_t PyInt_AsLong
......
...@@ -1029,7 +1029,8 @@ static CYTHON_INLINE PyObject *__Pyx_GetAttr(PyObject *o, PyObject *n) { ...@@ -1029,7 +1029,8 @@ static CYTHON_INLINE PyObject *__Pyx_GetAttr(PyObject *o, PyObject *n) {
/////////////// PyObjectLookupSpecial.proto /////////////// /////////////// PyObjectLookupSpecial.proto ///////////////
//@requires: PyObjectGetAttrStr //@requires: PyObjectGetAttrStr
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON && (PY_VERSION_HEX >= 0x03020000 || PY_MAJOR_VERSION < 3 && PY_VERSION_HEX >= 0x02070000)
// looks like calling _PyType_Lookup() isn't safe in Py<=2.6/3.1
static CYTHON_INLINE PyObject* __Pyx_PyObject_LookupSpecial(PyObject* obj, PyObject* attr_name) { static CYTHON_INLINE PyObject* __Pyx_PyObject_LookupSpecial(PyObject* obj, PyObject* attr_name) {
PyObject *res; PyObject *res;
PyTypeObject *tp = Py_TYPE(obj); PyTypeObject *tp = Py_TYPE(obj);
...@@ -1052,7 +1053,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_LookupSpecial(PyObject* obj, PyObj ...@@ -1052,7 +1053,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_LookupSpecial(PyObject* obj, PyObj
return res; return res;
} }
#else #else
#define __Pyx_PyObject_LookupSpecial(o,n) PyObject_GetAttr(o,n) #define __Pyx_PyObject_LookupSpecial(o,n) __Pyx_PyObject_GetAttrStr(o,n)
#endif #endif
/////////////// PyObjectGetAttrStr.proto /////////////// /////////////// PyObjectGetAttrStr.proto ///////////////
...@@ -1093,6 +1094,7 @@ static CYTHON_INLINE int __Pyx_PyObject_SetAttrStr(PyObject* obj, PyObject* attr ...@@ -1093,6 +1094,7 @@ static CYTHON_INLINE int __Pyx_PyObject_SetAttrStr(PyObject* obj, PyObject* attr
/////////////// PyObjectCallMethod.proto /////////////// /////////////// PyObjectCallMethod.proto ///////////////
//@requires: PyObjectGetAttrStr //@requires: PyObjectGetAttrStr
//@requires: PyObjectCall
//@substitute: naming //@substitute: naming
static PyObject* __Pyx_PyObject_CallMethodTuple(PyObject* obj, PyObject* method_name, PyObject* args) { static PyObject* __Pyx_PyObject_CallMethodTuple(PyObject* obj, PyObject* method_name, PyObject* args) {
...@@ -1100,7 +1102,7 @@ static PyObject* __Pyx_PyObject_CallMethodTuple(PyObject* obj, PyObject* method_ ...@@ -1100,7 +1102,7 @@ static PyObject* __Pyx_PyObject_CallMethodTuple(PyObject* obj, PyObject* method_
if (unlikely(!args)) return NULL; if (unlikely(!args)) return NULL;
method = __Pyx_PyObject_GetAttrStr(obj, method_name); method = __Pyx_PyObject_GetAttrStr(obj, method_name);
if (unlikely(!method)) goto bad; if (unlikely(!method)) goto bad;
result = PyObject_Call(method, args, NULL); result = __Pyx_PyObject_Call(method, args, NULL);
Py_DECREF(method); Py_DECREF(method);
bad: bad:
Py_DECREF(args); Py_DECREF(args);
...@@ -1123,3 +1125,38 @@ bad: ...@@ -1123,3 +1125,38 @@ bad:
static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* args, PyObject* kwargs) { static CYTHON_INLINE PyObject* __Pyx_tp_new_kwargs(PyObject* type_obj, PyObject* args, PyObject* kwargs) {
return (PyObject*) (((PyTypeObject*)type_obj)->tp_new((PyTypeObject*)type_obj, args, kwargs)); return (PyObject*) (((PyTypeObject*)type_obj)->tp_new((PyTypeObject*)type_obj, args, kwargs));
} }
/////////////// PyObjectCall.proto ///////////////
#if CYTHON_COMPILING_IN_CPYTHON
static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw); /*proto*/
#else
#define __Pyx_PyObject_Call(func, arg, kw) PyObject_Call(func, arg, kw)
#endif
/////////////// PyObjectCall ///////////////
#if CYTHON_COMPILING_IN_CPYTHON
static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) {
PyObject *result;
ternaryfunc call = func->ob_type->tp_call;
if (unlikely(!call))
return PyObject_Call(func, arg, kw);
#if PY_VERSION_HEX >= 0x02060000
if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object")))
return NULL;
#endif
result = (*call)(func, arg, kw);
#if PY_VERSION_HEX >= 0x02060000
Py_LeaveRecursiveCall();
#endif
if (unlikely(!result) && unlikely(!PyErr_Occurred())) {
PyErr_SetString(
PyExc_SystemError,
"NULL result without error in PyObject_Call");
}
return result;
}
#endif
...@@ -47,10 +47,16 @@ static int __Pyx_Print(PyObject* f, PyObject *arg_tuple, int newline) { ...@@ -47,10 +47,16 @@ static int __Pyx_Print(PyObject* f, PyObject *arg_tuple, int newline) {
if (PyString_Check(v)) { if (PyString_Check(v)) {
char *s = PyString_AsString(v); char *s = PyString_AsString(v);
Py_ssize_t len = PyString_Size(v); Py_ssize_t len = PyString_Size(v);
if (len > 0 && if (len > 0) {
isspace(Py_CHARMASK(s[len-1])) && // append soft-space if necessary (not using isspace() due to C/C++ problem on MacOS-X)
s[len-1] != ' ') switch (s[len-1]) {
case ' ': break;
case '\f': case '\r': case '\n': case '\t': case '\v':
PyFile_SoftSpace(f, 0); PyFile_SoftSpace(f, 0);
break;
default: break;
}
}
} }
} }
if (newline) { if (newline) {
......
...@@ -206,11 +206,13 @@ static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_ ...@@ -206,11 +206,13 @@ static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_
} else } else
#endif /* __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT */ #endif /* __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT */
#if !CYTHON_COMPILING_IN_PYPY
#if PY_VERSION_HEX >= 0x02060000 #if PY_VERSION_HEX >= 0x02060000
if (PyByteArray_Check(o)) { if (PyByteArray_Check(o)) {
*length = PyByteArray_GET_SIZE(o); *length = PyByteArray_GET_SIZE(o);
return PyByteArray_AS_STRING(o); return PyByteArray_AS_STRING(o);
} else } else
#endif
#endif #endif
{ {
char* result; char* result;
......
...@@ -398,7 +398,7 @@ Globally ...@@ -398,7 +398,7 @@ Globally
One can set compiler directives through a special header comment at the top of the file, like this:: One can set compiler directives through a special header comment at the top of the file, like this::
#!python #!python
#cython: boundscheck=False #cython: language_level=3, boundscheck=False
The comment must appear before any code (but can appear after other The comment must appear before any code (but can appear after other
comments or whitespace). comments or whitespace).
...@@ -426,7 +426,8 @@ statement, like this:: ...@@ -426,7 +426,8 @@ statement, like this::
@cython.boundscheck(False) # turn off boundscheck for this function @cython.boundscheck(False) # turn off boundscheck for this function
def f(): def f():
... ...
with cython.boundscheck(True): # turn it temporarily on again for this block # turn it temporarily on again for this block
with cython.boundscheck(True):
... ...
.. Warning:: These two methods of setting directives are **not** .. Warning:: These two methods of setting directives are **not**
......
...@@ -16,46 +16,46 @@ implicitly insert these encoding/decoding steps. ...@@ -16,46 +16,46 @@ implicitly insert these encoding/decoding steps.
Python string types in Cython code Python string types in Cython code
---------------------------------- ----------------------------------
Cython supports four Python string types: ``bytes``, ``str``, Cython supports four Python string types: :obj:`bytes`, :obj:`str`,
``unicode`` and ``basestring``. The ``bytes`` and ``unicode`` types :obj:`unicode` and :obj:`basestring`. The :obj:`bytes` and :obj:`unicode` types
are the specific types known from normal Python 2.x (named ``bytes`` are the specific types known from normal Python 2.x (named :obj:`bytes`
and ``str`` in Python 3). Additionally, Cython also supports the and :obj:`str` in Python 3). Additionally, Cython also supports the
``bytearray`` type starting with Python 2.6. It behaves like the :obj:`bytearray` type starting with Python 2.6. It behaves like the
``bytes`` type, except that it is mutable. :obj:`bytes` type, except that it is mutable.
The ``str`` type is special in that it is the byte string in Python 2 The :obj:`str` type is special in that it is the byte string in Python 2
and the Unicode string in Python 3 (for Cython code compiled with and the Unicode string in Python 3 (for Cython code compiled with
language level 2, i.e. the default). Meaning, it always corresponds language level 2, i.e. the default). Meaning, it always corresponds
exactly with the type that the Python runtime itself calls ``str``. exactly with the type that the Python runtime itself calls :obj:`str`.
Thus, in Python 2, both ``bytes`` and ``str`` represent the byte string Thus, in Python 2, both :obj:`bytes` and :obj:`str` represent the byte string
type, whereas in Python 3, both ``str`` and ``unicode`` represent the type, whereas in Python 3, both :obj:`str` and :obj:`unicode` represent the
Python Unicode string type. The switch is made at C compile time, the Python Unicode string type. The switch is made at C compile time, the
Python version that is used to run Cython is not relevant. Python version that is used to run Cython is not relevant.
When compiling Cython code with language level 3, the ``str`` type is When compiling Cython code with language level 3, the :obj:`str` type is
identified with exactly the Unicode string type at Cython compile time, identified with exactly the Unicode string type at Cython compile time,
i.e. it does not identify with ``bytes`` when running in Python 2. i.e. it does not identify with :obj:`bytes` when running in Python 2.
Note that the ``str`` type is not compatible with the ``unicode`` Note that the :obj:`str` type is not compatible with the :obj:`unicode`
type in Python 2, i.e. you cannot assign a Unicode string to a variable type in Python 2, i.e. you cannot assign a Unicode string to a variable
or argument that is typed ``str``. The attempt will result in either or argument that is typed :obj:`str`. The attempt will result in either
a compile time error (if detectable) or a ``TypeError`` exception at a compile time error (if detectable) or a :obj:`TypeError` exception at
runtime. You should therefore be careful when you statically type a runtime. You should therefore be careful when you statically type a
string variable in code that must be compatible with Python 2, as this string variable in code that must be compatible with Python 2, as this
Python version allows a mix of byte strings and unicode strings for data Python version allows a mix of byte strings and unicode strings for data
and users normally expect code to be able to work with both. Code that and users normally expect code to be able to work with both. Code that
only targets Python 3 can safely type variables and arguments as either only targets Python 3 can safely type variables and arguments as either
``bytes`` or ``unicode``. :obj:`bytes` or :obj:`unicode`.
The ``basestring`` type represents both the types ``str`` and ``unicode``, The :obj:`basestring` type represents both the types :obj:`str` and :obj:`unicode`,
i.e. all Python text string types in Python 2 and Python 3. This can be i.e. all Python text string types in Python 2 and Python 3. This can be
used for typing text variables that normally contain Unicode text (at used for typing text variables that normally contain Unicode text (at
least in Python 3) but must additionally accept the ``str`` type in least in Python 3) but must additionally accept the :obj:`str` type in
Python 2 for backwards compatibility reasons. It is not compatible with Python 2 for backwards compatibility reasons. It is not compatible with
the ``bytes`` type. Its usage should be rare in normal Cython code as the :obj:`bytes` type. Its usage should be rare in normal Cython code as
the generic ``object`` type (i.e. untyped code) will normally be good the generic :obj:`object` type (i.e. untyped code) will normally be good
enough and has the additional advantage of supporting the assignment of enough and has the additional advantage of supporting the assignment of
string subtypes. Support for the ``basestring`` type is new in Cython string subtypes. Support for the :obj:`basestring` type is new in Cython
0.20. 0.20.
...@@ -100,7 +100,7 @@ Python variable:: ...@@ -100,7 +100,7 @@ Python variable::
cdef char* c_string = c_call_returning_a_c_string() cdef char* c_string = c_call_returning_a_c_string()
cdef bytes py_string = c_string cdef bytes py_string = c_string
A type cast to ``object`` or ``bytes`` will do the same thing:: A type cast to :obj:`object` or :obj:`bytes` will do the same thing::
py_string = <bytes> c_string py_string = <bytes> c_string
...@@ -163,13 +163,116 @@ however, when the C function stores the pointer for later use. Apart ...@@ -163,13 +163,116 @@ however, when the C function stores the pointer for later use. Apart
from keeping a Python reference to the string object, no manual memory from keeping a Python reference to the string object, no manual memory
management is required. management is required.
Starting with Cython 0.20, the ``bytearray`` type is supported and Starting with Cython 0.20, the :obj:`bytearray` type is supported and
coerces in the same way as the ``bytes`` type. However, when using it coerces in the same way as the :obj:`bytes` type. However, when using it
in a C context, special care must be taken not to grow or shrink the in a C context, special care must be taken not to grow or shrink the
object buffer after converting it to a C string pointer. These object buffer after converting it to a C string pointer. These
modifications can change the internal buffer address, which will make modifications can change the internal buffer address, which will make
the pointer invalid. the pointer invalid.
Accepting strings from Python code
----------------------------------
The other side, receiving input from Python code, may appear simple
at first sight, as it only deals with objects. However, getting this
right without making the API too narrow or too unsafe may not be
entirely obvious.
In the case that the API only deals with byte strings, i.e. binary
data or encoded text, it is best not to type the input argument as
something like :obj:`bytes`, because that would restrict the allowed
input to exactly that type and exclude both subtypes and other kinds
of byte containers, e.g. :obj:`bytearray` objects or memory views.
Depending on how (and where) the data is being processed, it may be a
good idea to instead receive a 1-dimensional memory view, e.g.
::
def process_byte_data(unsigned char[:] data):
length = data.shape[0]
first_byte = data[0]
slice_view = data[1:-1]
...
Cython's memory views are described in more detail in
:doc:`../userguide/memoryviews`, but the above example already shows
most of the relevant functionality for 1-dimensional byte views. They
allow for efficient processing of arrays and accept anything that can
unpack itself into a byte buffer, without intermediate copying. The
processed content can finally be returned in the memory view itself
(or a slice of it), but it is often better to copy the data back into
a flat and simple :obj:`bytes` or :obj:`bytearray` object, especially
when only a small slice is returned. Since memoryviews do not copy the
data, they would otherwise keep the entire original buffer alive. The
general idea here is to be liberal with input by accepting any kind of
byte buffer, but strict with output by returning a simple, well adapted
object. This can simply be done as follows::
def process_byte_data(unsigned char[:] data):
# ... process the data
if return_all:
return bytes(data)
else:
# example for returning a slice
return bytes(data[5:35])
If the byte input is actually encoded text, and the further processing
should happen at the Unicode level, then the right thing to do is to
decode the input straight away. This is almost only a problem in Python
2.x, where Python code expects that it can pass a byte string (:obj:`str`)
with encoded text into a text API. Since this usually happens in more
than one place in the module's API, a helper function is almost always the
way to go, since it allows for easy adaptation of the input normalisation
process later.
This kind of input normalisation function will commonly look similar to
the following::
from cpython.version cimport PY_MAJOR_VERSION
cdef unicode _ustring(s):
if type(s) is unicode:
# fast path for most common case(s)
return <unicode>s
elif PY_MAJOR_VERSION < 3 and isinstance(s, bytes):
# only accept byte strings in Python 2.x, not in Py3
return (<bytes>s).decode('ascii')
elif isinstance(s, unicode):
# an evil cast to <unicode> might work here in some(!) cases,
# depending on what the further processing does. to be safe,
# we can always create a copy instead
return unicode(s)
else:
raise TypeError(...)
And should then be used like this::
def api_func(s):
text = _ustring(s)
...
Similarly, if the further processing happens at the byte level, but Unicode
string input should be accepted, then the following might work, if you are
using memory views::
# define a global name for whatever char type is used in the module
ctypedef unsigned char char_type
cdef char_type[:] _chars(s):
if isinstance(s, unicode):
# encode to the specific encoding used inside of the module
s = (<unicode>s).encode('utf8')
return s
In this case, you might want to additionally ensure that byte string
input really uses the correct encoding, e.g. if you require pure ASCII
input data, you can run over the buffer in a loop and check the highest
bit of each byte. This should then also be done in the input normalisation
function.
Dealing with "const" Dealing with "const"
-------------------- --------------------
...@@ -224,6 +327,7 @@ In Cython 0.18, these standard declarations have been changed to ...@@ -224,6 +327,7 @@ In Cython 0.18, these standard declarations have been changed to
use the correct ``const`` modifier, so your code will automatically use the correct ``const`` modifier, so your code will automatically
benefit from the new ``const`` support if it uses them. benefit from the new ``const`` support if it uses them.
Decoding bytes to text Decoding bytes to text
---------------------- ----------------------
...@@ -234,7 +338,7 @@ the C byte strings to Python Unicode strings on reception, and to ...@@ -234,7 +338,7 @@ the C byte strings to Python Unicode strings on reception, and to
encode Python Unicode strings to C byte strings on the way out. encode Python Unicode strings to C byte strings on the way out.
With a Python byte string object, you would normally just call the With a Python byte string object, you would normally just call the
``.decode()`` method to decode it into a Unicode string:: ``bytes.decode()`` method to decode it into a Unicode string::
ustring = byte_string.decode('UTF-8') ustring = byte_string.decode('UTF-8')
...@@ -318,6 +422,7 @@ assignment. Later access to the invalidated pointer will read invalid ...@@ -318,6 +422,7 @@ assignment. Later access to the invalidated pointer will read invalid
memory and likely result in a segfault. Cython will therefore refuse memory and likely result in a segfault. Cython will therefore refuse
to compile this code. to compile this code.
C++ strings C++ strings
----------- -----------
...@@ -375,7 +480,7 @@ There are two use cases where this is inconvenient. First, if all ...@@ -375,7 +480,7 @@ There are two use cases where this is inconvenient. First, if all
C strings that are being processed (or the large majority) contain C strings that are being processed (or the large majority) contain
text, automatic encoding and decoding from and to Python unicode text, automatic encoding and decoding from and to Python unicode
objects can reduce the code overhead a little. In this case, you objects can reduce the code overhead a little. In this case, you
can set the ``c_string_type`` directive in your module to ``unicode`` can set the ``c_string_type`` directive in your module to :obj:`unicode`
and the ``c_string_encoding`` to the encoding that your C code uses, and the ``c_string_encoding`` to the encoding that your C code uses,
for example:: for example::
...@@ -393,7 +498,7 @@ The second use case is when all C strings that are being processed ...@@ -393,7 +498,7 @@ The second use case is when all C strings that are being processed
only contain ASCII encodable characters (e.g. numbers) and you want only contain ASCII encodable characters (e.g. numbers) and you want
your code to use the native legacy string type in Python 2 for them, your code to use the native legacy string type in Python 2 for them,
instead of always using Unicode. In this case, you can set the instead of always using Unicode. In this case, you can set the
string type to ``str``:: string type to :obj:`str`::
# cython: c_string_type=str, c_string_encoding=ascii # cython: c_string_type=str, c_string_encoding=ascii
...@@ -472,15 +577,15 @@ whereas the following ``ISO-8859-15`` encoded source file will print ...@@ -472,15 +577,15 @@ whereas the following ``ISO-8859-15`` encoded source file will print
Note that the unicode literal ``u'abcö'`` is a correctly decoded four Note that the unicode literal ``u'abcö'`` is a correctly decoded four
character Unicode string in both cases, whereas the unprefixed Python character Unicode string in both cases, whereas the unprefixed Python
``str`` literal ``'abcö'`` will become a byte string in Python 2 (thus :obj:`str` literal ``'abcö'`` will become a byte string in Python 2 (thus
having length 4 or 5 in the examples above), and a 4 character Unicode having length 4 or 5 in the examples above), and a 4 character Unicode
string in Python 3. If you are not familiar with encodings, this may string in Python 3. If you are not familiar with encodings, this may
not appear obvious at first read. See `CEP 108`_ for details. not appear obvious at first read. See `CEP 108`_ for details.
As a rule of thumb, it is best to avoid unprefixed non-ASCII ``str`` As a rule of thumb, it is best to avoid unprefixed non-ASCII :obj:`str`
literals and to use unicode string literals for all text. Cython also literals and to use unicode string literals for all text. Cython also
supports the ``__future__`` import ``unicode_literals`` that instructs supports the ``__future__`` import ``unicode_literals`` that instructs
the parser to read all unprefixed ``str`` literals in a source file as the parser to read all unprefixed :obj:`str` literals in a source file as
unicode string literals, just like Python 3. unicode string literals, just like Python 3.
.. _`CEP 108`: http://wiki.cython.org/enhancements/stringliterals .. _`CEP 108`: http://wiki.cython.org/enhancements/stringliterals
...@@ -522,7 +627,7 @@ explicitly, and the following will print ``A`` (or ``b'A'`` in Python ...@@ -522,7 +627,7 @@ explicitly, and the following will print ``A`` (or ``b'A'`` in Python
The explicit coercion works for any C integer type. Values outside of The explicit coercion works for any C integer type. Values outside of
the range of a :c:type:`char` or :c:type:`unsigned char` will raise an the range of a :c:type:`char` or :c:type:`unsigned char` will raise an
``OverflowError`` at runtime. Coercion will also happen automatically :obj:`OverflowError` at runtime. Coercion will also happen automatically
when assigning to a typed variable, e.g.:: when assigning to a typed variable, e.g.::
cdef bytes py_byte_string cdef bytes py_byte_string
...@@ -544,10 +649,10 @@ The following will print 65:: ...@@ -544,10 +649,10 @@ The following will print 65::
cdef Py_UCS4 uchar_val = u'A' cdef Py_UCS4 uchar_val = u'A'
print( <long>uchar_val ) print( <long>uchar_val )
Note that casting to a C ``long`` (or ``unsigned long``) will work Note that casting to a C :c:type:`long` (or :c:type:`unsigned long`) will work
just fine, as the maximum code point value that a Unicode character just fine, as the maximum code point value that a Unicode character
can have is 1114111 (``0x10FFFF``). On platforms with 32bit or more, can have is 1114111 (``0x10FFFF``). On platforms with 32bit or more,
``int`` is just as good. :c:type:`int` is just as good.
Narrow Unicode builds Narrow Unicode builds
...@@ -682,15 +787,15 @@ zero-terminated UTF-16 encoded :c:type:`wchar_t*` strings, so called ...@@ -682,15 +787,15 @@ zero-terminated UTF-16 encoded :c:type:`wchar_t*` strings, so called
"wide strings". "wide strings".
By default, Windows builds of CPython define :c:type:`Py_UNICODE` as By default, Windows builds of CPython define :c:type:`Py_UNICODE` as
a synonym for :c:type:`wchar_t`. This makes internal ``unicode`` a synonym for :c:type:`wchar_t`. This makes internal :obj:`unicode`
representation compatible with UTF-16 and allows for efficient zero-copy representation compatible with UTF-16 and allows for efficient zero-copy
conversions. This also means that Windows builds are always conversions. This also means that Windows builds are always
`Narrow Unicode builds`_ with all the caveats. `Narrow Unicode builds`_ with all the caveats.
To aid interoperation with Windows APIs, Cython 0.19 supports wide To aid interoperation with Windows APIs, Cython 0.19 supports wide
strings (in the form of :c:type:`Py_UNICODE*`) and implicitly converts strings (in the form of :c:type:`Py_UNICODE*`) and implicitly converts
them to and from ``unicode`` string objects. These conversions behave the them to and from :obj:`unicode` string objects. These conversions behave the
same way as they do for :c:type:`char*` and ``bytes`` as described in same way as they do for :c:type:`char*` and :obj:`bytes` as described in
`Passing byte strings`_. `Passing byte strings`_.
In addition to automatic conversion, unicode literals that appear In addition to automatic conversion, unicode literals that appear
...@@ -722,7 +827,7 @@ Here is an example of how one would call a Unicode API on Windows:: ...@@ -722,7 +827,7 @@ Here is an example of how one would call a Unicode API on Windows::
APIs deprecated and inefficient. APIs deprecated and inefficient.
One consequence of CPython 3.3 changes is that :py:func:`len` of One consequence of CPython 3.3 changes is that :py:func:`len` of
``unicode`` strings is always measured in *code points* ("characters"), :obj:`unicode` strings is always measured in *code points* ("characters"),
while Windows API expect the number of UTF-16 *code units* while Windows API expect the number of UTF-16 *code units*
(where each surrogate is counted individually). To always get the number (where each surrogate is counted individually). To always get the number
of code units, call :c:func:`PyUnicode_GetSize` directly. of code units, call :c:func:`PyUnicode_GetSize` directly.
...@@ -207,6 +207,11 @@ EXT_EXTRAS = { ...@@ -207,6 +207,11 @@ EXT_EXTRAS = {
'tag:trace': update_linetrace_extension, 'tag:trace': update_linetrace_extension,
} }
def _is_py3_before_32(excluded, version):
return version[0] >= 3 and version < (3,2)
# TODO: use tags # TODO: use tags
VER_DEP_MODULES = { VER_DEP_MODULES = {
# tests are excluded if 'CurrentPythonVersion OP VersionTuple', i.e. # tests are excluded if 'CurrentPythonVersion OP VersionTuple', i.e.
...@@ -248,10 +253,12 @@ VER_DEP_MODULES = { ...@@ -248,10 +253,12 @@ VER_DEP_MODULES = {
'memoryview.numpy_memoryview', 'memoryview.numpy_memoryview',
'memoryview.memoryviewattrs', 'memoryview.memoryviewattrs',
'memoryview.memoryview', 'memoryview.memoryview',
'run.withstat_py',
]), ]),
(2,7) : (operator.lt, lambda x: x in ['run.withstat_py', # multi context with statement (2,7) : (operator.lt, lambda x: x in ['run.withstat_py27', # multi context with statement
'run.yield_inside_lambda', 'run.yield_inside_lambda',
'run.test_dictviews', 'run.test_dictviews',
'run.pyclass_special_methods',
]), ]),
# The next line should start (3,); but this is a dictionary, so # The next line should start (3,); but this is a dictionary, so
# we can only have one (3,) key. Since 2.7 is supposed to be the # we can only have one (3,) key. Since 2.7 is supposed to be the
...@@ -263,9 +270,14 @@ VER_DEP_MODULES = { ...@@ -263,9 +270,14 @@ VER_DEP_MODULES = {
(3,): (operator.ge, lambda x: x in ['run.non_future_division', (3,): (operator.ge, lambda x: x in ['run.non_future_division',
'compile.extsetslice', 'compile.extsetslice',
'compile.extdelslice', 'compile.extdelslice',
'run.special_methods_T561_py2']), 'run.special_methods_T561_py2'
]),
(3,1): (_is_py3_before_32, lambda x: x in ['run.pyclass_special_methods',
]),
(3,3) : (operator.lt, lambda x: x in ['build.package_compilation', (3,3) : (operator.lt, lambda x: x in ['build.package_compilation',
]), ]),
(3,4,0,'beta',3) : (operator.le, lambda x: x in ['run.py34_signature',
]),
} }
# files that should not be converted to Python 3 code with 2to3 # files that should not be converted to Python 3 code with 2to3
...@@ -332,9 +344,18 @@ def parse_tags(filepath): ...@@ -332,9 +344,18 @@ def parse_tags(filepath):
f.close() f.close()
return tags return tags
list_unchanging_dir = memoize(lambda x: os.listdir(x)) list_unchanging_dir = memoize(lambda x: os.listdir(x))
@memoize
def _list_pyregr_data_files(test_directory):
is_data_file = re.compile('(?:[.](txt|pem|db|html)|^bad.*[.]py)$').search
return ['__init__.py'] + [
filename for filename in list_unchanging_dir(test_directory)
if is_data_file(filename)]
def import_ext(module_name, file_path=None): def import_ext(module_name, file_path=None):
if file_path: if file_path:
import imp import imp
...@@ -514,18 +535,18 @@ class TestBuilder(object): ...@@ -514,18 +535,18 @@ 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')
tests = [ self.build_test(test_class, path, workdir, module, tests = [ self.build_test(test_class, path, workdir, module, tags,
language, expect_errors, warning_errors) language, expect_errors, warning_errors)
for language in languages ] for language in languages ]
return tests return tests
def build_test(self, test_class, path, workdir, module, def build_test(self, test_class, path, workdir, module, tags,
language, expect_errors, warning_errors): language, expect_errors, warning_errors):
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)
return test_class(path, workdir, module, return test_class(path, workdir, module, tags,
language=language, language=language,
expect_errors=expect_errors, expect_errors=expect_errors,
annotate=self.annotate, annotate=self.annotate,
...@@ -538,11 +559,12 @@ class TestBuilder(object): ...@@ -538,11 +559,12 @@ 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, language='c', def __init__(self, test_directory, workdir, module, tags, language='c',
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):
self.test_directory = test_directory self.test_directory = test_directory
self.tags = tags
self.workdir = workdir self.workdir = workdir
self.module = module self.module = module
self.language = language self.language = language
...@@ -603,7 +625,7 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -603,7 +625,7 @@ class CythonCompileTestCase(unittest.TestCase):
if not cleanup_c_files: if not cleanup_c_files:
if (rmfile[-2:] in (".c", ".h") or if (rmfile[-2:] in (".c", ".h") or
rmfile[-4:] == ".cpp" or rmfile[-4:] == ".cpp" or
rmfile.endswith(".html")): rmfile.endswith(".html") and rmfile.startswith(self.module)):
continue continue
if not cleanup_lib_files and (rmfile.endswith(".so") or rmfile.endswith(".dll")): if not cleanup_lib_files and (rmfile.endswith(".so") or rmfile.endswith(".dll")):
continue continue
...@@ -641,9 +663,17 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -641,9 +663,17 @@ 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
try:
copy = os.symlink
except AttributeError:
copy = shutil.copy
join = os.path.join
for filename in file_list: for filename in file_list:
shutil.copy(os.path.join(test_directory, filename), file_path = join(test_directory, filename)
target_directory) if os.path.exists(file_path):
copy(file_path, join(target_directory, filename))
def source_files(self, workdir, module_name, file_list): def source_files(self, workdir, module_name, file_list):
return ([self.build_target_filename(module_name)] + return ([self.build_target_filename(module_name)] +
...@@ -708,12 +738,6 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -708,12 +738,6 @@ class CythonCompileTestCase(unittest.TestCase):
def run_distutils(self, test_directory, module, workdir, incdir, def run_distutils(self, test_directory, module, workdir, incdir,
extra_extension_args=None): extra_extension_args=None):
original_source = self.find_module_source_file(
os.path.join(test_directory, module + '.pyx'))
try:
tags = parse_tags(original_source)
except IOError:
tags = {}
cwd = os.getcwd() cwd = os.getcwd()
os.chdir(workdir) os.chdir(workdir)
try: try:
...@@ -748,12 +772,13 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -748,12 +772,13 @@ class CythonCompileTestCase(unittest.TestCase):
# Set the language now as the fixer might need it # Set the language now as the fixer might need it
extension.language = 'c++' extension.language = 'c++'
for matcher, fixer in EXT_EXTRAS.items(): for matcher, fixer in list(EXT_EXTRAS.items()):
if isinstance(matcher, str): if isinstance(matcher, str):
# lazy init
del EXT_EXTRAS[matcher] del EXT_EXTRAS[matcher]
matcher = string_selector(matcher) matcher = string_selector(matcher)
EXT_EXTRAS[matcher] = fixer EXT_EXTRAS[matcher] = fixer
if matcher(module, tags): if matcher(module, self.tags):
newext = fixer(extension) newext = fixer(extension)
if newext is EXCLUDE_EXT: if newext is EXCLUDE_EXT:
return return
...@@ -891,9 +916,10 @@ def run_forked_test(result, run_func, test_name, fork=True): ...@@ -891,9 +916,10 @@ def run_forked_test(result, run_func, test_name, fork=True):
child_id = os.fork() child_id = os.fork()
if not child_id: if not child_id:
result_code = 0 result_code = 0
output = None
try: try:
try: try:
tests = None tests = partial_result = None
try: try:
partial_result = PartialTestResult(result) partial_result = PartialTestResult(result)
run_func(partial_result) run_func(partial_result)
...@@ -901,6 +927,8 @@ def run_forked_test(result, run_func, test_name, fork=True): ...@@ -901,6 +927,8 @@ def run_forked_test(result, run_func, test_name, fork=True):
sys.stderr.flush() sys.stderr.flush()
gc.collect() gc.collect()
except Exception: except Exception:
result_code = 1
if partial_result is not None:
if tests is None: if tests is None:
# importing failed, try to fake a test class # importing failed, try to fake a test class
tests = _FakeClass( tests = _FakeClass(
...@@ -908,14 +936,20 @@ def run_forked_test(result, run_func, test_name, fork=True): ...@@ -908,14 +936,20 @@ def run_forked_test(result, run_func, test_name, fork=True):
_shortDescription=test_name, _shortDescription=test_name,
module_name=None) module_name=None)
partial_result.addError(tests, sys.exc_info()) partial_result.addError(tests, sys.exc_info())
result_code = 1
output = open(result_file, 'wb') output = open(result_file, 'wb')
pickle.dump(partial_result.data(), output) pickle.dump(partial_result.data(), output)
except: except:
traceback.print_exc() traceback.print_exc()
finally: finally:
try: output.close() try: sys.stderr.flush()
except: pass except: pass
try: sys.stdout.flush()
except: pass
try:
if output is not None:
output.close()
except:
pass
os._exit(result_code) os._exit(result_code)
try: try:
...@@ -1048,6 +1082,9 @@ class CythonPyregrTestCase(CythonRunTestCase): ...@@ -1048,6 +1082,9 @@ class CythonPyregrTestCase(CythonRunTestCase):
set_initial_path="SOURCEFILE")) set_initial_path="SOURCEFILE"))
patch_inspect_isfunction() patch_inspect_isfunction()
def related_files(self, test_directory, module_name):
return _list_pyregr_data_files(test_directory)
def _run_unittest(self, result, *classes): def _run_unittest(self, result, *classes):
"""Run tests from unittest.TestCase-derived classes.""" """Run tests from unittest.TestCase-derived classes."""
valid_types = (unittest.TestSuite, unittest.TestCase) valid_types = (unittest.TestSuite, unittest.TestCase)
...@@ -1677,13 +1714,13 @@ def main(): ...@@ -1677,13 +1714,13 @@ def main():
_, return_code = runtests(options, cmd_args, coverage) _, return_code = runtests(options, cmd_args, coverage)
print("ALL DONE") print("ALL DONE")
try: try:
check_thread_termination(ignore_seen=False) check_thread_termination(ignore_seen=False)
sys.exit(return_code)
except PendingThreadsError: except PendingThreadsError:
# normal program exit won't kill the threads, do it the hard way here # normal program exit won't kill the threads, do it the hard way here
flush_and_terminate(return_code) flush_and_terminate(return_code)
else:
sys.exit(return_code)
def runtests_callback(args): def runtests_callback(args):
......
...@@ -61,11 +61,10 @@ import a, b, c ...@@ -61,11 +61,10 @@ import a, b, c
######## fake_grep.py ######## ######## fake_grep.py ########
import platform
import re import re
import sys import sys
if platform == 'Windows': if sys.platform == 'win32':
opt, pattern, file = sys.argv[1:] opt, pattern, file = sys.argv[1:]
assert opt == '-c' assert opt == '-c'
count = 0 count = 0
......
...@@ -175,6 +175,7 @@ def test_cdef_attribute(): ...@@ -175,6 +175,7 @@ def test_cdef_attribute():
>>> test_cdef_attribute() >>> test_cdef_attribute()
Memoryview is not initialized Memoryview is not initialized
local variable 'myview' referenced before assignment local variable 'myview' referenced before assignment
local variable 'myview' referenced before assignment
get_ext_obj called get_ext_obj called
Memoryview is not initialized Memoryview is not initialized
<MemoryView of 'array' object> <MemoryView of 'array' object>
...@@ -195,8 +196,11 @@ def test_cdef_attribute(): ...@@ -195,8 +196,11 @@ def test_cdef_attribute():
else: else:
print "No UnboundLocalError was raised" print "No UnboundLocalError was raised"
# uninitialized assignment is valid cdef int[:] otherview
cdef int[:] otherview = myview try:
otherview = myview
except UnboundLocalError, e:
print e.args[0]
try: try:
print get_ext_obj().mview print get_ext_obj().mview
......
...@@ -406,7 +406,7 @@ cdef class DeallocateMe(object): ...@@ -406,7 +406,7 @@ cdef class DeallocateMe(object):
# Disabled! References cycles don't seem to be supported by NumPy # Disabled! References cycles don't seem to be supported by NumPy
# @testcase # @testcase
def acquire_release_cycle(obj): def acquire_release_cycle(obj):
""" DISABLED_DOCSTRING = """
>>> a = np.arange(20, dtype=np.object) >>> a = np.arange(20, dtype=np.object)
>>> a[10] = DeallocateMe() >>> a[10] = DeallocateMe()
>>> acquire_release_cycle(a) >>> acquire_release_cycle(a)
......
...@@ -20,6 +20,40 @@ def min3(a,b,c): ...@@ -20,6 +20,40 @@ def min3(a,b,c):
""" """
return min(a,b,c) return min(a,b,c)
@cython.test_assert_path_exists("//CondExprNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
def min3_list(a,b,c):
"""
>>> min3_list(1,2,3)
1
>>> min3_list(2,3,1)
1
>>> min3_list(2,1,3)
1
>>> min3_list(3,1,2)
1
>>> min3_list(3,2,1)
1
"""
return min([a,b,c])
@cython.test_assert_path_exists("//CondExprNode")
@cython.test_fail_if_path_exists("//SimpleCallNode")
def min3_tuple(a,b,c):
"""
>>> min3_tuple(1,2,3)
1
>>> min3_tuple(2,3,1)
1
>>> min3_tuple(2,1,3)
1
>>> min3_tuple(3,1,2)
1
>>> min3_tuple(3,2,1)
1
"""
return min((a,b,c))
@cython.test_assert_path_exists("//CondExprNode") @cython.test_assert_path_exists("//CondExprNode")
@cython.test_fail_if_path_exists("//SimpleCallNode") @cython.test_fail_if_path_exists("//SimpleCallNode")
def min3_typed(int a, int b, int c): def min3_typed(int a, int b, int c):
......
...@@ -199,7 +199,7 @@ def bytearray_decode_unbound_method(bytearray s, start=None, stop=None): ...@@ -199,7 +199,7 @@ def bytearray_decode_unbound_method(bytearray s, start=None, stop=None):
return bytearray.decode(s[start:stop], 'utf8') return bytearray.decode(s[start:stop], 'utf8')
def bytearray_append(bytearray b, char c, int i, object o): def bytearray_append(bytearray b, signed char c, int i, object o):
""" """
>>> b = bytearray(b'abc') >>> b = bytearray(b'abc')
>>> b = bytearray_append(b, ord('x'), ord('y'), ord('z')) >>> b = bytearray_append(b, ord('x'), ord('y'), ord('z'))
......
...@@ -4,6 +4,56 @@ ...@@ -4,6 +4,56 @@
import sys import sys
IS_PY3 = sys.version_info[0] >= 3 IS_PY3 = sys.version_info[0] >= 3
IS_PY34 = sys.version_info > (3, 4, 0, 'beta', 3)
def inspect_isroutine():
"""
>>> inspect_isroutine()
True
"""
import inspect
return inspect.isroutine(inspect_isroutine)
def inspect_isfunction():
"""
>>> inspect_isfunction()
False
False
"""
import inspect, types
print isinstance(inspect_isfunction, types.FunctionType)
return inspect.isfunction(inspect_isfunction)
def inspect_isbuiltin():
"""
>>> inspect_isbuiltin()
False
False
"""
import inspect, types
print isinstance(inspect_isfunction, types.BuiltinFunctionType)
return inspect.isbuiltin(inspect_isbuiltin)
def inspect_signature(a, b, c=123, *, d=234):
"""
>>> sig = inspect_signature(1, 2)
>>> if IS_PY34: list(sig.parameters)
... else: ['a', 'b', 'c', 'd']
['a', 'b', 'c', 'd']
>>> if IS_PY34: sig.parameters['c'].default == 123
... else: True
True
>>> if IS_PY34: sig.parameters['d'].default == 234
... else: True
True
"""
import inspect
return inspect.signature(inspect_signature) if IS_PY34 else None
def test_dict(): def test_dict():
""" """
...@@ -42,6 +92,19 @@ def test_doc(): ...@@ -42,6 +92,19 @@ def test_doc():
'docstring' 'docstring'
""" """
def test_hash():
"""
>>> d = {test_hash: 123}
>>> test_hash in d
True
>>> d[test_hash]
123
>>> hash(test_hash) == hash(test_hash)
True
"""
def test_closure(): def test_closure():
""" """
>>> test_closure.func_closure is None >>> test_closure.func_closure is None
......
...@@ -4,6 +4,8 @@ import sys ...@@ -4,6 +4,8 @@ import sys
if sys.version_info >= (3, 4): if sys.version_info >= (3, 4):
def funcdoc(f): def funcdoc(f):
if not f.__text_signature__:
return f.__doc__
doc = '%s%s' % (f.__name__, f.__text_signature__) doc = '%s%s' % (f.__name__, f.__text_signature__)
if f.__doc__: if f.__doc__:
if '\n' in f.__doc__: if '\n' in f.__doc__:
......
__doc__ = u""" __doc__ = u"""
>>> D >>> D
2 2
>>> XYZ
5
""" """
D = 1 D = 1
include "testinclude.pxi" include "testinclude.pxi"
include "includes/includefile.pxi"
# this file will be included
XYZ = 5
...@@ -286,6 +286,11 @@ def conditional_none(int a): ...@@ -286,6 +286,11 @@ def conditional_none(int a):
""" """
return None if a in {1,2,3,4} else 1 return None if a in {1,2,3,4} else 1
@cython.test_assert_path_exists(
"//BoolBinopNode",
"//BoolBinopNode//PrimaryCmpNode"
)
@cython.test_fail_if_path_exists("//ListNode")
def n(a): def n(a):
""" """
>>> n('d *') >>> n('d *')
......
# cython: binding=True, language_level=3
# mode: run
# tag: cyfunction
import inspect
sig = inspect.Signature.from_function
def signatures_match(f1, f2):
if sig(f1) == sig(f2):
return None # nothing to show in doctest
return sig(f1), sig(f2)
def b(a, b, c):
"""
>>> def py_b(a, b, c): pass
>>> signatures_match(b, py_b)
"""
def c(a, b, c=1):
"""
>>> def py_c(a, b, c=1): pass
>>> signatures_match(c, py_c)
"""
def d(a, b, *, c = 88):
"""
>>> def py_d(a, b, *, c = 88): pass
>>> signatures_match(d, py_d)
"""
def e(a, b, c = 88, **kwds):
"""
>>> def py_e(a, b, c = 88, **kwds): pass
>>> signatures_match(e, py_e)
"""
def f(a, b, *, c, d = 42):
"""
>>> def py_f(a, b, *, c, d = 42): pass
>>> signatures_match(f, py_f)
"""
def g(a, b, *, c, d = 42, e = 17, f, **kwds):
"""
>>> def py_g(a, b, *, c, d = 42, e = 17, f, **kwds): pass
>>> signatures_match(g, py_g)
"""
def h(a, b, *args, c, d = 42, e = 17, f, **kwds):
"""
>>> def py_h(a, b, *args, c, d = 42, e = 17, f, **kwds): pass
>>> signatures_match(h, py_h)
"""
def k(a, b, c=1, *args, d = 42, e = 17, f, **kwds):
"""
>>> def py_k(a, b, c=1, *args, d = 42, e = 17, f, **kwds): pass
>>> signatures_match(k, py_k)
"""
def l(*, a, b, c = 88):
"""
>>> def py_l(*, a, b, c = 88): pass
>>> signatures_match(l, py_l)
"""
def m(a, *, b, c = 88):
"""
>>> def py_m(a, *, b, c = 88): pass
>>> signatures_match(m, py_m)
"""
a, b, c = b, c, a
def n(a, *, b, c = 88):
"""
>>> def py_n(a, *, b, c = 88): pass
>>> signatures_match(n, py_n)
"""
...@@ -647,3 +647,27 @@ def self_lookup(a): ...@@ -647,3 +647,27 @@ def self_lookup(a):
def bar(foo): def bar(foo):
qux = foo qux = foo
quux = foo[qux.baz] quux = foo[qux.baz]
cdef enum MyEnum:
enum_x = 1
enum_y = 2
cdef class InferInProperties:
"""
>>> InferInProperties().x
('double', 'unicode object', 'MyEnum', 'MyEnum')
"""
cdef MyEnum attr
def __cinit__(self):
self.attr = enum_x
property x:
def __get__(self):
a = 1.0
b = u'abc'
c = self.attr
d = enum_y
c = d
return typeof(a), typeof(b), typeof(c), typeof(d)
...@@ -26,6 +26,37 @@ def test_object_assmt(): ...@@ -26,6 +26,37 @@ def test_object_assmt():
assert typeof(a) == "Python object", typeof(a) assert typeof(a) == "Python object", typeof(a)
assert typeof(b) == "long", typeof(b) assert typeof(b) == "long", typeof(b)
class RAdd(object):
other = None
def __radd__(self, other):
self._other = other
return self
def __repr__(self):
return '%s(%s)' % (type(self).__name__, self._other)
def test_inplace_assignment():
"""
>>> test_inplace_assignment()
RAdd([1, 2, 3])
"""
l = [1, 2, 3]
# inferred type of l is list, but assignment result is object
l += RAdd()
return l
def test_reassignment():
"""
>>> test_reassignment()
(1, 2, 3)
"""
l = [1, 2, 3]
l = (1, 2, 3)
return l
def test_long_vs_double(cond): def test_long_vs_double(cond):
""" """
>>> test_long_vs_double(0) >>> test_long_vs_double(0)
......
...@@ -129,3 +129,51 @@ def test_class(cond): ...@@ -129,3 +129,51 @@ def test_class(cond):
class A: class A:
x = 1 x = 1
return A.x return A.x
def test_try_except_regression(c):
"""
>>> test_try_except_regression(True)
(123,)
>>> test_try_except_regression(False)
Traceback (most recent call last):
...
UnboundLocalError: local variable 'a' referenced before assignment
"""
if c:
a = (123,)
try:
return a
except:
return a
def test_try_finally_regression(c):
"""
>>> test_try_finally_regression(True)
(123,)
>>> test_try_finally_regression(False)
Traceback (most recent call last):
...
UnboundLocalError: local variable 'a' referenced before assignment
"""
if c:
a = (123,)
try:
return a
finally:
return a
def test_expression_calculation_order_bug(a):
"""
>>> test_expression_calculation_order_bug(False)
[]
>>> test_expression_calculation_order_bug(True)
Traceback (most recent call last):
...
UnboundLocalError: local variable 'b' referenced before assignment
"""
if not a:
b = []
return (a or b) and (b or a)
import sys import sys
def typename(t): def typename(t):
name = type(t).__name__ name = type(t).__name__
if sys.version_info < (2,5): if sys.version_info < (2,5):
...@@ -9,9 +10,11 @@ def typename(t): ...@@ -9,9 +10,11 @@ def typename(t):
name = 'MyException' name = 'MyException'
return "<type '%s'>" % name return "<type '%s'>" % name
class MyException(Exception): class MyException(Exception):
pass pass
class ContextManager(object): class ContextManager(object):
def __init__(self, value, exit_ret = None): def __init__(self, value, exit_ret = None):
self.value = value self.value = value
...@@ -25,6 +28,7 @@ class ContextManager(object): ...@@ -25,6 +28,7 @@ class ContextManager(object):
print("enter") print("enter")
return self.value return self.value
def no_as(): def no_as():
""" """
>>> no_as() >>> no_as()
...@@ -35,6 +39,7 @@ def no_as(): ...@@ -35,6 +39,7 @@ def no_as():
with ContextManager("value"): with ContextManager("value"):
print("hello") print("hello")
def basic(): def basic():
""" """
>>> basic() >>> basic()
...@@ -45,6 +50,7 @@ def basic(): ...@@ -45,6 +50,7 @@ def basic():
with ContextManager("value") as x: with ContextManager("value") as x:
print(x) print(x)
def with_pass(): def with_pass():
""" """
>>> with_pass() >>> with_pass()
...@@ -54,6 +60,7 @@ def with_pass(): ...@@ -54,6 +60,7 @@ def with_pass():
with ContextManager("value") as x: with ContextManager("value") as x:
pass pass
def with_return(): def with_return():
""" """
>>> print(with_return()) >>> print(with_return())
...@@ -64,6 +71,7 @@ def with_return(): ...@@ -64,6 +71,7 @@ def with_return():
with ContextManager("value") as x: with ContextManager("value") as x:
return x return x
def with_break(): def with_break():
""" """
>>> print(with_break()) >>> print(with_break())
...@@ -77,6 +85,7 @@ def with_break(): ...@@ -77,6 +85,7 @@ def with_break():
print("FAILED") print("FAILED")
return c return c
def with_continue(): def with_continue():
""" """
>>> print(with_continue()) >>> print(with_continue())
...@@ -94,6 +103,7 @@ def with_continue(): ...@@ -94,6 +103,7 @@ def with_continue():
print("FAILED") print("FAILED")
return c return c
def with_exception(exit_ret): def with_exception(exit_ret):
""" """
>>> with_exception(None) >>> with_exception(None)
...@@ -113,6 +123,25 @@ def with_exception(exit_ret): ...@@ -113,6 +123,25 @@ def with_exception(exit_ret):
except: except:
print("outer except") print("outer except")
def with_real_lock():
"""
>>> with_real_lock()
about to acquire lock
holding lock
lock no longer held
"""
from threading import Lock
lock = Lock()
print("about to acquire lock")
with lock:
print("holding lock")
print("lock no longer held")
def functions_in_with(): def functions_in_with():
""" """
>>> f = functions_in_with() >>> f = functions_in_with()
...@@ -133,6 +162,7 @@ def functions_in_with(): ...@@ -133,6 +162,7 @@ def functions_in_with():
print("outer except") print("outer except")
return f return f
def multitarget(): def multitarget():
""" """
>>> multitarget() >>> multitarget()
...@@ -143,6 +173,7 @@ def multitarget(): ...@@ -143,6 +173,7 @@ def multitarget():
with ContextManager((1, 2, (3, (4, 5)))) as (a, b, (c, (d, e))): with ContextManager((1, 2, (3, (4, 5)))) as (a, b, (c, (d, e))):
print('%s %s %s %s %s' % (a, b, c, d, e)) print('%s %s %s %s %s' % (a, b, c, d, e))
def tupletarget(): def tupletarget():
""" """
>>> tupletarget() >>> tupletarget()
...@@ -153,39 +184,12 @@ def tupletarget(): ...@@ -153,39 +184,12 @@ def tupletarget():
with ContextManager((1, 2, (3, (4, 5)))) as t: with ContextManager((1, 2, (3, (4, 5)))) as t:
print(t) print(t)
def multimanager():
"""
>>> multimanager()
enter
enter
enter
enter
enter
enter
2
value
1 2 3 4 5
nested
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
"""
with ContextManager(1), ContextManager(2) as x, ContextManager('value') as y,\
ContextManager(3), ContextManager((1, 2, (3, (4, 5)))) as (a, b, (c, (d, e))):
with ContextManager('nested') as nested:
print(x)
print(y)
print('%s %s %s %s %s' % (a, b, c, d, e))
print(nested)
class GetManager(object): class GetManager(object):
def get(self, *args): def get(self, *args):
return ContextManager(*args) return ContextManager(*args)
def manager_from_expression(): def manager_from_expression():
""" """
>>> manager_from_expression() >>> manager_from_expression()
...@@ -201,94 +205,3 @@ def manager_from_expression(): ...@@ -201,94 +205,3 @@ def manager_from_expression():
g = GetManager() g = GetManager()
with g.get(2) as x: with g.get(2) as x:
print(x) print(x)
# Tests borrowed from pyregr test_with.py,
# modified to follow the constraints of Cython.
import unittest
class Dummy(object):
def __init__(self, value=None, gobble=False):
if value is None:
value = self
self.value = value
self.gobble = gobble
self.enter_called = False
self.exit_called = False
def __enter__(self):
self.enter_called = True
return self.value
def __exit__(self, *exc_info):
self.exit_called = True
self.exc_info = exc_info
if self.gobble:
return True
class InitRaises(object):
def __init__(self): raise RuntimeError()
class EnterRaises(object):
def __enter__(self): raise RuntimeError()
def __exit__(self, *exc_info): pass
class ExitRaises(object):
def __enter__(self): pass
def __exit__(self, *exc_info): raise RuntimeError()
class NestedWith(unittest.TestCase):
"""
>>> NestedWith().runTest()
"""
def runTest(self):
self.testNoExceptions()
self.testExceptionInExprList()
self.testExceptionInEnter()
self.testExceptionInExit()
self.testEnterReturnsTuple()
def testNoExceptions(self):
with Dummy() as a, Dummy() as b:
self.assertTrue(a.enter_called)
self.assertTrue(b.enter_called)
self.assertTrue(a.exit_called)
self.assertTrue(b.exit_called)
def testExceptionInExprList(self):
try:
with Dummy() as a, InitRaises():
pass
except:
pass
self.assertTrue(a.enter_called)
self.assertTrue(a.exit_called)
def testExceptionInEnter(self):
try:
with Dummy() as a, EnterRaises():
self.fail('body of bad with executed')
except RuntimeError:
pass
else:
self.fail('RuntimeError not reraised')
self.assertTrue(a.enter_called)
self.assertTrue(a.exit_called)
def testExceptionInExit(self):
body_executed = False
with Dummy(gobble=True) as a, ExitRaises():
body_executed = True
self.assertTrue(a.enter_called)
self.assertTrue(a.exit_called)
self.assertTrue(body_executed)
self.assertNotEqual(a.exc_info[0], None)
def testEnterReturnsTuple(self):
with Dummy(value=(1,2)) as (a1, a2), \
Dummy(value=(10, 20)) as (b1, b2):
self.assertEqual(1, a1)
self.assertEqual(2, a2)
self.assertEqual(10, b1)
self.assertEqual(20, b2)
import sys
def typename(t):
name = type(t).__name__
if sys.version_info < (2,5):
if name == 'classobj' and issubclass(t, MyException):
name = 'type'
elif name == 'instance' and isinstance(t, MyException):
name = 'MyException'
return "<type '%s'>" % name
class MyException(Exception):
pass
class ContextManager(object):
def __init__(self, value, exit_ret = None):
self.value = value
self.exit_ret = exit_ret
def __exit__(self, a, b, tb):
print("exit %s %s %s" % (typename(a), typename(b), typename(tb)))
return self.exit_ret
def __enter__(self):
print("enter")
return self.value
def multimanager():
"""
>>> multimanager()
enter
enter
enter
enter
enter
enter
2
value
1 2 3 4 5
nested
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
"""
with ContextManager(1), ContextManager(2) as x, ContextManager('value') as y,\
ContextManager(3), ContextManager((1, 2, (3, (4, 5)))) as (a, b, (c, (d, e))):
with ContextManager('nested') as nested:
print(x)
print(y)
print('%s %s %s %s %s' % (a, b, c, d, e))
print(nested)
class GetManager(object):
def get(self, *args):
return ContextManager(*args)
def manager_from_expression():
"""
>>> manager_from_expression()
enter
1
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
enter
2
exit <type 'NoneType'> <type 'NoneType'> <type 'NoneType'>
"""
with GetManager().get(1) as x:
print(x)
g = GetManager()
with g.get(2) as x:
print(x)
# Tests borrowed from pyregr test_with.py,
# modified to follow the constraints of Cython.
import unittest
class Dummy(object):
def __init__(self, value=None, gobble=False):
if value is None:
value = self
self.value = value
self.gobble = gobble
self.enter_called = False
self.exit_called = False
def __enter__(self):
self.enter_called = True
return self.value
def __exit__(self, *exc_info):
self.exit_called = True
self.exc_info = exc_info
if self.gobble:
return True
class InitRaises(object):
def __init__(self): raise RuntimeError()
class EnterRaises(object):
def __enter__(self): raise RuntimeError()
def __exit__(self, *exc_info): pass
class ExitRaises(object):
def __enter__(self): pass
def __exit__(self, *exc_info): raise RuntimeError()
class NestedWith(unittest.TestCase):
"""
>>> NestedWith().runTest()
"""
def runTest(self):
self.testNoExceptions()
self.testExceptionInExprList()
self.testExceptionInEnter()
self.testExceptionInExit()
self.testEnterReturnsTuple()
def testNoExceptions(self):
with Dummy() as a, Dummy() as b:
self.assertTrue(a.enter_called)
self.assertTrue(b.enter_called)
self.assertTrue(a.exit_called)
self.assertTrue(b.exit_called)
def testExceptionInExprList(self):
try:
with Dummy() as a, InitRaises():
pass
except:
pass
self.assertTrue(a.enter_called)
self.assertTrue(a.exit_called)
def testExceptionInEnter(self):
try:
with Dummy() as a, EnterRaises():
self.fail('body of bad with executed')
except RuntimeError:
pass
else:
self.fail('RuntimeError not reraised')
self.assertTrue(a.enter_called)
self.assertTrue(a.exit_called)
def testExceptionInExit(self):
body_executed = False
with Dummy(gobble=True) as a, ExitRaises():
body_executed = True
self.assertTrue(a.enter_called)
self.assertTrue(a.exit_called)
self.assertTrue(body_executed)
self.assertNotEqual(a.exc_info[0], None)
def testEnterReturnsTuple(self):
with Dummy(value=(1,2)) as (a1, a2), \
Dummy(value=(10, 20)) as (b1, b2):
self.assertEqual(1, a1)
self.assertEqual(2, a2)
self.assertEqual(10, b1)
self.assertEqual(20, b2)
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