Commit 91d8ecbc authored by Stefan Behnel's avatar Stefan Behnel

merged in latest cython-devel

parents deffc0ad 20de226f
......@@ -93,10 +93,13 @@ class AnnotationCCodeWriter(CCodeWriter):
body { font-family: courier; font-size: 12; }
.code { font-size: 9; color: #444444; display: none; margin-left: 20px; }
.py_api { color: red; }
.pyx_api { color: #FF3000; }
.py_macro_api { color: #FF8000; }
.error_goto { color: #FF8000; }
.py_c_api { color: red; }
.py_macro_api { color: #FF7000; }
.pyx_c_api { color: #FF3000; }
.pyx_macro_api { color: #FF7000; }
.refnanny { color: #FFA000; }
.error_goto { color: #FFA000; }
.tag { }
......@@ -126,10 +129,12 @@ function toggleDiv(id) {
f.write(u'<p>Raw output: <a href="%s">%s</a>\n' % (c_file, c_file))
k = 0
py_c_api = re.compile(u'(Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]+)')
pyx_api = re.compile(u'(__Pyx[A-Za-z_]+)\(')
py_marco_api = re.compile(u'(Py[A-Za-z]*_[A-Z][A-Z_]+)')
py_c_api = re.compile(u'(Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]+)\(')
py_marco_api = re.compile(u'(Py[A-Z][a-z]+_[A-Z][A-Z_]+)\(')
pyx_c_api = re.compile(u'(__Pyx_[A-Z][a-z_][A-Za-z_]+)\(')
pyx_macro_api = re.compile(u'(__Pyx_[A-Z][A-Z_]+)\(')
error_goto = re.compile(ur'((; *if .*)? \{__pyx_filename = .*goto __pyx_L\w+;\})')
refnanny = re.compile(u'(__Pyx_X?(GOT|GIVE)REF|__Pyx_RefNanny[A-Za-z]+)')
for line in lines:
......@@ -139,14 +144,17 @@ function toggleDiv(id) {
except KeyError:
code = ''
code, c_api_calls = py_c_api.subn(ur"<span class='py_api'>\1</span>", code)
code, pyx_api_calls = pyx_api.subn(ur"<span class='pyx_api'>\1</span>(", code)
code, macro_api_calls = py_marco_api.subn(ur"<span class='py_macro_api'>\1</span>", code)
code, py_c_api_calls = py_c_api.subn(ur"<span class='py_c_api'>\1</span>(", code)
code, pyx_c_api_calls = pyx_c_api.subn(ur"<span class='pyx_c_api'>\1</span>(", code)
code, py_macro_api_calls = py_marco_api.subn(ur"<span class='py_macro_api'>\1</span>(", code)
code, pyx_macro_api_calls = pyx_macro_api.subn(ur"<span class='pyx_macro_api'>\1</span>(", code)
code, refnanny_calls = refnanny.subn(ur"<span class='refnanny'>\1</span>", code)
code, error_goto_calls = error_goto.subn(ur"<span class='error_goto'>\1</span>", code)
code = code.replace(u"<span class='error_goto'>;", u";<span class='error_goto'>")
color = u"FFFF%02x" % int(255/(1+(5*c_api_calls+2*pyx_api_calls+macro_api_calls)/10.0))
score = 5*py_c_api_calls + 2*pyx_c_api_calls + py_macro_api_calls + pyx_macro_api_calls - refnanny_calls
color = u"FFFF%02x" % int(255/(1+score/10.0))
f.write(u"<pre class='line' style='background-color: #%s' onclick='toggleDiv(\"line%s\")'>" % (color, k))
f.write(u" %d: " % k)
......
......@@ -28,7 +28,7 @@ builtin_function_table = [
#('hex', "", "", ""),
#('id', "", "", ""),
#('input', "", "", ""),
('intern', "s", "O", "__Pyx_InternFromString"),
('intern', "O", "O", "__Pyx_Intern"),
('isinstance', "OO", "b", "PyObject_IsInstance"),
('issubclass', "OO", "b", "PyObject_IsSubclass"),
('iter', "O", "O", "PyObject_GetIter"),
......@@ -161,6 +161,9 @@ bad:
pyexec_utility_code = UtilityCode(
proto = """
#if PY_VERSION_HEX < 0x02040000
#ifndef Py_COMPILE_H
#include "compile.h"
#endif
#ifndef Py_EVAL_H
#include "eval.h"
#endif
......@@ -237,12 +240,23 @@ bad:
intern_utility_code = UtilityCode(
proto = """
#if PY_MAJOR_VERSION >= 3
# define __Pyx_InternFromString(s) PyUnicode_InternFromString(s)
#else
# define __Pyx_InternFromString(s) PyString_InternFromString(s)
#endif
""")
static PyObject* __Pyx_Intern(PyObject* s); /* proto */
""",
impl = '''
static PyObject* __Pyx_Intern(PyObject* s) {
if (!(likely(PyString_CheckExact(s)))) {
PyErr_Format(PyExc_TypeError, "Expected str, got %s", Py_TYPE(s)->tp_name);
return 0;
}
Py_INCREF(s);
#if PY_MAJOR_VERSION >= 3
PyUnicode_InternInPlace(&s);
#else
PyString_InternInPlace(&s);
#endif
return s;
}
''')
def put_py23_set_init_utility_code(code, pos):
code.putln("#if PY_VERSION_HEX < 0x02040000")
......
This diff is collapsed.
......@@ -93,6 +93,7 @@ class Context(object):
from AutoDocTransforms import EmbedSignature
from Optimize import FlattenInListTransform, SwitchTransform, IterationTransform
from Optimize import OptimizeBuiltinCalls, ConstantFolding, FinalOptimizePhase
from Optimize import DropRefcountingTransform
from Buffer import IntroduceBufferAuxiliaryVars
from ModuleNode import check_c_declarations, check_c_declarations_pxd
......@@ -140,6 +141,7 @@ class Context(object):
OptimizeBuiltinCalls(),
IterationTransform(),
SwitchTransform(),
DropRefcountingTransform(),
FinalOptimizePhase(self),
GilCheck(),
# ClearResultCodes(self),
......
......@@ -515,11 +515,14 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln(" #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask")
code.putln(" #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask")
code.putln(" #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y)")
code.putln(" #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y)")
code.putln("#else")
if Future.division in env.context.future_directives:
code.putln(" #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y)")
code.putln(" #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y)")
else:
code.putln(" #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y)")
code.putln(" #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceDivide(x,y)")
code.putln(" #define PyBytes_Type PyString_Type")
code.putln("#endif")
......@@ -871,7 +874,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if scope.defines_any(["__setitem__", "__delitem__"]):
self.generate_ass_subscript_function(scope, code)
if scope.defines_any(["__setslice__", "__delslice__"]):
warning(self.pos, "__setslice__ and __delslice__ are not supported by Python 3", 1)
warning(self.pos, "__setslice__ and __delslice__ are not supported by Python 3, use __setitem__ and __getitem__ instead", 1)
self.generate_ass_slice_function(scope, code)
if scope.defines_any(["__getattr__","__getattribute__"]):
self.generate_getattro_function(scope, code)
......@@ -1199,6 +1202,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# Setting and deleting a slice are both done through
# the ass_slice method, so we dispatch to user's __setslice__
# or __delslice__, or raise an exception.
code.putln("#if PY_MAJOR_VERSION >= 3")
code.putln("#error __setslice__ and __delslice__ not supported in Python 3.")
code.putln("#endif")
base_type = scope.parent_type.base_type
set_entry = scope.lookup_here("__setslice__")
del_entry = scope.lookup_here("__delslice__")
......
......@@ -546,18 +546,24 @@ class CFuncDeclaratorNode(CDeclaratorNode):
else:
if self.exception_value:
self.exception_value.analyse_const_expression(env)
exc_val = self.exception_value.get_constant_c_result_code()
if self.exception_check == '+':
self.exception_value.analyse_types(env)
exc_val_type = self.exception_value.type
if not exc_val_type.is_error and \
not exc_val_type.is_pyobject and \
not (exc_val_type.is_cfunction and not exc_val_type.return_type.is_pyobject and len(exc_val_type.args)==0):
error(self.exception_value.pos,
"Exception value must be a Python exception or cdef function with no arguments.")
exc_val = self.exception_value
else:
if not return_type.assignable_from(self.exception_value.type):
error(self.exception_value.pos,
"Exception value incompatible with function return type")
if self.exception_value.analyse_const_expression(env):
exc_val = self.exception_value.get_constant_c_result_code()
if exc_val is None:
raise InternalError("get_constant_c_result_code not implemented for %s" %
self.exception_value.__class__.__name__)
if not return_type.assignable_from(self.exception_value.type):
error(self.exception_value.pos,
"Exception value incompatible with function return type")
exc_check = self.exception_check
if return_type.is_array:
error(self.pos,
......@@ -1013,12 +1019,12 @@ class FuncDefNode(StatNode, BlockNode):
def create_local_scope(self, env):
genv = env
while env.is_py_class_scope or env.is_c_class_scope:
env = env.outer_scope
while genv.is_py_class_scope or genv.is_c_class_scope:
genv = env.outer_scope
if self.needs_closure:
lenv = ClosureScope(name = self.entry.name, scope_name = self.entry.cname, outer_scope = genv)
else:
lenv = LocalScope(name = self.entry.name, outer_scope = genv)
lenv = LocalScope(name = self.entry.name, outer_scope = genv, parent_scope = env)
lenv.return_type = self.return_type
type = self.entry.type
if type.is_cfunction:
......@@ -2797,7 +2803,7 @@ class CClassDefNode(ClassDefNode):
buffer_defaults = buffer_defaults)
if home_scope is not env and self.visibility == 'extern':
env.add_imported_entry(self.class_name, self.entry, pos)
scope = self.entry.type.scope
self.scope = scope = self.entry.type.scope
if scope is not None:
scope.directives = env.directives
......@@ -3266,7 +3272,7 @@ class InPlaceAssignmentNode(AssignmentNode):
"+": "PyNumber_InPlaceAdd",
"-": "PyNumber_InPlaceSubtract",
"*": "PyNumber_InPlaceMultiply",
"/": "PyNumber_InPlaceDivide",
"/": "__Pyx_PyNumber_InPlaceDivide",
"%": "PyNumber_InPlaceRemainder",
"<<": "PyNumber_InPlaceLshift",
">>": "PyNumber_InPlaceRshift",
......
......@@ -25,6 +25,10 @@ try:
except NameError:
from sets import Set as set
class FakePythonEnv(object):
"A fake environment for creating type test nodes etc."
nogil = False
def unwrap_node(node):
while isinstance(node, UtilNodes.ResultRefNode):
node = node.expression
......@@ -297,11 +301,11 @@ class IterationTransform(Visitor.VisitorTransform):
tuple_target = node.target
def coerce_object_to(obj_node, dest_type):
class FakeEnv(object):
nogil = False
if dest_type.is_pyobject:
if dest_type.is_extension_type or dest_type.is_builtin_type:
obj_node = ExprNodes.PyTypeTestNode(obj_node, dest_type, FakeEnv())
if dest_type != obj_node.type:
if dest_type.is_extension_type or dest_type.is_builtin_type:
obj_node = ExprNodes.PyTypeTestNode(
obj_node, dest_type, FakePythonEnv(), notnone=True)
result = ExprNodes.TypecastNode(
obj_node.pos,
operand = obj_node,
......@@ -316,7 +320,7 @@ class IterationTransform(Visitor.VisitorTransform):
return temp_result.result()
def generate_execution_code(self, code):
self.generate_result_code(code)
return (temp_result, CoercedTempNode(dest_type, obj_node, FakeEnv()))
return (temp_result, CoercedTempNode(dest_type, obj_node, FakePythonEnv()))
if isinstance(node.body, Nodes.StatListNode):
body = node.body
......@@ -528,39 +532,106 @@ class DropRefcountingTransform(Visitor.VisitorTransform):
visit_Node = Visitor.VisitorTransform.recurse_to_children
def visit_ParallelAssignmentNode(self, node):
left, right, temps = [], [], []
left_names, right_names = [], []
left_indices, right_indices = [], []
temps = []
for stat in node.stats:
if isinstance(stat, Nodes.SingleAssignmentNode):
lhs = unwrap_node(stat.lhs)
if not isinstance(lhs, ExprNodes.NameNode):
if not self._extract_operand(stat.lhs, left_names,
left_indices, temps):
return node
left.append(lhs)
rhs = unwrap_node(stat.rhs)
if isinstance(rhs, ExprNodes.CoerceToTempNode):
temps.append(rhs)
rhs = rhs.arg
if not isinstance(rhs, ExprNodes.NameNode):
if not self._extract_operand(stat.rhs, right_names,
right_indices, temps):
return node
right.append(rhs)
elif isinstance(stat, Nodes.CascadedAssignmentNode):
# FIXME
return node
else:
return node
for name_node in left + right:
if name_node.entry.is_builtin or name_node.entry.is_pyglobal:
if left_names or right_names:
# lhs/rhs names must be a non-redundant permutation
lnames = [n.name for n in left_names]
rnames = [n.name for n in right_names]
if set(lnames) != set(rnames):
return node
if len(set(lnames)) != len(right_names):
return node
left_names = [n.name for n in left]
right_names = [n.name for n in right]
if set(left_names) != set(right_names):
return node
if len(set(left_names)) != len(right):
if left_indices or right_indices:
# base name and index of index nodes must be a
# non-redundant permutation
lindices = []
for lhs_node in left_indices:
index_id = self._extract_index_id(lhs_node)
if not index_id:
return node
lindices.append(index_id)
rindices = []
for rhs_node in right_indices:
index_id = self._extract_index_id(rhs_node)
if not index_id:
return node
rindices.append(index_id)
if set(lindices) != set(rindices):
return node
if len(set(lindices)) != len(right_indices):
return node
# really supporting IndexNode requires support in
# __Pyx_GetItemInt(), so let's stop short for now
return node
for name_node in left + right + temps:
name_node.use_managed_ref = False
temp_args = [t.arg for t in temps]
for temp in temps:
temp.use_managed_ref = False
for name_node in left_names + right_names:
if name_node not in temp_args:
name_node.use_managed_ref = False
for index_node in left_indices + right_indices:
index_node.use_managed_ref = False
return node
def _extract_operand(self, node, names, indices, temps):
node = unwrap_node(node)
if not node.type.is_pyobject:
return False
if isinstance(node, ExprNodes.CoerceToTempNode):
temps.append(node)
node = node.arg
if isinstance(node, ExprNodes.NameNode):
if node.entry.is_builtin or node.entry.is_pyglobal:
return False
names.append(node)
elif isinstance(node, ExprNodes.IndexNode):
if node.base.type != Builtin.list_type:
return False
if not node.index.type.is_int:
return False
if not isinstance(node.base, ExprNodes.NameNode):
return False
indices.append(node)
else:
return False
return True
def _extract_index_id(self, index_node):
base = index_node.base
index = index_node.index
if isinstance(index, ExprNodes.NameNode):
index_val = index.name
elif isinstance(index, ExprNodes.ConstNode):
# FIXME:
return None
else:
return None
return (base.name, index_val)
class OptimizeBuiltinCalls(Visitor.VisitorTransform):
"""Optimize some common methods calls and instantiation patterns
......@@ -618,7 +689,7 @@ class OptimizeBuiltinCalls(Visitor.VisitorTransform):
return function_handler(node, arg_tuple, kwargs)
else:
return function_handler(node, arg_tuple)
elif isinstance(function, ExprNodes.AttributeNode):
elif function.is_attribute:
arg_list = arg_tuple.args
self_arg = function.obj
obj_type = self_arg.type
......@@ -648,6 +719,22 @@ class OptimizeBuiltinCalls(Visitor.VisitorTransform):
else:
return node
def _error_wrong_arg_count(self, function_name, node, args, expected=None):
if not expected: # None or 0
arg_str = ''
elif isinstance(expected, basestring) or expected > 1:
arg_str = '...'
elif expected == 1:
arg_str = 'x'
else:
arg_str = ''
if expected is not None:
expected_str = 'expected %s, ' % expected
else:
expected_str = ''
error(node.pos, "%s(%s) called with wrong number of args, %sfound %d" % (
function_name, arg_str, expected_str, len(args)))
### builtin types
def _handle_general_function_dict(self, node, pos_args, kwargs):
......@@ -781,8 +868,7 @@ class OptimizeBuiltinCalls(Visitor.VisitorTransform):
is_temp = node.is_temp
)
else:
error(node.pos, "getattr() called with wrong number of args, "
"expected 2 or 3, found %d" % len(args))
self._error_wrong_arg_count('getattr', node, args, '2 or 3')
return node
Pyx_Type_func_type = PyrexTypes.CFuncType(
......@@ -831,8 +917,7 @@ class OptimizeBuiltinCalls(Visitor.VisitorTransform):
def _handle_simple_method_list_append(self, node, args, is_unbound_method):
if len(args) != 2:
error(node.pos, "list.append(x) called with wrong number of args, found %d" %
len(args))
self._error_wrong_arg_count('list.append', node, args, 2)
return node
return self._substitute_method_call(
node, "PyList_Append", self.PyList_Append_func_type,
......@@ -853,8 +938,7 @@ class OptimizeBuiltinCalls(Visitor.VisitorTransform):
def _handle_simple_method_list_reverse(self, node, args, is_unbound_method):
if len(args) != 1:
error(node.pos, "list.reverse(x) called with wrong number of args, found %d" %
len(args))
self._error_wrong_arg_count('list.reverse', node, args, 1)
return node
return self._substitute_method_call(
node, "PyList_Reverse", self.single_param_func_type,
......@@ -882,8 +966,7 @@ class OptimizeBuiltinCalls(Visitor.VisitorTransform):
def _handle_simple_method_unicode_encode(self, node, args, is_unbound_method):
if len(args) < 1 or len(args) > 3:
error(node.pos, "unicode.encode(...) called with wrong number of args, found %d" %
len(args))
self._error_wrong_arg_count('unicode.encode', node, args, '1-3')
return node
null_node = ExprNodes.NullNode(node.pos)
......@@ -977,7 +1060,6 @@ class OptimizeBuiltinCalls(Visitor.VisitorTransform):
self_arg, "PyExc_AttributeError",
"'NoneType' object has no attribute '%s'" % attr_name)
args[0] = self_arg
# FIXME: args[0] may need a runtime None check (ticket #166)
return ExprNodes.PythonCapiCallNode(
node.pos, name, func_type,
args = args,
......
......@@ -724,6 +724,12 @@ property NAME:
self.visitchildren(node)
return node
def visit_ClassDefNode(self, node):
self.env_stack.append(node.scope)
self.visitchildren(node)
self.env_stack.pop()
return node
def visit_FuncDefNode(self, node):
self.seen_vars_stack.append(set())
lenv = node.create_local_scope(self.env_stack[-1])
......@@ -1002,6 +1008,7 @@ class TransformBuiltinMethods(EnvTransform):
return node
def visit_AttributeNode(self, node):
self.visitchildren(node)
return self.visit_cython_attribute(node)
def visit_NameNode(self, node):
......@@ -1023,8 +1030,15 @@ class TransformBuiltinMethods(EnvTransform):
# locals builtin
if isinstance(node.function, ExprNodes.NameNode):
if node.function.name == 'locals':
pos = node.pos
lenv = self.env_stack[-1]
entry = lenv.lookup_here('locals')
if entry:
# not the builtin 'locals'
return node
if len(node.args) > 0:
error(self.pos, "Builtin 'locals()' called with wrong number of args, expected 0, got %d" % len(node.args))
return node
pos = node.pos
items = [ExprNodes.DictItemNode(pos,
key=ExprNodes.StringNode(pos, value=var),
value=ExprNodes.NameNode(pos, name=var)) for var in lenv.entries]
......
......@@ -967,6 +967,8 @@ def flatten_parallel_assignments(input, output):
output.append(input)
return
complete_assignments = []
rhs_size = len(rhs.args)
lhs_targets = [ [] for _ in range(rhs_size) ]
starred_assignments = []
......@@ -974,7 +976,7 @@ def flatten_parallel_assignments(input, output):
if not lhs.is_sequence_constructor:
if lhs.is_starred:
error(lhs.pos, "starred assignment target must be in a list or tuple")
output.append([lhs,rhs])
complete_assignments.append(lhs)
continue
lhs_size = len(lhs.args)
starred_targets = sum([1 for expr in lhs.args if expr.is_starred])
......@@ -1005,6 +1007,10 @@ def flatten_parallel_assignments(input, output):
for targets, expr in zip(lhs_targets, lhs.args):
targets.append(expr)
if complete_assignments:
complete_assignments.append(rhs)
output.append(complete_assignments)
# recursively flatten partial assignments
for cascade, rhs in zip(lhs_targets, rhs.args):
if cascade:
......
......@@ -408,19 +408,24 @@ class BuiltinObjectType(PyObjectType):
def subtype_of(self, type):
return type.is_pyobject and self.assignable_from(type)
def type_test_code(self, arg):
def type_test_code(self, arg, notnone=False):
type_name = self.name
if type_name == 'str':
check = 'PyString_CheckExact'
type_check = 'PyString_CheckExact'
elif type_name == 'set':
check = 'PyAnySet_CheckExact'
type_check = 'PyAnySet_CheckExact'
elif type_name == 'frozenset':
check = 'PyFrozenSet_CheckExact'
type_check = 'PyFrozenSet_CheckExact'
elif type_name == 'bool':
check = 'PyBool_Check'
type_check = 'PyBool_Check'
else:
check = 'Py%s_CheckExact' % type_name.capitalize()
return 'likely(%s(%s)) || (%s) == Py_None || (PyErr_Format(PyExc_TypeError, "Expected %s, got %%s", Py_TYPE(%s)->tp_name), 0)' % (check, arg, arg, self.name, arg)
type_check = 'Py%s_CheckExact' % type_name.capitalize()
check = 'likely(%s(%s))' % (type_check, arg)
if not notnone:
check = check + ('||((%s) == Py_None)' % arg)
error = '(PyErr_Format(PyExc_TypeError, "Expected %s, got %%.200s", Py_TYPE(%s)->tp_name), 0)' % (self.name, arg)
return check + '||' + error
def declaration_code(self, entity_code,
for_display = 0, dll_linkage = None, pyrex = 0):
......@@ -504,9 +509,16 @@ class PyExtensionType(PyObjectType):
else:
return "%s *%s" % (base, entity_code)
def type_test_code(self, py_arg):
return "__Pyx_TypeTest(%s, %s)" % (py_arg, self.typeptr_cname)
def type_test_code(self, py_arg, notnone=False):
none_check = "((%s) == Py_None)" % py_arg
type_check = "likely(__Pyx_TypeTest(%s, %s))" % (
py_arg, self.typeptr_cname)
if notnone:
return type_check
else:
return "likely(%s || %s)" % (none_check, type_check)
def attributes_known(self):
return self.scope is not None
......@@ -609,12 +621,14 @@ static INLINE %(type)s __Pyx_PyInt_As%(SignWord)s%(TypeName)s(PyObject *);
""",
impl="""
static INLINE %(type)s __Pyx_PyInt_As%(SignWord)s%(TypeName)s(PyObject* x) {
const %(type)s neg_one = (%(type)s)-1, zero = 0;
const int is_unsigned = neg_one > zero;
if (sizeof(%(type)s) < sizeof(long)) {
long val = __Pyx_PyInt_AsLong(x);
if (unlikely(val != (long)(%(type)s)val)) {
if (!unlikely(val == -1 && PyErr_Occurred())) {
PyErr_SetString(PyExc_OverflowError,
(((%(type)s)-1) > ((%(type)s)0) && unlikely(val < 0)) ?
(is_unsigned && unlikely(val < 0)) ?
"can't convert negative value to %(type)s" :
"value too large to convert to %(type)s");
}
......@@ -632,10 +646,12 @@ static INLINE %(type)s __Pyx_PyInt_As%(SignWord)s%(TypeName)s(PyObject *);
""",
impl="""
static INLINE %(type)s __Pyx_PyInt_As%(SignWord)s%(TypeName)s(PyObject* x) {
const %(type)s neg_one = (%(type)s)-1, zero = 0;
const int is_unsigned = neg_one > zero;
#if PY_VERSION_HEX < 0x03000000
if (likely(PyInt_Check(x))) {
long val = PyInt_AS_LONG(x);
if (((%(type)s)-1) > ((%(type)s)0) && unlikely(val < 0)) {
if (is_unsigned && unlikely(val < 0)) {
PyErr_SetString(PyExc_OverflowError,
"can't convert negative value to %(type)s");
return (%(type)s)-1;
......@@ -644,14 +660,16 @@ static INLINE %(type)s __Pyx_PyInt_As%(SignWord)s%(TypeName)s(PyObject* x) {
} else
#endif
if (likely(PyLong_Check(x))) {
if (((%(type)s)-1) > ((%(type)s)0) && unlikely(Py_SIZE(x) < 0)) {
PyErr_SetString(PyExc_OverflowError,
"can't convert negative value to %(type)s");
return (%(type)s)-1;
if (is_unsigned) {
if (unlikely(Py_SIZE(x) < 0)) {
PyErr_SetString(PyExc_OverflowError,
"can't convert negative value to %(type)s");
return (%(type)s)-1;
}
return PyLong_AsUnsigned%(TypeName)s(x);
} else {
return PyLong_As%(TypeName)s(x);
}
return (((%(type)s)-1) < ((%(type)s)0)) ?
PyLong_As%(TypeName)s(x) :
PyLong_AsUnsigned%(TypeName)s(x);
} else {
%(type)s val;
PyObject *tmp = __Pyx_PyNumber_Int(x);
......@@ -669,35 +687,44 @@ static INLINE %(type)s __Pyx_PyInt_from_py_%(TypeName)s(PyObject *);
""",
impl="""
static INLINE %(type)s __Pyx_PyInt_from_py_%(TypeName)s(PyObject* x) {
/**/ if (sizeof(%(type)s) == sizeof(char))
return (((%(type)s)-1) < ((%(type)s)0)) ?
(%(type)s)__Pyx_PyInt_AsSignedChar(x) :
(%(type)s)__Pyx_PyInt_AsUnsignedChar(x);
else if (sizeof(%(type)s) == sizeof(short))
return (((%(type)s)-1) < ((%(type)s)0)) ?
(%(type)s)__Pyx_PyInt_AsSignedShort(x) :
(%(type)s)__Pyx_PyInt_AsUnsignedShort(x);
else if (sizeof(%(type)s) == sizeof(int))
return (((%(type)s)-1) < ((%(type)s)0)) ?
(%(type)s)__Pyx_PyInt_AsSignedInt(x) :
(%(type)s)__Pyx_PyInt_AsUnsignedInt(x);
else if (sizeof(%(type)s) == sizeof(long))
return (((%(type)s)-1) < ((%(type)s)0)) ?
(%(type)s)__Pyx_PyInt_AsSignedLong(x) :
(%(type)s)__Pyx_PyInt_AsUnsignedLong(x);
else if (sizeof(%(type)s) == sizeof(PY_LONG_LONG))
return (((%(type)s)-1) < ((%(type)s)0)) ?
(%(type)s)__Pyx_PyInt_AsSignedLongLong(x) :
(%(type)s)__Pyx_PyInt_AsUnsignedLongLong(x);
const %(type)s neg_one = (%(type)s)-1, zero = 0;
const int is_unsigned = neg_one > zero;
if (sizeof(%(type)s) == sizeof(char)) {
if (is_unsigned)
return (%(type)s)__Pyx_PyInt_AsUnsignedChar(x);
else
return (%(type)s)__Pyx_PyInt_AsSignedChar(x);
} else if (sizeof(%(type)s) == sizeof(short)) {
if (is_unsigned)
return (%(type)s)__Pyx_PyInt_AsUnsignedShort(x);
else
return (%(type)s)__Pyx_PyInt_AsSignedShort(x);
} else if (sizeof(%(type)s) == sizeof(int)) {
if (is_unsigned)
return (%(type)s)__Pyx_PyInt_AsUnsignedInt(x);
else
return (%(type)s)__Pyx_PyInt_AsSignedInt(x);
} else if (sizeof(%(type)s) == sizeof(long)) {
if (is_unsigned)
return (%(type)s)__Pyx_PyInt_AsUnsignedLong(x);
else
return (%(type)s)__Pyx_PyInt_AsSignedLong(x);
} else if (sizeof(%(type)s) == sizeof(PY_LONG_LONG)) {
if (is_unsigned)
return (%(type)s)__Pyx_PyInt_AsUnsignedLongLong(x);
else
return (%(type)s)__Pyx_PyInt_AsSignedLongLong(x);
#if 0
else if (sizeof(%(type)s) > sizeof(short) &&
sizeof(%(type)s) < sizeof(int)) /* __int32 ILP64 ? */
return (((%(type)s)-1) < ((%(type)s)0)) ?
(%(type)s)__Pyx_PyInt_AsSignedInt(x) :
(%(type)s)__Pyx_PyInt_AsUnsignedInt(x);
} else if (sizeof(%(type)s) > sizeof(short) &&
sizeof(%(type)s) < sizeof(int)) { /* __int32 ILP64 ? */
if (is_unsigned)
return (%(type)s)__Pyx_PyInt_AsUnsignedInt(x);
else
return (%(type)s)__Pyx_PyInt_AsSignedInt(x);
#endif
PyErr_SetString(PyExc_TypeError, "%(TypeName)s");
return (%(type)s)-1;
}
PyErr_SetString(PyExc_TypeError, "%(TypeName)s");
return (%(type)s)-1;
}
""")
......@@ -707,16 +734,21 @@ static INLINE PyObject *__Pyx_PyInt_to_py_%(TypeName)s(%(type)s);
""",
impl="""
static INLINE PyObject *__Pyx_PyInt_to_py_%(TypeName)s(%(type)s val) {
/**/ if (sizeof(%(type)s) < sizeof(long))
return PyInt_FromLong((long)val);
else if (sizeof(%(type)s) == sizeof(long))
return (((%(type)s)-1) < ((%(type)s)0)) ?
PyInt_FromLong((long)val) :
PyLong_FromUnsignedLong((unsigned long)val);
else /* (sizeof(%(type)s) > sizeof(long)) */
return (((%(type)s)-1) < ((%(type)s)0)) ?
PyLong_FromLongLong((PY_LONG_LONG)val) :
PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)val);
const %(type)s neg_one = (%(type)s)-1, zero = 0;
const int is_unsigned = neg_one > zero;
if (sizeof(%(type)s) < sizeof(long)) {
return PyInt_FromLong((long)val);
} else if (sizeof(%(type)s) == sizeof(long)) {
if (is_unsigned)
return PyLong_FromUnsignedLong((unsigned long)val);
else
return PyInt_FromLong((long)val);
} else { /* (sizeof(%(type)s) > sizeof(long)) */
if (is_unsigned)
return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)val);
else
return PyLong_FromLongLong((PY_LONG_LONG)val);
}
}
""")
......@@ -1282,10 +1314,17 @@ class CFuncType(CType):
arg_reprs = map(repr, self.args)
if self.has_varargs:
arg_reprs.append("...")
return "<CFuncType %s %s[%s]>" % (
if self.exception_value:
except_clause = " %r" % self.exception_value
else:
except_clause = ""
if self.exception_check:
except_clause += "?"
return "<CFuncType %s %s[%s]%s>" % (
repr(self.return_type),
self.calling_convention_prefix(),
",".join(arg_reprs))
",".join(arg_reprs),
except_clause)
def calling_convention_prefix(self):
cc = self.calling_convention
......
......@@ -1079,8 +1079,10 @@ class ModuleScope(Scope):
class LocalScope(Scope):
def __init__(self, name, outer_scope):
Scope.__init__(self, name, outer_scope, outer_scope)
def __init__(self, name, outer_scope, parent_scope = None):
if parent_scope is None:
parent_scope = outer_scope
Scope.__init__(self, name, outer_scope, parent_scope)
def mangle(self, prefix, name):
return prefix + name
......
......@@ -52,6 +52,33 @@ cdef extern from "numpy/arrayobject.h":
NPY_NTYPES
NPY_NOTYPE
NPY_INT8
NPY_INT16
NPY_INT32
NPY_INT64
NPY_INT128
NPY_INT256
NPY_UINT8
NPY_UINT16
NPY_UINT32
NPY_UINT64
NPY_UINT128
NPY_UINT256
NPY_FLOAT16
NPY_FLOAT32
NPY_FLOAT64
NPY_FLOAT80
NPY_FLOAT96
NPY_FLOAT128
NPY_FLOAT256
NPY_COMPLEX32
NPY_COMPLEX64
NPY_COMPLEX128
NPY_COMPLEX160
NPY_COMPLEX192
NPY_COMPLEX256
NPY_COMPLEX512
enum NPY_ORDER:
NPY_ANYORDER
NPY_CORDER
......@@ -335,7 +362,7 @@ cdef extern from "numpy/arrayobject.h":
# Macros from ndarrayobject.h
#
bint PyArray_CHKFLAGS(ndarray m, int flags)
bint PyArray_ISISCONTIGUOUS(ndarray m)
bint PyArray_ISCONTIGUOUS(ndarray m)
bint PyArray_ISWRITEABLE(ndarray m)
bint PyArray_ISALIGNED(ndarray m)
......
......@@ -125,7 +125,6 @@ from python_mem cimport *
from python_tuple cimport *
from python_list cimport *
from python_object cimport *
from python_cobject cimport *
from python_sequence cimport *
from python_mapping cimport *
from python_iterator cimport *
......@@ -142,4 +141,18 @@ from python_dict cimport *
from python_instance cimport *
from python_function cimport *
from python_method cimport *
from python_weakref cimport *
from python_getargs cimport *
# Python <= 2.x
from python_cobject cimport *
# Python >= 2.4
from python_set cimport *
# Python >= 2.6
from python_buffer cimport *
from python_bytes cimport *
# Python >= 3.0
from python_pycapsule cimport *
......@@ -79,7 +79,7 @@ cdef extern from "Python.h":
# fastest). If fortran is 'A', then it does not matter and the
# copy will be made in whatever way is more efficient.
int PyObject_CopyData(object dest, object src)
int PyObject_CopyData(object dest, object src) except -1
# Copy the data from the src buffer to the buffer of destination
bint PyBuffer_IsContiguous(Py_buffer *view, char fort)
......
from python_ref cimport PyObject
cdef extern from "Python.h":
ctypedef struct va_list
############################################################################
# 7.3.1 String Objects
############################################################################
# These functions raise TypeError when expecting a string
# parameter and are called with a non-string parameter.
# PyStringObject
# This subtype of PyObject represents a Python bytes object.
# PyTypeObject PyBytes_Type
# This instance of PyTypeObject represents the Python bytes type;
# it is the same object as bytes and types.BytesType in the Python
# layer.
bint PyBytes_Check(object o)
# Return true if the object o is a string object or an instance of
# a subtype of the string type.
bint PyBytes_CheckExact(object o)
# Return true if the object o is a string object, but not an instance of a subtype of the string type.
object PyBytes_FromString(char *v)
# Return value: New reference.
# Return a new string object with the value v on success, and NULL
# on failure. The parameter v must not be NULL; it will not be
# checked.
object PyBytes_FromStringAndSize(char *v, Py_ssize_t len)
# Return value: New reference.
# Return a new string object with the value v and length len on
# success, and NULL on failure. If v is NULL, the contents of the
# string are uninitialized.
object PyBytes_FromFormat(char *format, ...)
# Return value: New reference.
# Take a C printf()-style format string and a variable number of
# arguments, calculate the size of the resulting Python string and
# return a string with the values formatted into it. The variable
# arguments must be C types and must correspond exactly to the
# format characters in the format string. The following format
# characters are allowed:
# Format Characters Type Comment
# %% n/a The literal % character.
# %c int A single character, represented as an C int.
# %d int Exactly equivalent to printf("%d").
# %u unsigned int Exactly equivalent to printf("%u").
# %ld long Exactly equivalent to printf("%ld").
# %lu unsigned long Exactly equivalent to printf("%lu").
# %zd Py_ssize_t Exactly equivalent to printf("%zd").
# %zu size_t Exactly equivalent to printf("%zu").
# %i int Exactly equivalent to printf("%i").
# %x int Exactly equivalent to printf("%x").
# %s char* A null-terminated C character array.
# %p void* The hex representation of a C pointer.
# Mostly equivalent to printf("%p") except that it is guaranteed to
# start with the literal 0x regardless of what the platform's printf
# yields.
# An unrecognized format character causes all the rest of the
# format string to be copied as-is to the result string, and any
# extra arguments discarded.
object PyBytes_FromFormatV(char *format, va_list vargs)
# Return value: New reference.
# Identical to PyBytes_FromFormat() except that it takes exactly two arguments.
Py_ssize_t PyBytes_Size(object string) except -1
# Return the length of the string in string object string.
Py_ssize_t PyBytes_GET_SIZE(object string)
# Macro form of PyBytes_Size() but without error checking.
char* PyBytes_AsString(object string) except NULL
# Return a NUL-terminated representation of the contents of
# string. The pointer refers to the internal buffer of string, not
# a copy. The data must not be modified in any way, unless the
# string was just created using PyBytes_FromStringAndSize(NULL,
# size). It must not be deallocated. If string is a Unicode
# object, this function computes the default encoding of string
# and operates on that. If string is not a string object at all,
# PyBytes_AsString() returns NULL and raises TypeError.
char* PyBytes_AS_STRING(object string)
# Macro form of PyBytes_AsString() but without error
# checking. Only string objects are supported; no Unicode objects
# should be passed.
int PyBytes_AsStringAndSize(object obj, char **buffer, Py_ssize_t *length) except -1
# Return a NULL-terminated representation of the contents of the
# object obj through the output variables buffer and length.
#
# The function accepts both string and Unicode objects as
# input. For Unicode objects it returns the default encoded
# version of the object. If length is NULL, the resulting buffer
# may not contain NUL characters; if it does, the function returns
# -1 and a TypeError is raised.
# The buffer refers to an internal string buffer of obj, not a
# copy. The data must not be modified in any way, unless the
# string was just created using PyBytes_FromStringAndSize(NULL,
# size). It must not be deallocated. If string is a Unicode
# object, this function computes the default encoding of string
# and operates on that. If string is not a string object at all,
# PyBytes_AsStringAndSize() returns -1 and raises TypeError.
void PyBytes_Concat(PyObject **string, object newpart)
# Create a new string object in *string containing the contents of
# newpart appended to string; the caller will own the new
# reference. The reference to the old value of string will be
# stolen. If the new string cannot be created, the old reference
# to string will still be discarded and the value of *string will
# be set to NULL; the appropriate exception will be set.
void PyBytes_ConcatAndDel(PyObject **string, object newpart)
# Create a new string object in *string containing the contents of
# newpart appended to string. This version decrements the
# reference count of newpart.
int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1
# A way to resize a string object even though it is
# ``immutable''. Only use this to build up a brand new string
# object; don't use this if the string may already be known in
# other parts of the code. It is an error to call this function if
# the refcount on the input string object is not one. Pass the
# address of an existing string object as an lvalue (it may be
# written into), and the new size desired. On success, *string
# holds the resized string object and 0 is returned; the address
# in *string may differ from its input value. If the reallocation
# fails, the original string object at *string is deallocated,
# *string is set to NULL, a memory exception is set, and -1 is
# returned.
object PyBytes_Format(object format, object args)
# Return value: New reference. Return a new string object from
# format and args. Analogous to format % args. The args argument
# must be a tuple.
void PyBytes_InternInPlace(PyObject **string)
# Intern the argument *string in place. The argument must be the
# address of a pointer variable pointing to a Python string
# object. If there is an existing interned string that is the same
# as *string, it sets *string to it (decrementing the reference
# count of the old string object and incrementing the reference
# count of the interned string object), otherwise it leaves
# *string alone and interns it (incrementing its reference
# count). (Clarification: even though there is a lot of talk about
# reference counts, think of this function as
# reference-count-neutral; you own the object after the call if
# and only if you owned it before the call.)
object PyBytes_InternFromString(char *v)
# Return value: New reference.
# A combination of PyBytes_FromString() and
# PyBytes_InternInPlace(), returning either a new string object
# that has been interned, or a new (``owned'') reference to an
# earlier interned string object with the same value.
object PyBytes_Decode(char *s, Py_ssize_t size, char *encoding, char *errors)
# Return value: New reference.
# Create an object by decoding size bytes of the encoded buffer s
# using the codec registered for encoding. encoding and errors
# have the same meaning as the parameters of the same name in the
# unicode() built-in function. The codec to be used is looked up
# using the Python codec registry. Return NULL if an exception was
# raised by the codec.
object PyBytes_AsDecodedObject(object str, char *encoding, char *errors)
# Return value: New reference.
# Decode a string object by passing it to the codec registered for
# encoding and return the result as Python object. encoding and
# errors have the same meaning as the parameters of the same name
# in the string encode() method. The codec to be used is looked up
# using the Python codec registry. Return NULL if an exception was
# raised by the codec.
object PyBytes_Encode(char *s, Py_ssize_t size, char *encoding, char *errors)
# Return value: New reference.
# Encode the char buffer of the given size by passing it to the
# codec registered for encoding and return a Python
# object. encoding and errors have the same meaning as the
# parameters of the same name in the string encode() method. The
# codec to be used is looked up using the Python codec
# registry. Return NULL if an exception was raised by the codec.
object PyBytes_AsEncodedObject(object str, char *encoding, char *errors)
# Return value: New reference.
# Encode a string object using the codec registered for encoding
# and return the result as Python object. encoding and errors have
# the same meaning as the parameters of the same name in the
# string encode() method. The codec to be used is looked up using
# the Python codec registry. Return NULL if an exception was
# raised by the codec.
......@@ -25,13 +25,13 @@ cdef extern from "Python.h":
# be called when the object is reclaimed. The desc argument can be
# used to pass extra callback data for the destructor function.
void* PyCObject_AsVoidPtr(object self)
void* PyCObject_AsVoidPtr(object self) except? NULL
# Return the object void * that the PyCObject self was created with.
void* PyCObject_GetDesc(object self)
void* PyCObject_GetDesc(object self) except? NULL
# Return the description void * that the PyCObject self was created with.
int PyCObject_SetVoidPtr(object self, void* cobj)
int PyCObject_SetVoidPtr(object self, void* cobj) except 0
# Set the void pointer inside self to cobj. The PyCObject must not
# have an associated destructor. Return true on success, false on
# failure.
......@@ -29,11 +29,13 @@ cdef extern from "Python.h":
# Return value: New reference.
# Return a new PyComplexObject object from real and imag.
double PyComplex_RealAsDouble(object op)
double PyComplex_RealAsDouble(object op) except? -1
# Return the real part of op as a C double.
double PyComplex_ImagAsDouble(object op)
double PyComplex_ImagAsDouble(object op) except? -1
# Return the imaginary part of op as a C double.
Py_complex PyComplex_AsCComplex(object op)
# Return the Py_complex value of the complex number op.
# Return the Py_complex value of the complex number op.
#
# Returns (-1+0i) in case of an error
......@@ -95,7 +95,7 @@ cdef extern from "Python.h":
# dictionary p, as in the dictionary method values() (see the
# Python Library Reference).
Py_ssize_t PyDict_Size(object p)
Py_ssize_t PyDict_Size(object p) except -1
# Return the number of items in the dictionary. This is equivalent
# to "len(p)" on a dictionary.
......
......@@ -53,7 +53,7 @@ cdef extern from "Python.h":
# of a class, in the case of a class exception, or it may the a
# subclass of the expected exception.)
int PyErr_ExceptionMatches(object exc)
bint PyErr_ExceptionMatches(object exc)
# Equivalent to "PyErr_GivenExceptionMatches(PyErr_Occurred(),
# exc)". This should only be called when an exception is actually
# set; a memory access violation will occur if no exception has
......@@ -126,7 +126,7 @@ cdef extern from "Python.h":
void PyErr_SetNone(object type)
# This is a shorthand for "PyErr_SetObject(type, Py_None)".
int PyErr_BadArgument()
int PyErr_BadArgument() except 0
# This is a shorthand for "PyErr_SetString(PyExc_TypeError,
# message)", where message indicates that a built-in operation was
......@@ -196,7 +196,7 @@ cdef extern from "Python.h":
# (e.g. a Python/C API function) was invoked with an illegal
# argument. It is mostly for internal use.
int PyErr_WarnEx(object category, char *message, int stacklevel)
int PyErr_WarnEx(object category, char *message, int stacklevel) except -1
# Issue a warning message. The category argument is a warning
# category (see below) or NULL; the message argument is a message
# string. stacklevel is a positive number giving a number of stack
......@@ -205,14 +205,14 @@ cdef extern from "Python.h":
# function calling PyErr_WarnEx(), 2 is the function above that,
# and so forth.
int PyErr_WarnExplicit(object category, char *message, char *filename, int lineno, char *module, object registry)
int PyErr_WarnExplicit(object category, char *message, char *filename, int lineno, char *module, object registry) except -1
# Issue a warning message with explicit control over all warning
# attributes. This is a straightforward wrapper around the Python
# function warnings.warn_explicit(), see there for more
# information. The module and registry arguments may be set to
# NULL to get the default effect described there.
int PyErr_CheckSignals()
int PyErr_CheckSignals() except -1
# This function interacts with Python's signal handling. It checks
# whether a signal has been sent to the processes and if so,
# invokes the corresponding signal handler. If the signal module
......
......@@ -31,9 +31,9 @@ cdef extern from "Python.h":
# Return value: New reference.
# Create a PyFloatObject object from v, or NULL on failure.
double PyFloat_AsDouble(object pyfloat)
double PyFloat_AsDouble(object pyfloat) except? -1
# Return a C double representation of the contents of pyfloat.
double PyFloat_AS_DOUBLE(object pyfloat)
double PyFloat_AS_DOUBLE(object pyfloat) except? -1
# Return a C double representation of the contents of pyfloat, but
# without error checking.
......@@ -30,21 +30,21 @@ cdef extern from "Python.h":
# the code object, the argument defaults and closure are set to
# NULL.
PyObject* PyFunction_GetCode(object op)
PyObject* PyFunction_GetCode(object op) except? NULL
# Return value: Borrowed reference.
# Return the code object associated with the function object op.
PyObject* PyFunction_GetGlobals(object op)
PyObject* PyFunction_GetGlobals(object op) except? NULL
# Return value: Borrowed reference.
# Return the globals dictionary associated with the function object op.
PyObject* PyFunction_GetModule(object op)
PyObject* PyFunction_GetModule(object op) except? NULL
# Return value: Borrowed reference.
# Return the __module__ attribute of the function object op. This
# is normally a string containing the module name, but can be set
# to any other object by Python code.
PyObject* PyFunction_GetDefaults(object op)
PyObject* PyFunction_GetDefaults(object op) except? NULL
# Return value: Borrowed reference.
# Return the argument default values of the function object
# op. This can be a tuple of arguments or NULL.
......@@ -54,7 +54,7 @@ cdef extern from "Python.h":
# op. defaults must be Py_None or a tuple.
# Raises SystemError and returns -1 on failure.
PyObject* PyFunction_GetClosure(object op)
PyObject* PyFunction_GetClosure(object op) except? NULL
# Return value: Borrowed reference.
# Return the closure associated with the function object op. This
# can be NULL or a tuple of cell objects.
......
from python_ref cimport PyObject
cdef extern from "Python.h":
ctypedef void PyObject
#####################################################################
# 5.5 Parsing arguments and building values
#####################################################################
ctypedef struct va_list
int PyArg_ParseTuple(PyObject *args, char *format, ...)
int PyArg_VaParse(PyObject *args, char *format, va_list vargs)
int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kw, char *format, char *keywords[], ...)
int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *kw, char *format, char *keywords[], va_list vargs)
int PyArg_Parse(PyObject *args, char *format, ...)
int PyArg_UnpackTuple(PyObject *args, char *name, Py_ssize_t min, Py_ssize_t max, ...)
int PyArg_ParseTuple(object args, char *format, ...) except 0
int PyArg_VaParse(object args, char *format, va_list vargs) except 0
int PyArg_ParseTupleAndKeywords(object args, object kw, char *format, char *keywords[], ...) except 0
int PyArg_VaParseTupleAndKeywords(object args, object kw, char *format, char *keywords[], va_list vargs) except 0
int PyArg_Parse(object args, char *format, ...) except 0
int PyArg_UnpackTuple(object args, char *name, Py_ssize_t min, Py_ssize_t max, ...) except 0
......@@ -58,18 +58,18 @@ cdef extern from "Python.h":
long PyInt_AS_LONG(object io)
# Return the value of the object io. No error checking is performed.
unsigned long PyInt_AsUnsignedLongMask(object io)
unsigned long PyInt_AsUnsignedLongMask(object io) except? -1
# Will first attempt to cast the object to a PyIntObject or
# PyLongObject, if it is not already one, and then return its
# value as unsigned long. This function does not check for
# overflow.
PY_LONG_LONG PyInt_AsUnsignedLongLongMask(object io)
PY_LONG_LONG PyInt_AsUnsignedLongLongMask(object io) except? -1
# Will first attempt to cast the object to a PyIntObject or
# PyLongObject, if it is not already one, and then return its
# value as unsigned long long, without checking for overflow.
Py_ssize_t PyInt_AsSsize_t(object io)
Py_ssize_t PyInt_AsSsize_t(object io) except? -1
# Will first attempt to cast the object to a PyIntObject or
# PyLongObject, if it is not already one, and then return its
# value as Py_ssize_t.
......
......@@ -22,14 +22,14 @@ cdef extern from "Python.h":
# Return true if p is a list object, but not an instance of a
# subtype of the list type.
Py_ssize_t PyList_Size(object list)
Py_ssize_t PyList_Size(object list) except -1
# Return the length of the list object in list; this is equivalent
# to "len(list)" on a list object.
Py_ssize_t PyList_GET_SIZE(object list)
# Macro form of PyList_Size() without error checking.
PyObject* PyList_GetItem(object list, Py_ssize_t index)
PyObject* PyList_GetItem(object list, Py_ssize_t index) except NULL
# Return value: Borrowed reference.
# Return the object at position pos in the list pointed to by
# p. The position must be positive, indexing from the end of the
......
......@@ -72,32 +72,32 @@ cdef extern from "Python.h":
# PyLong_AsVoidPtr(). If the integer is larger than LONG_MAX, a
# positive long integer is returned.
long PyLong_AsLong(object pylong)
long PyLong_AsLong(object pylong) except? -1
# Return a C long representation of the contents of pylong. If
# pylong is greater than LONG_MAX, an OverflowError is raised.
unsigned long PyLong_AsUnsignedLong(object pylong)
unsigned long PyLong_AsUnsignedLong(object pylong) except? -1
# Return a C unsigned long representation of the contents of
# pylong. If pylong is greater than ULONG_MAX, an OverflowError is
# raised.
PY_LONG_LONG PyLong_AsLongLong(object pylong)
PY_LONG_LONG PyLong_AsLongLong(object pylong) except? -1
# Return a C long long from a Python long integer. If pylong
# cannot be represented as a long long, an OverflowError will be
# raised.
uPY_LONG_LONG PyLong_AsUnsignedLongLong(object pylong)
uPY_LONG_LONG PyLong_AsUnsignedLongLong(object pylong) except? -1
#unsigned PY_LONG_LONG PyLong_AsUnsignedLongLong(object pylong)
# Return a C unsigned long long from a Python long integer. If
# pylong cannot be represented as an unsigned long long, an
# OverflowError will be raised if the value is positive, or a
# TypeError will be raised if the value is negative.
unsigned long PyLong_AsUnsignedLongMask(object io)
unsigned long PyLong_AsUnsignedLongMask(object io) except? -1
# Return a C unsigned long from a Python long integer, without
# checking for overflow.
uPY_LONG_LONG PyLong_AsUnsignedLongLongMask(object io)
uPY_LONG_LONG PyLong_AsUnsignedLongLongMask(object io) except? -1
#unsigned PY_LONG_LONG PyLong_AsUnsignedLongLongMask(object io)
# Return a C unsigned long long from a Python long integer,
# without checking for overflow.
......@@ -107,7 +107,7 @@ cdef extern from "Python.h":
# pylong cannot be approximately represented as a double, an
# OverflowError exception is raised and -1.0 will be returned.
void* PyLong_AsVoidPtr(object pylong)
void* PyLong_AsVoidPtr(object pylong) except? NULL
# Convert a Python integer or long integer pylong to a C void
# pointer. If pylong cannot be converted, an OverflowError will be
# raised. This is only assured to produce a usable void pointer
......
......@@ -21,7 +21,7 @@ cdef extern from "Python.h":
# otherwise self should be NULL and class should be the class
# which provides the unbound method..
PyObject* PyMethod_Class(object meth)
PyObject* PyMethod_Class(object meth) except NULL
# Return value: Borrowed reference.
# Return the class object from which the method meth was created;
# if this was created from an instance, it will be the class of
......@@ -31,7 +31,7 @@ cdef extern from "Python.h":
# Return value: Borrowed reference.
# Macro version of PyMethod_Class() which avoids error checking.
PyObject* PyMethod_Function(object meth)
PyObject* PyMethod_Function(object meth) except NULL
# Return value: Borrowed reference.
# Return the function object associated with the method meth.
......@@ -39,7 +39,7 @@ cdef extern from "Python.h":
# Return value: Borrowed reference.
# Macro version of PyMethod_Function() which avoids error checking.
PyObject* PyMethod_Self(object meth)
PyObject* PyMethod_Self(object meth) except? NULL
# Return value: Borrowed reference.
# Return the instance associated with the method meth if it is bound, otherwise return NULL.
......
......@@ -90,7 +90,7 @@ cdef extern from "Python.h":
# and .pyo files). The magic number should be present in the first
# four bytes of the bytecode file, in little-endian byte order.
PyObject* PyImport_GetModuleDict()
PyObject* PyImport_GetModuleDict() except NULL
# Return value: Borrowed reference.
# Return the dictionary used for the module administration
# (a.k.a. sys.modules). Note that this is a per-interpreter
......@@ -139,7 +139,7 @@ cdef extern from "Python.h":
# filled in; the caller is responsible for providing a __file__
# attribute.
PyObject* PyModule_GetDict(object module)
PyObject* PyModule_GetDict(object module) except NULL
# Return value: Borrowed reference.
# Return the dictionary object that implements module's namespace;
# this object is the same as the __dict__ attribute of the module
......
......@@ -236,7 +236,7 @@ cdef extern from "Python.h":
# Returns the o converted to a Python int or long on success or
# NULL with a TypeError exception raised on failure.
Py_ssize_t PyNumber_AsSsize_t(object o, object exc)
Py_ssize_t PyNumber_AsSsize_t(object o, object exc) except? -1
# Returns o converted to a Py_ssize_t value if o can be
# interpreted as an integer. If o can be converted to a Python int
# or long but the attempt to convert to a Py_ssize_t value would
......
......@@ -49,7 +49,7 @@ cdef extern from "Python.h":
# PyCapsule_Import().
void* PyCapsule_GetPointer(object capsule, char *name)
void* PyCapsule_GetPointer(object capsule, char *name) except? NULL
# Retrieve the pointer stored in the capsule. On failure, set an
# exception and return NULL.
#
......@@ -59,7 +59,7 @@ cdef extern from "Python.h":
# to compare capsule names.
PyCapsule_Destructor PyCapsule_GetDestructor(object capsule)
PyCapsule_Destructor PyCapsule_GetDestructor(object capsule) except? NULL
# Return the current destructor stored in the capsule. On failure,
# set an exception and return NULL.
#
......@@ -68,7 +68,7 @@ cdef extern from "Python.h":
# or PyErr_Occurred() to disambiguate.
char* PyCapsule_GetName(object capsule)
char* PyCapsule_GetName(object capsule) except? NULL
# Return the current name stored in the capsule. On failure, set
# an exception and return NULL.
#
......@@ -77,7 +77,7 @@ cdef extern from "Python.h":
# PyErr_Occurred() to disambiguate.
void* PyCapsule_GetContext(object capsule)
void* PyCapsule_GetContext(object capsule) except? NULL
# Return the current context stored in the capsule. On failure,
# set an exception and return NULL.
#
......@@ -86,7 +86,7 @@ cdef extern from "Python.h":
# PyErr_Occurred() to disambiguate.
int PyCapsule_IsValid(object capsule, char *name)
bint PyCapsule_IsValid(object capsule, char *name)
# Determines whether or not capsule is a valid capsule. A valid
# capsule is non-NULL, passes PyCapsule_CheckExact(), has a
# non-NULL pointer stored in it, and its internal name matches the
......@@ -101,7 +101,7 @@ cdef extern from "Python.h":
# name passed in. Return 0 otherwise. This function will not fail.
int PyCapsule_SetPointer(object capsule, void *pointer)
int PyCapsule_SetPointer(object capsule, void *pointer) except -1
# Set the void pointer inside capsule to pointer. The pointer may
# not be NULL.
#
......@@ -109,14 +109,14 @@ cdef extern from "Python.h":
# failure.
int PyCapsule_SetDestructor(object capsule, PyCapsule_Destructor destructor)
int PyCapsule_SetDestructor(object capsule, PyCapsule_Destructor destructor) except -1
# Set the destructor inside capsule to destructor.
#
# Return 0 on success. Return nonzero and set an exception on
# failure.
int PyCapsule_SetName(object capsule, char *name)
int PyCapsule_SetName(object capsule, char *name) except -1
# Set the name inside capsule to name. If non-NULL, the name must
# outlive the capsule. If the previous name stored in the capsule
# was not NULL, no attempt is made to free it.
......@@ -125,12 +125,12 @@ cdef extern from "Python.h":
# failure.
int PyCapsule_SetContext(object capsule, void *context)
int PyCapsule_SetContext(object capsule, void *context) except -1
# Set the context pointer inside capsule to context. Return 0 on
# success. Return nonzero and set an exception on failure.
void* PyCapsule_Import(char *name, int no_block)
void* PyCapsule_Import(char *name, int no_block) except? NULL
# Import a pointer to a C object from a capsule attribute in a
# module. The name parameter should specify the full name to the
# attribute, as in module.attribute. The name stored in the
......
......@@ -47,7 +47,7 @@ cdef extern from "Python.h":
bint PyFrozenSet_CheckExact(object p)
# Return true if p is a frozenset object but not an instance of a subtype.
PySet_New(object iterable)
object PySet_New(object iterable)
# Return value: New reference.
# Return a new set containing objects returned by the
# iterable. The iterable may be NULL to create a new empty
......@@ -55,7 +55,7 @@ cdef extern from "Python.h":
# TypeError if iterable is not actually iterable. The constructor
# is also useful for copying a set (c=set(s)).
PyFrozenSet_New(object iterable)
object PyFrozenSet_New(object iterable)
# Return value: New reference.
# Return a new frozenset containing objects returned by the
# iterable. The iterable may be NULL to create a new empty
......
......@@ -68,7 +68,7 @@ cdef extern from "Python.h":
# Return value: New reference.
# Identical to PyString_FromFormat() except that it takes exactly two arguments.
Py_ssize_t PyString_Size(object string)
Py_ssize_t PyString_Size(object string) except -1
# Return the length of the string in string object string.
Py_ssize_t PyString_GET_SIZE(object string)
......
......@@ -24,7 +24,7 @@ cdef extern from "Python.h":
# pointing to Python objects. "PyTuple_Pack(2, a, b)" is
# equivalent to "Py_BuildValue("(OO)", a, b)".
int PyTuple_Size(object p)
int PyTuple_Size(object p) except -1
# Take a pointer to a tuple object, and return the size of that tuple.
int PyTuple_GET_SIZE(object p)
......
......@@ -93,10 +93,10 @@ cdef extern from *:
# Return a read-only pointer to the Unicode object's internal
# Py_UNICODE buffer, NULL if unicode is not a Unicode object.
Py_UNICODE* PyUnicode_AsUnicode(object o)
Py_UNICODE* PyUnicode_AsUnicode(object o) except NULL
# Return the length of the Unicode object.
Py_ssize_t PyUnicode_GetSize(object o)
Py_ssize_t PyUnicode_GetSize(object o) except -1
# Coerce an encoded object obj to an Unicode object and return a
# reference with incremented refcount.
......
from python_ref cimport PyObject
cdef extern from "Python.h":
bint PyWeakref_Check(object ob)
# Return true if ob is either a reference or proxy object.
bint PyWeakref_CheckRef(object ob)
# Return true if ob is a reference object.
bint PyWeakref_CheckProxy(ob)
# Return true if *ob* is a proxy object.
object PyWeakref_NewRef(object ob, object callback)
# Return a weak reference object for the object ob. This will
# always return a new reference, but is not guaranteed to create a
# new object; an existing reference object may be returned. The
# second parameter, callback, can be a callable object that
# receives notification when ob is garbage collected; it should
# accept a single parameter, which will be the weak reference
# object itself. callback may also be None or NULL. If ob is not
# a weakly-referencable object, or if callback is not callable,
# None, or NULL, this will return NULL and raise TypeError.
object PyWeakref_NewProxy(object ob, object callback)
# Return a weak reference proxy object for the object ob. This
# will always return a new reference, but is not guaranteed to
# create a new object; an existing proxy object may be returned.
# The second parameter, callback, can be a callable object that
# receives notification when ob is garbage collected; it should
# accept a single parameter, which will be the weak reference
# object itself. callback may also be None or NULL. If ob is not
# a weakly-referencable object, or if callback is not callable,
# None, or NULL, this will return NULL and raise TypeError.
PyObject* PyWeakref_GetObject(object ref)
# Return the referenced object from a weak reference, ref. If the
# referent is no longer live, returns None.
PyObject* PyWeakref_GET_OBJECT(object ref)
# Similar to PyWeakref_GetObject, but implemented as a macro that
# does no error checking.
"""
Tool to run Cython files (.pyx) into .c and .cpp.
TODO:
- Add support for dynamically selecting in-process Cython
through CYTHONINPROCESS variable.
- Have a CYTHONCPP option which turns on C++ in flags and
changes output extension at the same time
VARIABLES:
- CYTHON - The path to the "cython" command line tool.
- CYTHONFLAGS - Flags to pass to the "cython" command line tool.
AUTHORS:
- David Cournapeau
- Dag Sverre Seljebotn
"""
import SCons
from SCons.Builder import Builder
from SCons.Action import Action
#def cython_action(target, source, env):
# print target, source, env
# from Cython.Compiler.Main import compile as cython_compile
# res = cython_compile(str(source[0]))
cythonAction = Action("$CYTHONCOM")
def create_builder(env):
try:
cython = env['BUILDERS']['Cython']
except KeyError:
cython = SCons.Builder.Builder(
action = cythonAction,
emitter = {},
suffix = cython_suffix_emitter,
single_source = 1)
env['BUILDERS']['Cython'] = cython
return cython
def cython_suffix_emitter(env, source):
print 'emitter called'
return "$CYTHONCFILESUFFIX"
def generate(env):
env["CYTHON"] = "cython"
env["CYTHONCOM"] = "$CYTHON $CYTHONFLAGS -o $TARGET $SOURCE"
env["CYTHONCFILESUFFIX"] = ".c"
c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
c_file.suffix['.pyx'] = cython_suffix_emitter
c_file.add_action('.pyx', cythonAction)
c_file.suffix['.py'] = cython_suffix_emitter
c_file.add_action('.py', cythonAction)
create_builder(env)
def exists(env):
try:
# import Cython
return True
except ImportError:
return False
"""SCons.Tool.pyext
Tool-specific initialization for python extensions builder.
AUTHORS:
- David Cournapeau
- Dag Sverre Seljebotn
"""
#
# __COPYRIGHT__
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
import SCons
from SCons.Tool import SourceFileScanner, ProgramScanner
# Create common python builders
def createPythonObjectBuilder(env):
"""This is a utility function that creates the PythonObject Builder in an
Environment if it is not there already.
If it is already there, we return the existing one.
"""
try:
pyobj = env['BUILDERS']['PythonObject']
except KeyError:
pyobj = SCons.Builder.Builder(action = {},
emitter = {},
prefix = '$PYEXTOBJPREFIX',
suffix = '$PYEXTOBJSUFFIX',
src_builder = ['CFile', 'CXXFile'],
source_scanner = SourceFileScanner,
single_source = 1)
env['BUILDERS']['PythonObject'] = pyobj
return pyobj
def createPythonExtensionBuilder(env):
"""This is a utility function that creates the PythonExtension Builder in
an Environment if it is not there already.
If it is already there, we return the existing one.
"""
try:
pyext = env['BUILDERS']['PythonExtension']
except KeyError:
import SCons.Action
import SCons.Defaults
action = SCons.Action.Action("$PYEXTLINKCOM", "$PYEXTLINKCOMSTR")
action_list = [ SCons.Defaults.SharedCheck,
action]
pyext = SCons.Builder.Builder(action = action_list,
emitter = "$SHLIBEMITTER",
prefix = '$PYEXTPREFIX',
suffix = '$PYEXTSUFFIX',
target_scanner = ProgramScanner,
src_suffix = '$PYEXTOBJSUFFIX',
src_builder = 'PythonObject')
env['BUILDERS']['PythonExtension'] = pyext
return pyext
def pyext_coms(platform):
"""Return PYEXTCCCOM, PYEXTCXXCOM and PYEXTLINKCOM for the given
platform."""
if platform == 'win32':
pyext_cccom = "$PYEXTCC /Fo$TARGET /c $PYEXTCCSHARED "\
"$PYEXTCFLAGS $PYEXTCCFLAGS $_CCCOMCOM "\
"$_PYEXTCPPINCFLAGS $SOURCES"
pyext_cxxcom = "$PYEXTCXX /Fo$TARGET /c $PYEXTCSHARED "\
"$PYEXTCXXFLAGS $PYEXTCCFLAGS $_CCCOMCOM "\
"$_PYEXTCPPINCFLAGS $SOURCES"
pyext_linkcom = '${TEMPFILE("$PYEXTLINK $PYEXTLINKFLAGS '\
'/OUT:$TARGET.windows $( $_LIBDIRFLAGS $) '\
'$_LIBFLAGS $_PYEXTRUNTIME $SOURCES.windows")}'
else:
pyext_cccom = "$PYEXTCC -o $TARGET -c $PYEXTCCSHARED "\
"$PYEXTCFLAGS $PYEXTCCFLAGS $_CCCOMCOM "\
"$_PYEXTCPPINCFLAGS $SOURCES"
pyext_cxxcom = "$PYEXTCXX -o $TARGET -c $PYEXTCSHARED "\
"$PYEXTCXXFLAGS $PYEXTCCFLAGS $_CCCOMCOM "\
"$_PYEXTCPPINCFLAGS $SOURCES"
pyext_linkcom = "$PYEXTLINK -o $TARGET $PYEXTLINKFLAGS "\
"$SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_PYEXTRUNTIME"
if platform == 'darwin':
pyext_linkcom += ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS'
return pyext_cccom, pyext_cxxcom, pyext_linkcom
def set_basic_vars(env):
# Set construction variables which are independant on whether we are using
# distutils or not.
env['PYEXTCPPPATH'] = SCons.Util.CLVar('$PYEXTINCPATH')
env['_PYEXTCPPINCFLAGS'] = '$( ${_concat(INCPREFIX, PYEXTCPPPATH, '\
'INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
env['PYEXTOBJSUFFIX'] = '$SHOBJSUFFIX'
env['PYEXTOBJPREFIX'] = '$SHOBJPREFIX'
env['PYEXTRUNTIME'] = SCons.Util.CLVar("")
# XXX: this should be handled with different flags
env['_PYEXTRUNTIME'] = '$( ${_concat(LIBLINKPREFIX, PYEXTRUNTIME, '\
'LIBLINKSUFFIX, __env__)} $)'
# XXX: This won't work in all cases (using mingw, for example). To make
# this work, we need to know whether PYEXTCC accepts /c and /Fo or -c -o.
# This is difficult with the current way tools work in scons.
pycc, pycxx, pylink = pyext_coms(sys.platform)
env['PYEXTLINKFLAGSEND'] = SCons.Util.CLVar('$LINKFLAGSEND')
env['PYEXTCCCOM'] = pycc
env['PYEXTCXXCOM'] = pycxx
env['PYEXTLINKCOM'] = pylink
def _set_configuration_nodistutils(env):
# Set env variables to sensible values when not using distutils
def_cfg = {'PYEXTCC' : '$SHCC',
'PYEXTCFLAGS' : '$SHCFLAGS',
'PYEXTCCFLAGS' : '$SHCCFLAGS',
'PYEXTCXX' : '$SHCXX',
'PYEXTCXXFLAGS' : '$SHCXXFLAGS',
'PYEXTLINK' : '$LDMODULE',
'PYEXTSUFFIX' : '$LDMODULESUFFIX',
'PYEXTPREFIX' : ''}
if sys.platform == 'darwin':
def_cfg['PYEXTSUFFIX'] = '.so'
for k, v in def_cfg.items():
ifnotset(env, k, v)
ifnotset(env, 'PYEXT_ALLOW_UNDEFINED',
SCons.Util.CLVar('$ALLOW_UNDEFINED'))
ifnotset(env, 'PYEXTLINKFLAGS', SCons.Util.CLVar('$LDMODULEFLAGS'))
env.AppendUnique(PYEXTLINKFLAGS = env['PYEXT_ALLOW_UNDEFINED'])
def ifnotset(env, name, value):
if not env.has_key(name):
env[name] = value
def set_configuration(env, use_distutils):
"""Set construction variables which are platform dependants.
If use_distutils == True, use distutils configuration. Otherwise, use
'sensible' default.
Any variable already defined is untouched."""
# We define commands as strings so that we can either execute them using
# eval (same python for scons and distutils) or by executing them through
# the shell.
dist_cfg = {'PYEXTCC': "sysconfig.get_config_var('CC')",
'PYEXTCFLAGS': "sysconfig.get_config_var('CFLAGS')",
'PYEXTCCSHARED': "sysconfig.get_config_var('CCSHARED')",
'PYEXTLINKFLAGS': "sysconfig.get_config_var('LDFLAGS')",
'PYEXTLINK': "sysconfig.get_config_var('LDSHARED')",
'PYEXTINCPATH': "sysconfig.get_python_inc()",
'PYEXTSUFFIX': "sysconfig.get_config_var('SO')"}
from distutils import sysconfig
# We set the python path even when not using distutils, because we rarely
# want to change this, even if not using distutils
ifnotset(env, 'PYEXTINCPATH', sysconfig.get_python_inc())
if use_distutils:
for k, v in dist_cfg.items():
ifnotset(env, k, eval(v))
else:
_set_configuration_nodistutils(env)
def generate(env):
"""Add Builders and construction variables for python extensions to an
Environment."""
if not env.has_key('PYEXT_USE_DISTUTILS'):
env['PYEXT_USE_DISTUTILS'] = False
# This sets all constructions variables used for pyext builders.
set_basic_vars(env)
set_configuration(env, env['PYEXT_USE_DISTUTILS'])
# Create the PythonObject builder
pyobj = createPythonObjectBuilder(env)
action = SCons.Action.Action("$PYEXTCCCOM", "$PYEXTCCCOMSTR")
pyobj.add_emitter('.c', SCons.Defaults.SharedObjectEmitter)
pyobj.add_action('.c', action)
action = SCons.Action.Action("$PYEXTCXXCOM", "$PYEXTCXXCOMSTR")
pyobj.add_emitter('$CXXFILESUFFIX', SCons.Defaults.SharedObjectEmitter)
pyobj.add_action('$CXXFILESUFFIX', action)
# Create the PythonExtension builder
createPythonExtensionBuilder(env)
def exists(env):
try:
# This is not quite right: if someone defines all variables by himself,
# it would work without distutils
from distutils import sysconfig
return True
except ImportError:
return False
......@@ -6,7 +6,6 @@ out_fname = pyx_to_dll("foo.pyx")
import os
import sys
import distutils
from distutils.dist import Distribution
from distutils.errors import DistutilsArgError, DistutilsError, CCompilerError
from distutils.extension import Extension
......@@ -16,11 +15,13 @@ try:
HAS_CYTHON = True
except ImportError:
HAS_CYTHON = False
import shutil
DEBUG = 0
_reloads={}
def pyx_to_dll(filename, ext = None, force_rebuild = 0,
build_in_temp=False, pyxbuild_dir=None):
build_in_temp=False, pyxbuild_dir=None, setup_args={}, reload_support=False):
"""Compile a PYX file to a DLL and return the name of the generated .so
or .dll ."""
assert os.path.exists(filename), "Could not find %s" % os.path.abspath(filename)
......@@ -37,7 +38,8 @@ def pyx_to_dll(filename, ext = None, force_rebuild = 0,
if not pyxbuild_dir:
pyxbuild_dir = os.path.join(path, "_pyxbld")
if DEBUG:
script_args=setup_args.get("script_args",[])
if DEBUG or "--verbose" in script_args:
quiet = "--verbose"
else:
quiet = "--quiet"
......@@ -46,7 +48,11 @@ def pyx_to_dll(filename, ext = None, force_rebuild = 0,
args.append("--force")
if HAS_CYTHON and build_in_temp:
args.append("--pyrex-c-in-temp")
dist = Distribution({"script_name": None, "script_args": args})
sargs = setup_args.copy()
sargs.update(
{"script_name": None,
"script_args": args + script_args} )
dist = Distribution(sargs)
if not dist.ext_modules:
dist.ext_modules = []
dist.ext_modules.append(ext)
......@@ -60,6 +66,10 @@ def pyx_to_dll(filename, ext = None, force_rebuild = 0,
except ValueError: pass
dist.parse_config_files(config_files)
cfgfiles = dist.find_config_files()
try: cfgfiles.remove('setup.cfg')
except ValueError: pass
dist.parse_config_files(cfgfiles)
try:
ok = dist.parse_command_line()
except DistutilsArgError:
......@@ -73,7 +83,39 @@ def pyx_to_dll(filename, ext = None, force_rebuild = 0,
try:
dist.run_commands()
return dist.get_command_obj("build_ext").get_outputs()[0]
obj_build_ext = dist.get_command_obj("build_ext")
so_path = obj_build_ext.get_outputs()[0]
if obj_build_ext.inplace:
# Python distutils get_outputs()[ returns a wrong so_path
# when --inplace ; see http://bugs.python.org/issue5977
# workaround:
so_path = os.path.join(os.path.dirname(filename),
os.path.basename(so_path))
if reload_support:
org_path = so_path
timestamp = os.path.getmtime(org_path)
global _reloads
last_timestamp, last_path, count = _reloads.get(org_path, (None,None,0) )
if last_timestamp == timestamp:
so_path = last_path
else:
basename = os.path.basename(org_path)
while count < 100:
count += 1
r_path = os.path.join(obj_build_ext.build_lib,
basename + '.reload%s'%count)
try:
import shutil # late import / reload_support is: debugging
shutil.copy2(org_path, r_path)
so_path = r_path
except IOError:
continue
break
else:
# used up all 100 slots
raise ImportError("reload count for %s reached maximum"%org_path)
_reloads[org_path]=(timestamp, so_path, count)
return so_path
except KeyboardInterrupt:
sys.exit(1)
except (IOError, os.error):
......@@ -82,16 +124,7 @@ def pyx_to_dll(filename, ext = None, force_rebuild = 0,
if DEBUG:
sys.stderr.write(error + "\n")
raise
else:
raise RuntimeError(error)
except (DistutilsError, CCompilerError):
if DEBUG:
raise
else:
exc = sys.exc_info()[1]
raise RuntimeError(repr(exc))
raise
if __name__=="__main__":
pyx_to_dll("dummy.pyx")
......
This diff is collapsed.
......@@ -7,4 +7,3 @@ numpy_ValueError_T172
unsignedbehaviour_T184
bad_c_struct_T252
missing_baseclass_in_predecl_T262
extended_unpacking_T409
cimport python_bool
cimport python_buffer
cimport python_bytes
cimport python_cobject
cimport python_complex
cimport python_dict
cimport python_exc
cimport python_float
cimport python_function
cimport python_getargs
cimport python_instance
cimport python_int
cimport python_iterator
......@@ -16,8 +19,8 @@ cimport python_method
cimport python_module
cimport python_number
cimport python_object
cimport python_parse
cimport python
cimport python_pycapsule
cimport python_ref
cimport python_sequence
cimport python_set
......@@ -26,5 +29,6 @@ cimport python_tuple
cimport python_type
cimport python_unicode
cimport python_version
cimport python_weakref
cimport stdio
cimport stdlib
......@@ -33,3 +33,43 @@ def single_c(int a, int b):
def cascaded_c(double a, double b, double c):
return a < b < c
def typed_cmp(list L):
"""
>>> typed_cmp([1,2,3])
False
False
False
False
"""
print L is Ellipsis
print Ellipsis is L
print 1 == L
print L == 1.5
def pointer_cmp():
"""
>>> pointer_cmp()
True
False
True
"""
cdef int* a = NULL
cdef double* b = NULL
cdef double** c = NULL
print a is NULL
print b is not NULL
print c == NULL
def c_cmp(double a, int b, long c):
"""
>>> c_cmp(1, 2, 3)
True
>>> c_cmp(1.5, 2, 2)
True
>>> c_cmp(1.5, 2, 0)
False
>>> c_cmp(1, 1, 3)
False
"""
return a < b <= c
cdef int raise_py_error() except *:
raise TypeError("custom")
cdef extern from "cpp_exceptions_helper.cpp":
cdef int raise_int_raw "raise_int"(bint fire) except +
cdef int raise_int_value "raise_int"(bint fire) except +ValueError
cdef int raise_int_custom "raise_int"(bint fire) except +raise_py_error
cdef int raise_index_raw "raise_index"(bint fire) except +
cdef int raise_index_value "raise_index"(bint fire) except +ValueError
cdef int raise_index_custom "raise_index"(bint fire) except +raise_py_error
def test_int_raw(bint fire):
"""
>>> test_int_raw(False)
>>> test_int_raw(True)
Traceback (most recent call last):
...
RuntimeError: Unknown exception
"""
raise_int_raw(fire)
def test_int_value(bint fire):
"""
>>> test_int_value(False)
>>> test_int_value(True)
Traceback (most recent call last):
...
ValueError
"""
raise_int_value(fire)
def test_int_custom(bint fire):
"""
>>> test_int_custom(False)
>>> test_int_custom(True)
Traceback (most recent call last):
...
TypeError: custom
"""
raise_int_custom(fire)
def test_index_raw(bint fire):
"""
>>> test_index_raw(False)
>>> test_index_raw(True)
Traceback (most recent call last):
...
IndexError: c++ error
"""
raise_index_raw(fire)
def test_index_value(bint fire):
"""
>>> test_index_value(False)
>>> test_index_value(True)
Traceback (most recent call last):
...
ValueError: c++ error
"""
raise_index_value(fire)
def test_index_custom(bint fire):
"""
>>> test_index_custom(False)
>>> test_index_custom(True)
Traceback (most recent call last):
...
TypeError: custom
"""
raise_index_custom(fire)
#include <stdexcept>
int raise_int(int fire) {
if (fire) {
throw 1;
}
return 0;
}
int raise_index(int fire) {
if (fire) {
throw std::out_of_range("c++ error");
}
return 0;
}
......@@ -15,3 +15,17 @@ cdef int obj2int(object ob) except *:
def foo(a):
cdef int i = obj2int(a)
CHKERR(i)
cdef int* except_expr(bint fire) except <int*>-1:
if fire:
raise RuntimeError
def test_except_expr(bint fire):
"""
>>> test_except_expr(False)
>>> test_except_expr(True)
Traceback (most recent call last):
...
RuntimeError
"""
except_expr(fire)
__doc__ = """
>>> simple()
(1, 2, [1, 2], [1, 2])
>>> extended()
(1, (), 2, [1, 2], [1, 2])
"""
def simple():
"""
>>> simple()
([1, 2], [1, 2])
"""
d = e = [1,2]
return d, e
def simple_parallel():
"""
>>> simple_parallel()
(1, 2, [1, 2], [1, 2])
"""
a, c = d = e = [1,2]
return a, c, d, e
def extended():
"""
>>> extended()
(1, [], 2, [1, 2], [1, 2])
"""
a, *b, c = d = e = [1,2]
return a, b, c, d, e
from __future__ import division
__doc__ = u"""
>>> doit(1,2)
(0.5, 0)
>>> doit(4,3)
(1.3333333333333333, 1)
>>> doit(4,3.0)
(1.3333333333333333, 1.0)
>>> doit(4,2)
(2.0, 2)
>>> constants()
(0.5, 0, 2.5, 2.0, 2.5, 2)
>>> py_mix(1)
(0.5, 0, 0.5, 0.0, 0.5, 0)
>>> py_mix_rev(4)
(0.25, 0, 1.25, 1.0, 1.25, 1)
>>> py_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> py_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
>>> int_mix(1)
(0.5, 0, 0.5, 0.0, 0.5, 0)
>>> int_mix_rev(4)
(0.25, 0, 1.25, 1.0, 1.25, 1)
>>> float_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> float_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
"""
def doit(x,y):
"""
>>> doit(1,2)
(0.5, 0)
>>> doit(4,3)
(1.3333333333333333, 1)
>>> doit(4,3.0)
(1.3333333333333333, 1.0)
>>> doit(4,2)
(2.0, 2)
"""
return x/y, x//y
def doit_inplace(x,y):
"""
>>> doit_inplace(1,2)
0.5
"""
x /= y
return x
def doit_inplace_floor(x,y):
"""
>>> doit_inplace_floor(1,2)
0
"""
x //= y
return x
def constants():
"""
>>> constants()
(0.5, 0, 2.5, 2.0, 2.5, 2)
"""
return 1/2, 1//2, 5/2.0, 5//2.0, 5/2, 5//2
def py_mix(a):
"""
>>> py_mix(1)
(0.5, 0, 0.5, 0.0, 0.5, 0)
>>> py_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
"""
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def py_mix_rev(a):
"""
>>> py_mix_rev(4)
(0.25, 0, 1.25, 1.0, 1.25, 1)
>>> py_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
"""
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
def int_mix(int a):
"""
>>> int_mix(1)
(0.5, 0, 0.5, 0.0, 0.5, 0)
"""
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def int_mix_rev(int a):
"""
>>> int_mix_rev(4)
(0.25, 0, 1.25, 1.0, 1.25, 1)
"""
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
def float_mix(float a):
"""
>>> float_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
"""
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def float_mix_rev(float a):
"""
>>> float_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
"""
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
__doc__ = u"""
>>> s == s_interned
True
>>> intern(s) is s_interned
True
>>> intern('abc') is s_interned
True
>>> intern('abc') is s_interned_dynamic
True
"""
s = 'abc'
s_interned = intern(s)
s_interned_dynamic = intern('a'+'b'+'c')
__doc__ = u"""
>>> sorted( get_locals(1,2,3, k=5) .items())
[('args', (2, 3)), ('kwds', {'k': 5}), ('x', 1), ('y', 'hi'), ('z', 5)]
"""
import sys
IS_PY3 = sys.version_info[0] >= 3
>>> in_locals('z')
True
>>> in_locals('args')
True
>>> in_locals('X')
False
"""
def get_locals(x, *args, **kwds):
cdef int z = 5
if IS_PY3:
y = u"hi"
else:
y = "hi"
y = "hi"
return locals()
def in_locals(x, *args, **kwds):
cdef int z = 5
y = "hi"
return x in locals()
def sorted(it):
l = list(it)
l.sort()
......
__doc__ = u"""
>>> sorted( get_locals(1,2,3, k=5) .items())
[('args', (2, 3)), ('kwds', {'k': 5}), ('x', 1), ('y', 'hi'), ('z', 5)]
>>> sorted(get_locals_items(1,2,3, k=5))
[('args', (2, 3)), ('kwds', {'k': 5}), ('x', 1), ('y', 'hi'), ('z', 5)]
>>> sorted(get_locals_items_listcomp(1,2,3, k=5))
[('args', (2, 3)), ('kwds', {'k': 5}), ('x', 1), ('y', 'hi'), ('z', 5)]
"""
def get_locals(x, *args, **kwds):
cdef int z = 5
y = "hi"
return locals()
def get_locals_items(x, *args, **kwds):
cdef int z = 5
y = "hi"
return locals().items()
def get_locals_items_listcomp(x, *args, **kwds):
cdef int z = 5
y = "hi"
return [ item for item in locals().items() ]
def sorted(it):
l = list(it)
l.sort()
return l
__doc__ = u"""
>>> sorted( get_locals(1,2,3, k=5) .items())
[('args', (2, 3)), ('kwds', {'k': 5}), ('x', 1), ('y', 'hi'), ('z', 5)]
>>> get_locals_rebound(1,2,3)
'REBOUND'
"""
def get_locals(x, *args, **kwds):
cdef int z = 5
y = "hi"
return locals()
def get_locals_rebound(x, *args, **kwds):
cdef int z = 5
locals = _locals
y = "hi"
return locals()
def _locals(): return "REBOUND"
def sorted(it):
l = list(it)
l.sort()
return l
# Py2.x mixed true-div/floor-div behaviour of '/' operator
__doc__ = u"""
>>> doit(1,2)
(0, 0)
>>> doit(4,3)
(1, 1)
>>> doit(4,3.0)
(1.3333333333333333, 1.0)
>>> doit(4,2)
(2, 2)
>>> constants()
(0, 0, 2.5, 2.0, 2, 2)
>>> py_mix(1)
(0, 0, 0.5, 0.0, 0, 0)
>>> py_mix_rev(4)
(0, 0, 1.25, 1.0, 1, 1)
>>> py_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> py_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
>>> int_mix(1)
(0, 0, 0.5, 0.0, 0, 0)
>>> int_mix_rev(4)
(0, 0, 1.25, 1.0, 1, 1)
>>> float_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
>>> float_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
"""
def doit(x,y):
"""
>>> doit(1,2)
(0, 0)
>>> doit(4,3)
(1, 1)
>>> doit(4,3.0)
(1.3333333333333333, 1.0)
>>> doit(4,2)
(2, 2)
"""
return x/y, x//y
def doit_inplace(x,y):
"""
>>> doit_inplace(1,2)
0
"""
x /= y
return x
def doit_inplace_floor(x,y):
"""
>>> doit_inplace_floor(1,2)
0
"""
x //= y
return x
def constants():
"""
>>> constants()
(0, 0, 2.5, 2.0, 2, 2)
"""
return 1/2, 1//2, 5/2.0, 5//2.0, 5/2, 5//2
def py_mix(a):
"""
>>> py_mix(1)
(0, 0, 0.5, 0.0, 0, 0)
>>> py_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
"""
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def py_mix_rev(a):
"""
>>> py_mix_rev(4)
(0, 0, 1.25, 1.0, 1, 1)
>>> py_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
"""
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
def int_mix(int a):
"""
>>> int_mix(1)
(0, 0, 0.5, 0.0, 0, 0)
"""
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def int_mix_rev(int a):
"""
>>> int_mix_rev(4)
(0, 0, 1.25, 1.0, 1, 1)
"""
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
def float_mix(float a):
"""
>>> float_mix(1.0)
(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)
"""
return a/2, a//2, a/2.0, a//2.0, a/2, a//2
def float_mix_rev(float a):
"""
>>> float_mix_rev(4.0)
(0.25, 0.0, 1.25, 1.0, 1.25, 1.0)
"""
return 1/a, 1//a, 5.0/a, 5.0//a, 5/a, 5//a
__doc__ = u"""
>>> swap(1,2)
(2, 1)
"""
cimport cython
@cython.test_assert_path_exists(
"//ParallelAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode/NameNode",
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode[@use_managed_ref=False]/NameNode",
)
@cython.test_fail_if_path_exists(
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode[@use_managed_ref=True]",
)
def swap(a,b):
"""
>>> swap(1,2)
(2, 1)
"""
a,b = b,a
return a,b
@cython.test_assert_path_exists(
"//ParallelAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode/NameNode",
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode[@use_managed_ref=False]/NameNode",
)
@cython.test_fail_if_path_exists(
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode[@use_managed_ref=True]",
)
def swap5(a,b,c,d,e):
"""
>>> swap5(1,2,3,4,5)
(5, 4, 3, 2, 1)
"""
a,b,c,d,e = e,d,c,b,a
return a,b,c,d,e
@cython.test_assert_path_exists(
"//ParallelAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode/NameNode",
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode[@use_managed_ref=False]/NameNode",
)
@cython.test_fail_if_path_exists(
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode[@use_managed_ref=True]",
)
cdef bint c_swap_cmp5(a, b, c, d, e):
a,b,c,d,e = e,d,c,b,a
return a > b > c > d > e
def swap_cmp5(a,b,c,d,e):
"""
>>> swap_cmp5(1,2,3,4,5)
True
"""
return c_swap_cmp5(a,b,c,d,e)
@cython.test_assert_path_exists(
"//ParallelAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode/NameNode",
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode[@use_managed_ref=True]/NameNode",
)
@cython.test_fail_if_path_exists(
"//ParallelAssignmentNode/SingleAssignmentNode//CoerceToTempNode[@use_managed_ref=False]",
)
def swap_py(a,b):
a,a = b,a
return a,b
@cython.test_assert_path_exists(
# "//ParallelAssignmentNode",
# "//ParallelAssignmentNode/SingleAssignmentNode",
# "//ParallelAssignmentNode/SingleAssignmentNode//IndexNode",
# "//ParallelAssignmentNode/SingleAssignmentNode//IndexNode[@use_managed_ref=False]",
)
@cython.test_fail_if_path_exists(
# "//ParallelAssignmentNode/SingleAssignmentNode//IndexNode[@use_managed_ref=True]",
)
def swap_list_items(list a, int i, int j):
"""
>>> l = [1,2,3,4]
>>> swap_list_items(l, 1, 2)
>>> l
[1, 3, 2, 4]
>>> swap_list_items(l, 3, 0)
>>> l
[4, 3, 2, 1]
>>> swap_list_items(l, 0, 5)
Traceback (most recent call last):
IndexError: list index out of range
>>> l
[4, 3, 2, 1]
"""
a[i], a[j] = a[j], a[i]
@cython.test_assert_path_exists(
"//ParallelAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode//IndexNode",
"//ParallelAssignmentNode/SingleAssignmentNode//IndexNode[@use_managed_ref=True]",
)
@cython.test_fail_if_path_exists(
"//ParallelAssignmentNode/SingleAssignmentNode//IndexNode[@use_managed_ref=False]",
)
def swap_list_items_py1(list a, int i, int j):
a[i], a[j] = a[j+1], a[i]
@cython.test_assert_path_exists(
"//ParallelAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode",
"//ParallelAssignmentNode/SingleAssignmentNode//IndexNode",
"//ParallelAssignmentNode/SingleAssignmentNode//IndexNode[@use_managed_ref=True]",
)
@cython.test_fail_if_path_exists(
"//ParallelAssignmentNode/SingleAssignmentNode//IndexNode[@use_managed_ref=False]",
)
def swap_list_items_py2(list a, int i, int j):
a[i], a[j] = a[i], a[i]
#cython: autotestdict=True
cdef class Foo:
pass
cdef class SubFoo(Foo):
pass
cdef class Bar:
pass
def foo1(arg):
"""
>>> foo1(Foo())
>>> foo1(SubFoo())
>>> foo1(None)
>>> foo1(123)
>>> foo1(Bar())
"""
cdef Foo val = <Foo>arg
def foo2(arg):
"""
>>> foo2(Foo())
>>> foo2(SubFoo())
>>> foo2(None)
>>> foo2(123)
Traceback (most recent call last):
...
TypeError: Cannot convert int to typetest_T417.Foo
>>> foo2(Bar())
Traceback (most recent call last):
...
TypeError: Cannot convert typetest_T417.Bar to typetest_T417.Foo
"""
cdef Foo val = arg
def foo3(arg):
"""
>>> foo3(Foo())
>>> foo3(SubFoo())
>>> foo3(None)
Traceback (most recent call last):
...
TypeError: Cannot convert NoneType to typetest_T417.Foo
>>> foo3(123)
Traceback (most recent call last):
...
TypeError: Cannot convert int to typetest_T417.Foo
>>> foo2(Bar())
Traceback (most recent call last):
...
TypeError: Cannot convert typetest_T417.Bar to typetest_T417.Foo
"""
cdef val = <Foo?>arg
cdef int count = 0
cdef object getFoo():
global count
count += 1
return Foo()
def test_getFoo():
"""
>>> test_getFoo()
1
"""
cdef int old_count = count
cdef Foo x = getFoo()
return count - old_count
def test_getFooCast():
"""
>>> test_getFooCast()
1
"""
cdef int old_count = count
cdef Foo x = <Foo?>getFoo()
return count - old_count
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