Commit bbba7ca8 authored by Dag Sverre Seljebotn's avatar Dag Sverre Seljebotn

merge

parents bd9d0283 a5e1aea2
...@@ -11,3 +11,5 @@ build/ ...@@ -11,3 +11,5 @@ build/
*.orig *.orig
*.rej *.rej
*.dep *.dep
tags
import re
from Cython.Compiler.Visitor import CythonTransform
from Cython.Compiler.Nodes import DefNode, CFuncDefNode
from Cython.Compiler.Errors import CompileError
from Cython.Compiler.StringEncoding import EncodedString
from Cython.Compiler import Options
class EmbedSignature(CythonTransform):
SPECIAL_METHOD_RE = re.compile(r'__\w+__')
def __init__(self, context):
super(EmbedSignature, self).__init__(context)
self.denv = None # XXX
self.is_in_class = False
self.class_name = None
def _fmt_basic_c_type_modifiers(self, ctype):
longness = ctype.longness
modifiers = ''
if longness < 0:
modifiers = 'short '
elif longness > 0:
modifiers = 'long ' * longness
signed = ctype.signed
if signed == 0:
modifiers = 'unsigned ' + modifiers
elif signed == 2:
modifiers = 'signed ' + modifiers
return modifiers[:-1] # strip final space
def _fmt_arg_type(self, arg):
try:
base_type = arg.base_type
arg_type = base_type.name
except AttributeError:
return ''
if base_type.is_basic_c_type:
modifiers = self._fmt_basic_c_type_modifiers(base_type)
if modifiers:
arg_type = '%s %s' % (modifiers, arg_type)
return arg_type
def _fmt_arg_name(self, arg):
try:
return arg.declarator.name
except AttributeError:
return arg.declarator.base.name
def _fmt_arg_defv(self, arg):
if not arg.default:
return None
try:
denv = self.denv # XXX
ctval = arg.default.compile_time_value(self.denv)
return '%s' % ctval
except Exception:
try:
return arg.default.name # XXX
except AttributeError:
return '<???>'
def _fmt_arg(self, arg):
arg_type = self._fmt_arg_type(arg)
arg_name = self._fmt_arg_name(arg)
arg_defv = self._fmt_arg_defv(arg)
doc = arg_name
if arg_type:
doc = ('%s ' % arg_type) + doc
if arg_defv:
doc = doc + ('=%s' % arg_defv)
return doc
def _fmt_arglist(self, args,
npargs=0, pargs=None,
nkargs=0, kargs=None):
arglist = []
for arg in args:
arg_doc = self._fmt_arg(arg)
arglist.append(arg_doc)
if pargs:
arglist.insert(npargs, '*%s' % pargs.name)
elif nkargs:
arglist.insert(npargs, '*')
if kargs:
arglist.append('**%s' % kargs.name)
return arglist
def _fmt_ret_type(self, ret):
ret_type = ret.name
if ret_type is None:
return ''
modifiers = self._fmt_basic_c_type_modifiers(ret)
if modifiers:
ret_type = '%s %s' % (modifiers, ret_type)
return ret_type
def _fmt_signature(self, cls_name, func_name, args,
npargs=0, pargs=None,
nkargs=0, kargs=None,
return_type=None):
arglist = self._fmt_arglist(args,
npargs, pargs,
nkargs, kargs)
arglist_doc = ', '.join(arglist)
func_doc = '%s(%s)' % (func_name, arglist_doc)
if cls_name:
func_doc = '%s.%s' % (cls_name, func_doc)
if return_type:
ret_doc = self._fmt_ret_type(return_type)
if ret_doc:
func_doc = '%s -> %s' % (func_doc, ret_doc)
return func_doc
def _embed_signature(self, signature, node_doc):
if node_doc:
return signature + '\n' + node_doc
else:
return signature
def __call__(self, node):
if not Options.docstrings:
return node
else:
return super(EmbedSignature, self).__call__(node)
def visit_ClassDefNode(self, node):
oldincls = self.is_in_class
oldname = self.class_name
self.is_in_class = True
try:
# PyClassDefNode
self.class_name = node.name
except AttributeError:
# CClassDefNode
self.class_name = node.class_name
self.visitchildren(node)
self.is_in_class = oldincls
self.class_name = oldname
return node
def visit_FuncDefNode(self, node):
if not self.current_directives['embedsignature']:
return node
signature = None
if type(node) is DefNode: # def FOO(...):
special_method = (self.is_in_class and \
self.SPECIAL_METHOD_RE.match(node.name))
if not special_method:
nkargs = getattr(node, 'num_kwonly_args', 0)
npargs = len(node.args) - nkargs
signature = self._fmt_signature(
self.class_name, node.name, node.args,
npargs, node.star_arg,
nkargs, node.starstar_arg,
return_type=None)
elif type(node) is CFuncDefNode:
if node.overridable: # cpdef FOO(...):
signature = self._fmt_signature(
self.class_name, node.declarator.base.name,
node.declarator.args,
return_type=node.base_type)
else: # should not fall here ...
assert False
if signature:
new_doc = self._embed_signature(signature, node.doc)
node.doc = EncodedString(new_doc) # XXX
return node
...@@ -92,7 +92,7 @@ class IntroduceBufferAuxiliaryVars(CythonTransform): ...@@ -92,7 +92,7 @@ class IntroduceBufferAuxiliaryVars(CythonTransform):
mode = entry.type.mode mode = entry.type.mode
if mode == 'full': if mode == 'full':
suboffsetvars = [var(Naming.bufsuboffset_prefix, i, "-1") for i in range(entry.type.ndim)] suboffsetvars = [var(Naming.bufsuboffset_prefix, i, "-1") for i in range(entry.type.ndim)]
elif mode == 'strided': else:
suboffsetvars = None suboffsetvars = None
entry.buffer_aux = Symtab.BufferAux(bufinfo, stridevars, shapevars, suboffsetvars) entry.buffer_aux = Symtab.BufferAux(bufinfo, stridevars, shapevars, suboffsetvars)
...@@ -113,17 +113,18 @@ class IntroduceBufferAuxiliaryVars(CythonTransform): ...@@ -113,17 +113,18 @@ class IntroduceBufferAuxiliaryVars(CythonTransform):
# #
# Analysis # Analysis
# #
buffer_options = ("dtype", "ndim", "mode") # ordered! buffer_options = ("dtype", "ndim", "mode", "negative_indices", "cast") # ordered!
buffer_defaults = {"ndim": 1, "mode": "full"} buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True, "cast": False}
buffer_positional_options_count = 1 # anything beyond this needs keyword argument buffer_positional_options_count = 1 # anything beyond this needs keyword argument
ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option' ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option'
ERR_BUF_TOO_MANY = 'Too many buffer options' ERR_BUF_TOO_MANY = 'Too many buffer options'
ERR_BUF_DUP = '"%s" buffer option already supplied' ERR_BUF_DUP = '"%s" buffer option already supplied'
ERR_BUF_MISSING = '"%s" missing' ERR_BUF_MISSING = '"%s" missing'
ERR_BUF_MODE = 'Only allowed buffer modes are "full" or "strided" (as a compile-time string)' ERR_BUF_MODE = 'Only allowed buffer modes are: "c", "fortran", "full", "strided" (as a compile-time string)'
ERR_BUF_NDIM = 'ndim must be a non-negative integer' ERR_BUF_NDIM = 'ndim must be a non-negative integer'
ERR_BUF_DTYPE = 'dtype must be "object", numeric type or a struct' ERR_BUF_DTYPE = 'dtype must be "object", numeric type or a struct'
ERR_BUF_BOOL = '"%s" must be a boolean'
def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, need_complete=True): def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, need_complete=True):
""" """
...@@ -175,9 +176,17 @@ def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, nee ...@@ -175,9 +176,17 @@ def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, nee
raise CompileError(globalpos, ERR_BUF_NDIM) raise CompileError(globalpos, ERR_BUF_NDIM)
mode = options.get("mode") mode = options.get("mode")
if mode and not (mode in ('full', 'strided')): if mode and not (mode in ('full', 'strided', 'c', 'fortran')):
raise CompileError(globalpos, ERR_BUF_MODE) raise CompileError(globalpos, ERR_BUF_MODE)
def assert_bool(name):
x = options.get(name)
if not isinstance(x, bool):
raise CompileError(globalpos, ERR_BUF_BOOL % name)
assert_bool('negative_indices')
assert_bool('cast')
return options return options
...@@ -188,10 +197,15 @@ def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, nee ...@@ -188,10 +197,15 @@ def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, nee
def get_flags(buffer_aux, buffer_type): def get_flags(buffer_aux, buffer_type):
flags = 'PyBUF_FORMAT' flags = 'PyBUF_FORMAT'
if buffer_type.mode == 'full': mode = buffer_type.mode
if mode == 'full':
flags += '| PyBUF_INDIRECT' flags += '| PyBUF_INDIRECT'
elif buffer_type.mode == 'strided': elif mode == 'strided':
flags += '| PyBUF_STRIDES' flags += '| PyBUF_STRIDES'
elif mode == 'c':
flags += '| PyBUF_C_CONTIGUOUS'
elif mode == 'fortran':
flags += '| PyBUF_F_CONTIGUOUS'
else: else:
assert False assert False
if buffer_aux.writable_needed: flags += "| PyBUF_WRITABLE" if buffer_aux.writable_needed: flags += "| PyBUF_WRITABLE"
...@@ -224,13 +238,15 @@ def put_acquire_arg_buffer(entry, code, pos): ...@@ -224,13 +238,15 @@ def put_acquire_arg_buffer(entry, code, pos):
code.globalstate.use_utility_code(acquire_utility_code) code.globalstate.use_utility_code(acquire_utility_code)
buffer_aux = entry.buffer_aux buffer_aux = entry.buffer_aux
getbuffer_cname = get_getbuffer_code(entry.type.dtype, code) getbuffer_cname = get_getbuffer_code(entry.type.dtype, code)
# Acquire any new buffer # Acquire any new buffer
code.putln(code.error_goto_if("%s((PyObject*)%s, &%s, %s, %d) == -1" % ( code.putln(code.error_goto_if("%s((PyObject*)%s, &%s, %s, %d, %d) == -1" % (
getbuffer_cname, getbuffer_cname,
entry.cname, entry.cname,
entry.buffer_aux.buffer_info_var.cname, entry.buffer_aux.buffer_info_var.cname,
get_flags(buffer_aux, entry.type), get_flags(buffer_aux, entry.type),
entry.type.ndim), pos)) entry.type.ndim,
int(entry.type.cast)), pos))
# An exception raised in arg parsing cannot be catched, so no # An exception raised in arg parsing cannot be catched, so no
# need to care about the buffer then. # need to care about the buffer then.
put_unpack_buffer_aux_into_scope(buffer_aux, entry.type.mode, code) put_unpack_buffer_aux_into_scope(buffer_aux, entry.type.mode, code)
...@@ -264,11 +280,12 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type, ...@@ -264,11 +280,12 @@ def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
bufstruct = buffer_aux.buffer_info_var.cname bufstruct = buffer_aux.buffer_info_var.cname
flags = get_flags(buffer_aux, buffer_type) flags = get_flags(buffer_aux, buffer_type)
getbuffer = "%s((PyObject*)%%s, &%s, %s, %d)" % (get_getbuffer_code(buffer_type.dtype, code), getbuffer = "%s((PyObject*)%%s, &%s, %s, %d, %d)" % (get_getbuffer_code(buffer_type.dtype, code),
# note: object is filled in later (%%s) # note: object is filled in later (%%s)
bufstruct, bufstruct,
flags, flags,
buffer_type.ndim) buffer_type.ndim,
int(buffer_type.cast))
if is_initialized: if is_initialized:
# Release any existing buffer # Release any existing buffer
...@@ -331,6 +348,7 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod ...@@ -331,6 +348,7 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod
""" """
bufaux = entry.buffer_aux bufaux = entry.buffer_aux
bufstruct = bufaux.buffer_info_var.cname bufstruct = bufaux.buffer_info_var.cname
negative_indices = entry.type.negative_indices
if options['boundscheck']: if options['boundscheck']:
# Check bounds and fix negative indices. # Check bounds and fix negative indices.
...@@ -344,9 +362,12 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod ...@@ -344,9 +362,12 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod
if signed != 0: if signed != 0:
# not unsigned, deal with negative index # not unsigned, deal with negative index
code.putln("if (%s < 0) {" % cname) code.putln("if (%s < 0) {" % cname)
if negative_indices:
code.putln("%s += %s;" % (cname, shape.cname)) code.putln("%s += %s;" % (cname, shape.cname))
code.putln("if (%s) %s = %d;" % ( code.putln("if (%s) %s = %d;" % (
code.unlikely("%s < 0" % cname), tmp_cname, dim)) code.unlikely("%s < 0" % cname), tmp_cname, dim))
else:
code.putln("%s = %d;" % (tmp_cname, dim))
code.put("} else ") code.put("} else ")
# check bounds in positive direction # check bounds in positive direction
code.putln("if (%s) %s = %d;" % ( code.putln("if (%s) %s = %d;" % (
...@@ -359,7 +380,7 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod ...@@ -359,7 +380,7 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod
code.putln(code.error_goto(pos)) code.putln(code.error_goto(pos))
code.end_block() code.end_block()
code.funcstate.release_temp(tmp_cname) code.funcstate.release_temp(tmp_cname)
else: elif negative_indices:
# Only fix negative indices. # Only fix negative indices.
for signed, cname, shape in zip(index_signeds, index_cnames, for signed, cname, shape in zip(index_signeds, index_cnames,
bufaux.shapevars): bufaux.shapevars):
...@@ -367,28 +388,43 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod ...@@ -367,28 +388,43 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, cod
code.putln("if (%s < 0) %s += %s;" % (cname, cname, shape.cname)) code.putln("if (%s < 0) %s += %s;" % (cname, cname, shape.cname))
# Create buffer lookup and return it # Create buffer lookup and return it
# This is done via utility macros/inline functions, which vary
# according to the access mode used.
params = [] params = []
nd = entry.type.ndim nd = entry.type.ndim
if entry.type.mode == 'full': mode = entry.type.mode
if mode == 'full':
for i, s, o in zip(index_cnames, bufaux.stridevars, bufaux.suboffsetvars): for i, s, o in zip(index_cnames, bufaux.stridevars, bufaux.suboffsetvars):
params.append(i) params.append(i)
params.append(s.cname) params.append(s.cname)
params.append(o.cname) params.append(o.cname)
funcname = "__Pyx_BufPtrFull%dd" % nd funcname = "__Pyx_BufPtrFull%dd" % nd
funcgen = buf_lookup_full_code funcgen = buf_lookup_full_code
else: else:
if mode == 'strided':
funcname = "__Pyx_BufPtrStrided%dd" % nd
funcgen = buf_lookup_strided_code
elif mode == 'c':
funcname = "__Pyx_BufPtrCContig%dd" % nd
funcgen = buf_lookup_c_code
elif mode == 'fortran':
funcname = "__Pyx_BufPtrFortranContig%dd" % nd
funcgen = buf_lookup_fortran_code
else:
assert False
for i, s in zip(index_cnames, bufaux.stridevars): for i, s in zip(index_cnames, bufaux.stridevars):
params.append(i) params.append(i)
params.append(s.cname) params.append(s.cname)
funcname = "__Pyx_BufPtrStrided%dd" % nd
funcgen = buf_lookup_strided_code
# Make sure the utility code is available # Make sure the utility code is available
code.globalstate.use_code_from(funcgen, name=funcname, nd=nd) code.globalstate.use_code_from(funcgen, name=funcname, nd=nd)
ptrcode = "%s(%s.buf, %s)" % (funcname, bufstruct, ", ".join(params)) ptr_type = entry.type.buffer_ptr_type
return entry.type.buffer_ptr_type.cast_code(ptrcode) ptrcode = "%s(%s, %s.buf, %s)" % (funcname,
ptr_type.declaration_code(""),
bufstruct,
", ".join(params))
return ptrcode
def use_empty_bufstruct_code(env, max_ndim): def use_empty_bufstruct_code(env, max_ndim):
...@@ -399,33 +435,59 @@ def use_empty_bufstruct_code(env, max_ndim): ...@@ -399,33 +435,59 @@ def use_empty_bufstruct_code(env, max_ndim):
env.use_utility_code([code, ""], "empty_bufstruct_code") env.use_utility_code([code, ""], "empty_bufstruct_code")
def buf_lookup_strided_code(proto, defin, name, nd):
"""
Generates a buffer lookup function for the right number
of dimensions. The function gives back a void* at the right location.
"""
# _i_ndex, _s_tride
args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd)])
proto.putln("#define %s(buf, %s) ((char*)buf + %s)" % (name, args, offset))
def buf_lookup_full_code(proto, defin, name, nd): def buf_lookup_full_code(proto, defin, name, nd):
""" """
Generates a buffer lookup function for the right number Generates a buffer lookup function for the right number
of dimensions. The function gives back a void* at the right location. of dimensions. The function gives back a void* at the right location.
""" """
# _i_ndex, _s_tride, sub_o_ffset # _i_ndex, _s_tride, sub_o_ffset
args = ", ".join(["Py_ssize_t i%d, Py_ssize_t s%d, Py_ssize_t o%d" % (i, i, i) for i in range(nd)]) macroargs = ", ".join(["i%d, s%d, o%d" % (i, i, i) for i in range(nd)])
proto.putln("static INLINE void* %s(void* buf, %s);" % (name, args)) proto.putln("#define %s(type, buf, %s) (type)(%s_imp(buf, %s))" % (name, macroargs, name, macroargs))
funcargs = ", ".join(["Py_ssize_t i%d, Py_ssize_t s%d, Py_ssize_t o%d" % (i, i, i) for i in range(nd)])
proto.putln("static INLINE void* %s_imp(void* buf, %s);" % (name, funcargs))
defin.putln(dedent(""" defin.putln(dedent("""
static INLINE void* %s(void* buf, %s) { static INLINE void* %s_imp(void* buf, %s) {
char* ptr = (char*)buf; char* ptr = (char*)buf;
""") % (name, args) + "".join([dedent("""\ """) % (name, funcargs) + "".join([dedent("""\
ptr += s%d * i%d; ptr += s%d * i%d;
if (o%d >= 0) ptr = *((char**)ptr) + o%d; if (o%d >= 0) ptr = *((char**)ptr) + o%d;
""") % (i, i, i, i) for i in range(nd)] """) % (i, i, i, i) for i in range(nd)]
) + "\nreturn ptr;\n}") ) + "\nreturn ptr;\n}")
def buf_lookup_strided_code(proto, defin, name, nd):
"""
Generates a buffer lookup function for the right number
of dimensions. The function gives back a void* at the right location.
"""
# _i_ndex, _s_tride
args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd)])
proto.putln("#define %s(type, buf, %s) (type)((char*)buf + %s)" % (name, args, offset))
def buf_lookup_c_code(proto, defin, name, nd):
"""
Similar to strided lookup, but can assume that the last dimension
doesn't need a multiplication as long as.
Still we keep the same signature for now.
"""
if nd == 1:
proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name)
else:
args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd - 1)])
proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, nd - 1))
def buf_lookup_fortran_code(proto, defin, name, nd):
"""
Like C lookup, but the first index is optimized instead.
"""
if nd == 1:
proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name)
else:
args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(1, nd)])
proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, 0))
# #
# Utils for creating type string checkers # Utils for creating type string checkers
...@@ -559,17 +621,17 @@ def get_getbuffer_code(dtype, code): ...@@ -559,17 +621,17 @@ def get_getbuffer_code(dtype, code):
name = "__Pyx_GetBuffer_%s" % mangle_dtype_name(dtype) name = "__Pyx_GetBuffer_%s" % mangle_dtype_name(dtype)
if not code.globalstate.has_code(name): if not code.globalstate.has_code(name):
code.globalstate.use_utility_code(acquire_utility_code) code.globalstate.use_utility_code(acquire_utility_code)
typestringchecker = "__Pyx_CheckTypestring_%s" % mangle_dtype_name(dtype) typestringchecker = "__Pyx_CheckTypestring_%s" % mangle_dtype_name(dtype)
code.globalstate.use_code_from(create_typestringchecker, code.globalstate.use_code_from(create_typestringchecker,
typestringchecker, typestringchecker,
dtype=dtype) dtype=dtype)
dtype_name = str(dtype) dtype_name = str(dtype)
dtype_cname = dtype.declaration_code("")
utilcode = [dedent(""" utilcode = [dedent("""
static int %s(PyObject* obj, Py_buffer* buf, int flags, int nd); /*proto*/ static int %s(PyObject* obj, Py_buffer* buf, int flags, int nd, int cast); /*proto*/
""") % name, dedent(""" """) % name, dedent("""
static int %(name)s(PyObject* obj, Py_buffer* buf, int flags, int nd) { static int %(name)s(PyObject* obj, Py_buffer* buf, int flags, int nd, int cast) {
const char* ts; const char* ts;
if (obj == Py_None) { if (obj == Py_None) {
__Pyx_ZeroBuffer(buf); __Pyx_ZeroBuffer(buf);
...@@ -581,14 +643,26 @@ def get_getbuffer_code(dtype, code): ...@@ -581,14 +643,26 @@ def get_getbuffer_code(dtype, code):
__Pyx_BufferNdimError(buf, nd); __Pyx_BufferNdimError(buf, nd);
goto fail; goto fail;
} }
if (!cast) {
ts = buf->format; ts = buf->format;
ts = %(typestringchecker)s(ts); if (!ts) goto fail;
ts = __Pyx_ConsumeWhitespace(ts); ts = __Pyx_ConsumeWhitespace(ts);
if (!ts) goto fail;
ts = %(typestringchecker)s(ts);
if (!ts) goto fail;
ts = __Pyx_ConsumeWhitespace(ts);
if (!ts) goto fail;
if (*ts != 0) { if (*ts != 0) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"Buffer format string specifies more data than '%(dtype_name)s' can hold (expected end, got '%%s')", ts); "Buffer format string specifies more data than '%(dtype_name)s' can hold (expected end, got '%%s')", ts);
goto fail; goto fail;
} }
} else {
if (buf->itemsize != sizeof(%(dtype_cname)s)) {
PyErr_SetString(PyExc_ValueError,
"Attempted cast of buffer to datatype of different size.");
goto fail;
}
}
if (buf->suboffsets == NULL) buf->suboffsets = __Pyx_minusones; if (buf->suboffsets == NULL) buf->suboffsets = __Pyx_minusones;
return 0; return 0;
fail:; fail:;
......
...@@ -181,7 +181,8 @@ def init_builtins(): ...@@ -181,7 +181,8 @@ def init_builtins():
init_builtin_funcs() init_builtin_funcs()
init_builtin_types() init_builtin_types()
init_builtin_structs() init_builtin_structs()
global list_type, tuple_type, dict_type, unicode_type global list_type, tuple_type, dict_type, unicode_type, type_type
type_type = builtin_scope.lookup('type').type
list_type = builtin_scope.lookup('list').type list_type = builtin_scope.lookup('list').type
tuple_type = builtin_scope.lookup('tuple').type tuple_type = builtin_scope.lookup('tuple').type
dict_type = builtin_scope.lookup('dict').type dict_type = builtin_scope.lookup('dict').type
......
...@@ -36,7 +36,7 @@ Options: ...@@ -36,7 +36,7 @@ Options:
-D, --no-docstrings Remove docstrings. -D, --no-docstrings Remove docstrings.
-a, --annotate Produce a colorized HTML version of the source. -a, --annotate Produce a colorized HTML version of the source.
--convert-range Convert for loops using range() function to for...from loops. --line-directives Produce #line directives pointing to the .pyx source
--cplus Output a c++ rather than c file. --cplus Output a c++ rather than c file.
-X, --directive <name>=<value>[,<name=value,...] Overrides a compiler directive -X, --directive <name>=<value>[,<name=value,...] Overrides a compiler directive
""" """
...@@ -114,6 +114,8 @@ def parse_command_line(args): ...@@ -114,6 +114,8 @@ def parse_command_line(args):
Options.annotate = True Options.annotate = True
elif option == "--convert-range": elif option == "--convert-range":
Options.convert_range = True Options.convert_range = True
elif option == "--line-directives":
options.emit_linenums = True
elif option in ("-X", "--directive"): elif option in ("-X", "--directive"):
try: try:
options.pragma_overrides = Options.parse_option_list(pop_arg()) options.pragma_overrides = Options.parse_option_list(pop_arg())
......
...@@ -161,7 +161,13 @@ class GlobalState(object): ...@@ -161,7 +161,13 @@ class GlobalState(object):
# interned_nums # interned_nums
# cached_builtins # cached_builtins
def __init__(self, rootwriter): # directives set Temporary variable used to track
# the current set of directives in the code generation
# process.
directives = {}
def __init__(self, rootwriter, emit_linenums=False):
self.filename_table = {} self.filename_table = {}
self.filename_list = [] self.filename_list = []
self.input_file_contents = {} self.input_file_contents = {}
...@@ -169,6 +175,7 @@ class GlobalState(object): ...@@ -169,6 +175,7 @@ class GlobalState(object):
self.declared_cnames = {} self.declared_cnames = {}
self.pystring_table_needed = False self.pystring_table_needed = False
self.in_utility_code_generation = False self.in_utility_code_generation = False
self.emit_linenums = emit_linenums
def initwriters(self, rootwriter): def initwriters(self, rootwriter):
self.utilprotowriter = rootwriter.new_writer() self.utilprotowriter = rootwriter.new_writer()
...@@ -379,6 +386,8 @@ class GlobalState(object): ...@@ -379,6 +386,8 @@ class GlobalState(object):
writer.insert(self.utilprotowriter) writer.insert(self.utilprotowriter)
def put_utility_code_defs(self, writer): def put_utility_code_defs(self, writer):
if self.emit_linenums:
writer.write('\n#line 1 "cython_utility"\n')
writer.insert(self.utildefwriter) writer.insert(self.utildefwriter)
...@@ -416,19 +425,21 @@ class CCodeWriter(object): ...@@ -416,19 +425,21 @@ class CCodeWriter(object):
# generation (labels and temps state etc.) # generation (labels and temps state etc.)
# globalstate GlobalState contains state global for a C file (input file info, # globalstate GlobalState contains state global for a C file (input file info,
# utility code, declared constants etc.) # utility code, declared constants etc.)
# emit_linenums boolean whether or not to write #line pragmas
def __init__(self, create_from=None, buffer=None, copy_formatting=False): def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None):
if buffer is None: buffer = StringIOTree() if buffer is None: buffer = StringIOTree()
self.buffer = buffer self.buffer = buffer
self.marker = None self.marker = None
self.last_marker_line = 0 self.last_marker_line = 0
self.source_desc = ""
self.funcstate = None self.funcstate = None
self.level = 0 self.level = 0
self.bol = 1 self.bol = 1
if create_from is None: if create_from is None:
# Root CCodeWriter # Root CCodeWriter
self.globalstate = GlobalState(self) self.globalstate = GlobalState(self, emit_linenums=emit_linenums)
self.globalstate.initwriters(self) self.globalstate.initwriters(self)
# ^^^ need seperate step because this will reference self.globalstate # ^^^ need seperate step because this will reference self.globalstate
else: else:
...@@ -438,6 +449,10 @@ class CCodeWriter(object): ...@@ -438,6 +449,10 @@ class CCodeWriter(object):
if copy_formatting: if copy_formatting:
self.level = create_from.level self.level = create_from.level
self.bol = create_from.bol self.bol = create_from.bol
if emit_linenums is None:
self.emit_linenums = self.globalstate.emit_linenums
else:
self.emit_linenums = emit_linenums
def create_new(self, create_from, buffer, copy_formatting): def create_new(self, create_from, buffer, copy_formatting):
# polymorphic constructor -- very slightly more versatile # polymorphic constructor -- very slightly more versatile
...@@ -505,6 +520,8 @@ class CCodeWriter(object): ...@@ -505,6 +520,8 @@ class CCodeWriter(object):
def putln(self, code = ""): def putln(self, code = ""):
if self.marker and self.bol: if self.marker and self.bol:
self.emit_marker() self.emit_marker()
if self.emit_linenums and self.last_marker_line != 0:
self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc))
if code: if code:
self.put(code) self.put(code)
self.write("\n"); self.write("\n");
...@@ -581,7 +598,8 @@ class CCodeWriter(object): ...@@ -581,7 +598,8 @@ class CCodeWriter(object):
marker = u'"%s":%d\n%s\n' % ( marker = u'"%s":%d\n%s\n' % (
source_desc.get_escaped_description(), line, u'\n'.join(lines)) source_desc.get_escaped_description(), line, u'\n'.join(lines))
self.marker = (line, marker) self.marker = (line, marker)
if self.emit_linenums:
self.source_desc = source_desc.get_escaped_description()
def put_label(self, lbl): def put_label(self, lbl):
if lbl in self.funcstate.labels_used: if lbl in self.funcstate.labels_used:
......
...@@ -103,11 +103,6 @@ class ExprNode(Node): ...@@ -103,11 +103,6 @@ class ExprNode(Node):
# - Call release_temp on sub-nodes and release any other # - Call release_temp on sub-nodes and release any other
# temps used during assignment. # temps used during assignment.
# #
# calculate_result_code
# - Called during the Allocate Temps phase. Should return a
# C code fragment evaluating to the result. This is only
# called when the result is not a temporary.
#
# target_code # target_code
# Called by the default implementation of allocate_target_temps. # Called by the default implementation of allocate_target_temps.
# Should return a C lvalue for assigning to the node. The default # Should return a C lvalue for assigning to the node. The default
...@@ -140,13 +135,18 @@ class ExprNode(Node): ...@@ -140,13 +135,18 @@ class ExprNode(Node):
# on all sub-expressions. # on all sub-expressions.
# #
# A default implementation of generate_evaluation_code # A default implementation of generate_evaluation_code
# is provided which uses the following abstract method: # is provided which uses the following abstract methods:
# #
# generate_result_code # generate_result_code
# - Generate any C statements necessary to calculate # - Generate any C statements necessary to calculate
# the result of this node from the results of its # the result of this node from the results of its
# sub-expressions. # sub-expressions.
# #
# calculate_result_code
# - Should return a C code fragment evaluating to the
# result. This is only called when the result is not
# a temporary.
#
# generate_assignment_code # generate_assignment_code
# Called on the LHS of an assignment. # Called on the LHS of an assignment.
# - Call generate_evaluation_code for sub-expressions. # - Call generate_evaluation_code for sub-expressions.
...@@ -205,9 +205,15 @@ class ExprNode(Node): ...@@ -205,9 +205,15 @@ class ExprNode(Node):
self.saved_subexpr_nodes = nodes self.saved_subexpr_nodes = nodes
return self.saved_subexpr_nodes return self.saved_subexpr_nodes
def result(self):
if self.is_temp:
return self.result_code
else:
return self.calculate_result_code()
def result_as(self, type = None): def result_as(self, type = None):
# Return the result code cast to the specified C type. # Return the result code cast to the specified C type.
return typecast(type, self.ctype(), self.result_code) return typecast(type, self.ctype(), self.result())
def py_result(self): def py_result(self):
# Return the result code cast to PyObject *. # Return the result code cast to PyObject *.
...@@ -381,8 +387,6 @@ class ExprNode(Node): ...@@ -381,8 +387,6 @@ class ExprNode(Node):
self.result_code = None self.result_code = None
if debug_temp_alloc: if debug_temp_alloc:
print("%s Allocated result %s" % (self, self.result_code)) print("%s Allocated result %s" % (self, self.result_code))
else:
self.result_code = self.calculate_result_code()
def target_code(self): def target_code(self):
# Return code fragment for use as LHS of a C assignment. # Return code fragment for use as LHS of a C assignment.
...@@ -418,7 +422,7 @@ class ExprNode(Node): ...@@ -418,7 +422,7 @@ class ExprNode(Node):
# If result is a pyobject, make sure we own # If result is a pyobject, make sure we own
# a reference to it. # a reference to it.
if self.type.is_pyobject and not self.result_in_temp(): if self.type.is_pyobject and not self.result_in_temp():
code.put_incref(self.result_code, self.ctype()) code.put_incref(self.result(), self.ctype())
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
code.mark_pos(self.pos) code.mark_pos(self.pos)
...@@ -442,7 +446,7 @@ class ExprNode(Node): ...@@ -442,7 +446,7 @@ class ExprNode(Node):
# temporary Python reference. # temporary Python reference.
if self.is_temp: if self.is_temp:
if self.type.is_pyobject: if self.type.is_pyobject:
code.put_decref_clear(self.result_code, self.ctype()) code.put_decref_clear(self.result(), self.ctype())
else: else:
self.generate_subexpr_disposal_code(code) self.generate_subexpr_disposal_code(code)
...@@ -458,7 +462,7 @@ class ExprNode(Node): ...@@ -458,7 +462,7 @@ class ExprNode(Node):
# the result if it is a Python object. # the result if it is a Python object.
if self.is_temp: if self.is_temp:
if self.type.is_pyobject: if self.type.is_pyobject:
code.putln("%s = 0;" % self.result_code) code.putln("%s = 0;" % self.result())
else: else:
self.generate_subexpr_disposal_code(code) self.generate_subexpr_disposal_code(code)
...@@ -795,9 +799,9 @@ class LongNode(AtomicExprNode): ...@@ -795,9 +799,9 @@ class LongNode(AtomicExprNode):
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
code.putln( code.putln(
'%s = PyLong_FromString("%s", 0, 0); %s' % ( '%s = PyLong_FromString("%s", 0, 0); %s' % (
self.result_code, self.result(),
self.value, self.value,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class ImagNode(AtomicExprNode): class ImagNode(AtomicExprNode):
...@@ -818,9 +822,9 @@ class ImagNode(AtomicExprNode): ...@@ -818,9 +822,9 @@ class ImagNode(AtomicExprNode):
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
code.putln( code.putln(
"%s = PyComplex_FromDoubles(0.0, %s); %s" % ( "%s = PyComplex_FromDoubles(0.0, %s); %s" % (
self.result_code, self.result(),
self.value, self.value,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class NameNode(AtomicExprNode): class NameNode(AtomicExprNode):
...@@ -1017,10 +1021,10 @@ class NameNode(AtomicExprNode): ...@@ -1017,10 +1021,10 @@ class NameNode(AtomicExprNode):
namespace = entry.scope.namespace_cname namespace = entry.scope.namespace_cname
code.putln( code.putln(
'%s = __Pyx_GetName(%s, %s); %s' % ( '%s = __Pyx_GetName(%s, %s); %s' % (
self.result_code, self.result(),
namespace, namespace,
self.interned_cname, self.interned_cname,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
elif entry.is_local and False: elif entry.is_local and False:
# control flow not good enough yet # control flow not good enough yet
assigned = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos) assigned = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos)
...@@ -1083,12 +1087,12 @@ class NameNode(AtomicExprNode): ...@@ -1083,12 +1087,12 @@ class NameNode(AtomicExprNode):
if entry.is_local and not Options.init_local_none: if entry.is_local and not Options.init_local_none:
initalized = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos) initalized = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos)
if initalized is True: if initalized is True:
code.put_decref(self.result_code, self.ctype()) code.put_decref(self.result(), self.ctype())
elif initalized is None: elif initalized is None:
code.put_xdecref(self.result_code, self.ctype()) code.put_xdecref(self.result(), self.ctype())
else: else:
code.put_decref(self.result_code, self.ctype()) code.put_decref(self.result(), self.ctype())
code.putln('%s = %s;' % (self.result_code, rhs.result_as(self.ctype()))) code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype())))
if debug_disposal_code: if debug_disposal_code:
print("NameNode.generate_assignment_code:") print("NameNode.generate_assignment_code:")
print("...generating post-assignment code for %s" % rhs) print("...generating post-assignment code for %s" % rhs)
...@@ -1101,7 +1105,7 @@ class NameNode(AtomicExprNode): ...@@ -1101,7 +1105,7 @@ class NameNode(AtomicExprNode):
code.putln('%s = %s;' % (rhstmp, rhs.result_as(self.ctype()))) code.putln('%s = %s;' % (rhstmp, rhs.result_as(self.ctype())))
import Buffer import Buffer
Buffer.put_assign_to_buffer(self.result_code, rhstmp, buffer_aux, self.entry.type, Buffer.put_assign_to_buffer(self.result(), rhstmp, buffer_aux, self.entry.type,
is_initialized=not self.skip_assignment_decref, is_initialized=not self.skip_assignment_decref,
pos=self.pos, code=code) pos=self.pos, code=code)
code.putln("%s = 0;" % rhstmp) code.putln("%s = 0;" % rhstmp)
...@@ -1145,9 +1149,9 @@ class BackquoteNode(ExprNode): ...@@ -1145,9 +1149,9 @@ class BackquoteNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln( code.putln(
"%s = PyObject_Repr(%s); %s" % ( "%s = PyObject_Repr(%s); %s" % (
self.result_code, self.result(),
self.arg.py_result(), self.arg.py_result(),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class ImportNode(ExprNode): class ImportNode(ExprNode):
...@@ -1179,10 +1183,10 @@ class ImportNode(ExprNode): ...@@ -1179,10 +1183,10 @@ class ImportNode(ExprNode):
name_list_code = "0" name_list_code = "0"
code.putln( code.putln(
"%s = __Pyx_Import(%s, %s); %s" % ( "%s = __Pyx_Import(%s, %s); %s" % (
self.result_code, self.result(),
self.module_name.py_result(), self.module_name.py_result(),
name_list_code, name_list_code,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class IteratorNode(ExprNode): class IteratorNode(ExprNode):
...@@ -1206,7 +1210,7 @@ class IteratorNode(ExprNode): ...@@ -1206,7 +1210,7 @@ class IteratorNode(ExprNode):
gil_message = "Iterating over Python object" gil_message = "Iterating over Python object"
def release_temp(self, env): def release_temp(self, env):
env.release_temp(self.result_code) env.release_temp(self.result())
self.counter.release_temp(env) self.counter.release_temp(env)
def generate_result_code(self, code): def generate_result_code(self, code):
...@@ -1216,16 +1220,16 @@ class IteratorNode(ExprNode): ...@@ -1216,16 +1220,16 @@ class IteratorNode(ExprNode):
self.sequence.py_result())) self.sequence.py_result()))
code.putln( code.putln(
"%s = 0; %s = %s; Py_INCREF(%s);" % ( "%s = 0; %s = %s; Py_INCREF(%s);" % (
self.counter.result_code, self.counter.result(),
self.result_code, self.result(),
self.sequence.py_result(), self.sequence.py_result(),
self.result_code)) self.result()))
code.putln("} else {") code.putln("} else {")
code.putln("%s = -1; %s = PyObject_GetIter(%s); %s" % ( code.putln("%s = -1; %s = PyObject_GetIter(%s); %s" % (
self.counter.result_code, self.counter.result(),
self.result_code, self.result(),
self.sequence.py_result(), self.sequence.py_result(),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.putln("}") code.putln("}")
...@@ -1249,26 +1253,26 @@ class NextNode(AtomicExprNode): ...@@ -1249,26 +1253,26 @@ class NextNode(AtomicExprNode):
"if (likely(Py%s_CheckExact(%s))) {" % (py_type, self.iterator.py_result())) "if (likely(Py%s_CheckExact(%s))) {" % (py_type, self.iterator.py_result()))
code.putln( code.putln(
"if (%s >= Py%s_GET_SIZE(%s)) break;" % ( "if (%s >= Py%s_GET_SIZE(%s)) break;" % (
self.iterator.counter.result_code, self.iterator.counter.result(),
py_type, py_type,
self.iterator.py_result())) self.iterator.py_result()))
code.putln( code.putln(
"%s = Py%s_GET_ITEM(%s, %s); Py_INCREF(%s); %s++;" % ( "%s = Py%s_GET_ITEM(%s, %s); Py_INCREF(%s); %s++;" % (
self.result_code, self.result(),
py_type, py_type,
self.iterator.py_result(), self.iterator.py_result(),
self.iterator.counter.result_code, self.iterator.counter.result(),
self.result_code, self.result(),
self.iterator.counter.result_code)) self.iterator.counter.result()))
code.put("} else ") code.put("} else ")
code.putln("{") code.putln("{")
code.putln( code.putln(
"%s = PyIter_Next(%s);" % ( "%s = PyIter_Next(%s);" % (
self.result_code, self.result(),
self.iterator.py_result())) self.iterator.py_result()))
code.putln( code.putln(
"if (!%s) {" % "if (!%s) {" %
self.result_code) self.result())
code.putln(code.error_goto_if_PyErr(self.pos)) code.putln(code.error_goto_if_PyErr(self.pos))
code.putln("break;") code.putln("break;")
code.putln("}") code.putln("}")
...@@ -1466,7 +1470,7 @@ class IndexNode(ExprNode): ...@@ -1466,7 +1470,7 @@ class IndexNode(ExprNode):
return "<not used>" return "<not used>"
else: else:
return "(%s[%s])" % ( return "(%s[%s])" % (
self.base.result_code, self.index.result_code) self.base.result(), self.index.result())
def index_unsigned_parameter(self): def index_unsigned_parameter(self):
if self.index.type.is_int: if self.index.type.is_int:
...@@ -1495,35 +1499,36 @@ class IndexNode(ExprNode): ...@@ -1495,35 +1499,36 @@ class IndexNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
if self.is_buffer_access: if self.is_buffer_access:
# buffer_pointer_code is returned by result()
ptrcode = self.buffer_lookup_code(code) ptrcode = self.buffer_lookup_code(code)
code.putln("%s = *%s;" % ( code.putln("%s = *%s;" % (
self.result_code, self.result(),
self.buffer_type.buffer_ptr_type.cast_code(ptrcode))) self.buffer_type.buffer_ptr_type.cast_code(ptrcode)))
# Must incref the value we pulled out. # Must incref the value we pulled out.
if self.buffer_type.dtype.is_pyobject: if self.buffer_type.dtype.is_pyobject:
code.putln("Py_INCREF((PyObject*)%s);" % self.result_code) code.putln("Py_INCREF((PyObject*)%s);" % self.result())
elif self.type.is_pyobject: elif self.type.is_pyobject:
if self.index.type.is_int: if self.index.type.is_int:
function = "__Pyx_GetItemInt" function = "__Pyx_GetItemInt"
index_code = self.index.result_code index_code = self.index.result()
else: else:
function = "PyObject_GetItem" function = "PyObject_GetItem"
index_code = self.index.py_result() index_code = self.index.py_result()
sign_code = "" sign_code = ""
code.putln( code.putln(
"%s = %s(%s, %s%s); if (!%s) %s" % ( "%s = %s(%s, %s%s); if (!%s) %s" % (
self.result_code, self.result(),
function, function,
self.base.py_result(), self.base.py_result(),
index_code, index_code,
self.index_unsigned_parameter(), self.index_unsigned_parameter(),
self.result_code, self.result(),
code.error_goto(self.pos))) code.error_goto(self.pos)))
def generate_setitem_code(self, value_code, code): def generate_setitem_code(self, value_code, code):
if self.index.type.is_int: if self.index.type.is_int:
function = "__Pyx_SetItemInt" function = "__Pyx_SetItemInt"
index_code = self.index.result_code index_code = self.index.result()
else: else:
function = "PyObject_SetItem" function = "PyObject_SetItem"
index_code = self.index.py_result() index_code = self.index.py_result()
...@@ -1546,7 +1551,7 @@ class IndexNode(ExprNode): ...@@ -1546,7 +1551,7 @@ class IndexNode(ExprNode):
if rhs.is_temp: if rhs.is_temp:
rhs_code = code.funcstate.allocate_temp(rhs.type) rhs_code = code.funcstate.allocate_temp(rhs.type)
else: else:
rhs_code = rhs.result_code rhs_code = rhs.result()
code.putln("%s = %s;" % (ptr, ptrexpr)) code.putln("%s = %s;" % (ptr, ptrexpr))
code.putln("Py_DECREF(*%s); Py_INCREF(%s);" % ( code.putln("Py_DECREF(*%s); Py_INCREF(%s);" % (
ptr, rhs_code ptr, rhs_code
...@@ -1557,7 +1562,7 @@ class IndexNode(ExprNode): ...@@ -1557,7 +1562,7 @@ class IndexNode(ExprNode):
code.funcstate.release_temp(ptr) code.funcstate.release_temp(ptr)
else: else:
# Simple case # Simple case
code.putln("*%s %s= %s;" % (ptrexpr, op, rhs.result_code)) code.putln("*%s %s= %s;" % (ptrexpr, op, rhs.result()))
def generate_assignment_code(self, rhs, code): def generate_assignment_code(self, rhs, code):
self.generate_subexpr_evaluation_code(code) self.generate_subexpr_evaluation_code(code)
...@@ -1568,7 +1573,7 @@ class IndexNode(ExprNode): ...@@ -1568,7 +1573,7 @@ class IndexNode(ExprNode):
else: else:
code.putln( code.putln(
"%s = %s;" % ( "%s = %s;" % (
self.result_code, rhs.result_code)) self.result(), rhs.result()))
self.generate_subexpr_disposal_code(code) self.generate_subexpr_disposal_code(code)
rhs.generate_disposal_code(code) rhs.generate_disposal_code(code)
...@@ -1577,7 +1582,7 @@ class IndexNode(ExprNode): ...@@ -1577,7 +1582,7 @@ class IndexNode(ExprNode):
#if self.type.is_pyobject: #if self.type.is_pyobject:
if self.index.type.is_int: if self.index.type.is_int:
function = "PySequence_DelItem" function = "PySequence_DelItem"
index_code = self.index.result_code index_code = self.index.result()
else: else:
function = "PyObject_DelItem" function = "PyObject_DelItem"
index_code = self.index.py_result() index_code = self.index.py_result()
...@@ -1593,16 +1598,15 @@ class IndexNode(ExprNode): ...@@ -1593,16 +1598,15 @@ class IndexNode(ExprNode):
# Assign indices to temps # Assign indices to temps
index_temps = [code.funcstate.allocate_temp(i.type) for i in self.indices] index_temps = [code.funcstate.allocate_temp(i.type) for i in self.indices]
for temp, index in zip(index_temps, self.indices): for temp, index in zip(index_temps, self.indices):
code.putln("%s = %s;" % (temp, index.result_code)) code.putln("%s = %s;" % (temp, index.result()))
# Generate buffer access code using these temps # Generate buffer access code using these temps
import Buffer import Buffer
assert self.options is not None
# The above could happen because child_attrs is wrong somewhere so that # The above could happen because child_attrs is wrong somewhere so that
# options are not propagated. # options are not propagated.
return Buffer.put_buffer_lookup_code(entry=self.base.entry, return Buffer.put_buffer_lookup_code(entry=self.base.entry,
index_signeds=[i.type.signed for i in self.indices], index_signeds=[i.type.signed for i in self.indices],
index_cnames=index_temps, index_cnames=index_temps,
options=self.options, options=code.globalstate.directives,
pos=self.pos, code=code) pos=self.pos, code=code)
class SliceIndexNode(ExprNode): class SliceIndexNode(ExprNode):
...@@ -1647,11 +1651,11 @@ class SliceIndexNode(ExprNode): ...@@ -1647,11 +1651,11 @@ class SliceIndexNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln( code.putln(
"%s = PySequence_GetSlice(%s, %s, %s); %s" % ( "%s = PySequence_GetSlice(%s, %s, %s); %s" % (
self.result_code, self.result(),
self.base.py_result(), self.base.py_result(),
self.start_code(), self.start_code(),
self.stop_code(), self.stop_code(),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
def generate_assignment_code(self, rhs, code): def generate_assignment_code(self, rhs, code):
self.generate_subexpr_evaluation_code(code) self.generate_subexpr_evaluation_code(code)
...@@ -1660,7 +1664,7 @@ class SliceIndexNode(ExprNode): ...@@ -1660,7 +1664,7 @@ class SliceIndexNode(ExprNode):
self.base.py_result(), self.base.py_result(),
self.start_code(), self.start_code(),
self.stop_code(), self.stop_code(),
rhs.result_code)) rhs.result()))
self.generate_subexpr_disposal_code(code) self.generate_subexpr_disposal_code(code)
rhs.generate_disposal_code(code) rhs.generate_disposal_code(code)
...@@ -1675,18 +1679,18 @@ class SliceIndexNode(ExprNode): ...@@ -1675,18 +1679,18 @@ class SliceIndexNode(ExprNode):
def start_code(self): def start_code(self):
if self.start: if self.start:
return self.start.result_code return self.start.result()
else: else:
return "0" return "0"
def stop_code(self): def stop_code(self):
if self.stop: if self.stop:
return self.stop.result_code return self.stop.result()
else: else:
return "PY_SSIZE_T_MAX" return "PY_SSIZE_T_MAX"
def calculate_result_code(self): def calculate_result_code(self):
# self.result_code is not used, but this method must exist # self.result() is not used, but this method must exist
return "<unused>" return "<unused>"
...@@ -1724,11 +1728,11 @@ class SliceNode(ExprNode): ...@@ -1724,11 +1728,11 @@ class SliceNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln( code.putln(
"%s = PySlice_New(%s, %s, %s); %s" % ( "%s = PySlice_New(%s, %s, %s); %s" % (
self.result_code, self.result(),
self.start.py_result(), self.start.py_result(),
self.stop.py_result(), self.stop.py_result(),
self.step.py_result(), self.step.py_result(),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class CallNode(ExprNode): class CallNode(ExprNode):
...@@ -1888,6 +1892,9 @@ class SimpleCallNode(CallNode): ...@@ -1888,6 +1892,9 @@ class SimpleCallNode(CallNode):
arg_code = actual_arg.result_as(formal_arg.type) arg_code = actual_arg.result_as(formal_arg.type)
arg_list_code.append(arg_code) arg_list_code.append(arg_code)
if func_type.is_overridable:
arg_list_code.append(str(int(self.wrapper_call or self.function.entry.is_unbound_cmethod)))
if func_type.optional_arg_count: if func_type.optional_arg_count:
if expected_nargs == actual_nargs: if expected_nargs == actual_nargs:
optional_args = 'NULL' optional_args = 'NULL'
...@@ -1896,12 +1903,12 @@ class SimpleCallNode(CallNode): ...@@ -1896,12 +1903,12 @@ class SimpleCallNode(CallNode):
arg_list_code.append(optional_args) arg_list_code.append(optional_args)
for actual_arg in self.args[len(formal_args):]: for actual_arg in self.args[len(formal_args):]:
arg_list_code.append(actual_arg.result_code) arg_list_code.append(actual_arg.result())
result = "%s(%s)" % (self.function.result_code, result = "%s(%s)" % (self.function.result(),
join(arg_list_code, ", ")) join(arg_list_code, ", "))
if self.wrapper_call or \ # if self.wrapper_call or \
self.function.entry.is_unbound_cmethod and self.function.entry.type.is_overridable: # self.function.entry.is_unbound_cmethod and self.function.entry.type.is_overridable:
result = "(%s = 1, %s)" % (Naming.skip_dispatch_cname, result) # result = "(%s = 1, %s)" % (Naming.skip_dispatch_cname, result)
return result return result
def generate_result_code(self, code): def generate_result_code(self, code):
...@@ -1910,10 +1917,10 @@ class SimpleCallNode(CallNode): ...@@ -1910,10 +1917,10 @@ class SimpleCallNode(CallNode):
arg_code = self.arg_tuple.py_result() arg_code = self.arg_tuple.py_result()
code.putln( code.putln(
"%s = PyObject_Call(%s, %s, NULL); %s" % ( "%s = PyObject_Call(%s, %s, NULL); %s" % (
self.result_code, self.result(),
self.function.py_result(), self.function.py_result(),
arg_code, arg_code,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
elif func_type.is_cfunction: elif func_type.is_cfunction:
if self.has_optional_args: if self.has_optional_args:
actual_nargs = len(self.args) actual_nargs = len(self.args)
...@@ -1930,18 +1937,18 @@ class SimpleCallNode(CallNode): ...@@ -1930,18 +1937,18 @@ class SimpleCallNode(CallNode):
actual_arg.result_as(formal_arg.type))) actual_arg.result_as(formal_arg.type)))
exc_checks = [] exc_checks = []
if self.type.is_pyobject: if self.type.is_pyobject:
exc_checks.append("!%s" % self.result_code) exc_checks.append("!%s" % self.result())
else: else:
exc_val = func_type.exception_value exc_val = func_type.exception_value
exc_check = func_type.exception_check exc_check = func_type.exception_check
if exc_val is not None: if exc_val is not None:
exc_checks.append("%s == %s" % (self.result_code, exc_val)) exc_checks.append("%s == %s" % (self.result(), exc_val))
if exc_check: if exc_check:
exc_checks.append("PyErr_Occurred()") exc_checks.append("PyErr_Occurred()")
if self.is_temp or exc_checks: if self.is_temp or exc_checks:
rhs = self.c_call_code() rhs = self.c_call_code()
if self.result_code: if self.result():
lhs = "%s = " % self.result_code lhs = "%s = " % self.result()
if self.is_temp and self.type.is_pyobject: if self.is_temp and self.type.is_pyobject:
#return_type = self.type # func_type.return_type #return_type = self.type # func_type.return_type
#print "SimpleCallNode.generate_result_code: casting", rhs, \ #print "SimpleCallNode.generate_result_code: casting", rhs, \
...@@ -2032,9 +2039,9 @@ class GeneralCallNode(CallNode): ...@@ -2032,9 +2039,9 @@ class GeneralCallNode(CallNode):
keyword_code) keyword_code)
code.putln( code.putln(
"%s = %s; %s" % ( "%s = %s; %s" % (
self.result_code, self.result(),
call_code, call_code,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class AsTupleNode(ExprNode): class AsTupleNode(ExprNode):
...@@ -2064,9 +2071,9 @@ class AsTupleNode(ExprNode): ...@@ -2064,9 +2071,9 @@ class AsTupleNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln( code.putln(
"%s = PySequence_Tuple(%s); %s" % ( "%s = PySequence_Tuple(%s); %s" % (
self.result_code, self.result(),
self.arg.py_result(), self.arg.py_result(),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class AttributeNode(ExprNode): class AttributeNode(ExprNode):
...@@ -2074,6 +2081,8 @@ class AttributeNode(ExprNode): ...@@ -2074,6 +2081,8 @@ class AttributeNode(ExprNode):
# #
# obj ExprNode # obj ExprNode
# attribute string # attribute string
# needs_none_check boolean Used if obj is an extension type.
# If set to True, it is known that the type is not None.
# #
# Used internally: # Used internally:
# #
...@@ -2087,9 +2096,9 @@ class AttributeNode(ExprNode): ...@@ -2087,9 +2096,9 @@ class AttributeNode(ExprNode):
subexprs = ['obj'] subexprs = ['obj']
type = PyrexTypes.error_type type = PyrexTypes.error_type
result = "<error>"
entry = None entry = None
is_called = 0 is_called = 0
needs_none_check = True
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
# If coercing to a generic pyobject and this is a cpdef function # If coercing to a generic pyobject and this is a cpdef function
...@@ -2295,7 +2304,7 @@ class AttributeNode(ExprNode): ...@@ -2295,7 +2304,7 @@ class AttributeNode(ExprNode):
def calculate_result_code(self): def calculate_result_code(self):
#print "AttributeNode.calculate_result_code:", self.member ### #print "AttributeNode.calculate_result_code:", self.member ###
#print "...obj node =", self.obj, "code", self.obj.result_code ### #print "...obj node =", self.obj, "code", self.obj.result() ###
#print "...obj type", self.obj.type, "ctype", self.obj.ctype() ### #print "...obj type", self.obj.type, "ctype", self.obj.ctype() ###
obj = self.obj obj = self.obj
obj_code = obj.result_as(obj.type) obj_code = obj.result_as(obj.type)
...@@ -2314,10 +2323,17 @@ class AttributeNode(ExprNode): ...@@ -2314,10 +2323,17 @@ class AttributeNode(ExprNode):
if self.is_py_attr: if self.is_py_attr:
code.putln( code.putln(
'%s = PyObject_GetAttr(%s, %s); %s' % ( '%s = PyObject_GetAttr(%s, %s); %s' % (
self.result_code, self.result(),
self.obj.py_result(), self.obj.py_result(),
self.interned_attr_cname, self.interned_attr_cname,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
else:
# result_code contains what is needed, but we may need to insert
# a check and raise an exception
if (self.obj.type.is_extension_type
and self.needs_none_check
and code.globalstate.directives['nonecheck']):
self.put_nonecheck(code)
def generate_assignment_code(self, rhs, code): def generate_assignment_code(self, rhs, code):
self.obj.generate_evaluation_code(code) self.obj.generate_evaluation_code(code)
...@@ -2329,7 +2345,12 @@ class AttributeNode(ExprNode): ...@@ -2329,7 +2345,12 @@ class AttributeNode(ExprNode):
rhs.py_result())) rhs.py_result()))
rhs.generate_disposal_code(code) rhs.generate_disposal_code(code)
else: else:
select_code = self.result_code if (self.obj.type.is_extension_type
and self.needs_none_check
and code.globalstate.directives['nonecheck']):
self.put_nonecheck(code)
select_code = self.result()
if self.type.is_pyobject: if self.type.is_pyobject:
rhs.make_owned_reference(code) rhs.make_owned_reference(code)
code.put_decref(select_code, self.ctype()) code.put_decref(select_code, self.ctype())
...@@ -2337,7 +2358,7 @@ class AttributeNode(ExprNode): ...@@ -2337,7 +2358,7 @@ class AttributeNode(ExprNode):
"%s = %s;" % ( "%s = %s;" % (
select_code, select_code,
rhs.result_as(self.ctype()))) rhs.result_as(self.ctype())))
#rhs.result_code)) #rhs.result()))
rhs.generate_post_assignment_code(code) rhs.generate_post_assignment_code(code)
self.obj.generate_disposal_code(code) self.obj.generate_disposal_code(code)
...@@ -2358,6 +2379,14 @@ class AttributeNode(ExprNode): ...@@ -2358,6 +2379,14 @@ class AttributeNode(ExprNode):
else: else:
code.annotate(self.pos, AnnotationItem('c_attr', 'c attribute', size=len(self.attribute))) code.annotate(self.pos, AnnotationItem('c_attr', 'c attribute', size=len(self.attribute)))
def put_nonecheck(self, code):
code.globalstate.use_utility_code(raise_noneattr_error_utility_code)
code.putln("if (%s) {" % code.unlikely("%s == Py_None") % self.obj.result_as(PyrexTypes.py_object_type))
code.putln("__Pyx_RaiseNoneAttributeError(\"%s\");" % self.attribute.encode("UTF-8")) # todo: fix encoding
code.putln(code.error_goto(self.pos))
code.putln("}")
#------------------------------------------------------------------- #-------------------------------------------------------------------
# #
# Constructor nodes # Constructor nodes
...@@ -2439,9 +2468,9 @@ class SequenceNode(ExprNode): ...@@ -2439,9 +2468,9 @@ class SequenceNode(ExprNode):
item = self.unpacked_items[i] item = self.unpacked_items[i]
code.putln( code.putln(
"%s = PyTuple_GET_ITEM(tuple, %s);" % ( "%s = PyTuple_GET_ITEM(tuple, %s);" % (
item.result_code, item.result(),
i)) i))
code.put_incref(item.result_code, item.ctype()) code.put_incref(item.result(), item.ctype())
value_node = self.coerced_unpacked_items[i] value_node = self.coerced_unpacked_items[i]
value_node.generate_evaluation_code(code) value_node.generate_evaluation_code(code)
self.args[i].generate_assignment_code(value_node, code) self.args[i].generate_assignment_code(value_node, code)
...@@ -2452,9 +2481,9 @@ class SequenceNode(ExprNode): ...@@ -2452,9 +2481,9 @@ class SequenceNode(ExprNode):
code.putln( code.putln(
"%s = PyObject_GetIter(%s); %s" % ( "%s = PyObject_GetIter(%s); %s" % (
self.iterator.result_code, self.iterator.result(),
rhs.py_result(), rhs.py_result(),
code.error_goto_if_null(self.iterator.result_code, self.pos))) code.error_goto_if_null(self.iterator.result(), self.pos)))
rhs.generate_disposal_code(code) rhs.generate_disposal_code(code)
for i in range(len(self.args)): for i in range(len(self.args)):
item = self.unpacked_items[i] item = self.unpacked_items[i]
...@@ -2462,9 +2491,9 @@ class SequenceNode(ExprNode): ...@@ -2462,9 +2491,9 @@ class SequenceNode(ExprNode):
self.iterator.py_result(), i) self.iterator.py_result(), i)
code.putln( code.putln(
"%s = %s; %s" % ( "%s = %s; %s" % (
item.result_code, item.result(),
typecast(item.ctype(), py_object_type, unpack_code), typecast(item.ctype(), py_object_type, unpack_code),
code.error_goto_if_null(item.result_code, self.pos))) code.error_goto_if_null(item.result(), self.pos)))
value_node = self.coerced_unpacked_items[i] value_node = self.coerced_unpacked_items[i]
value_node.generate_evaluation_code(code) value_node.generate_evaluation_code(code)
self.args[i].generate_assignment_code(value_node, code) self.args[i].generate_assignment_code(value_node, code)
...@@ -2520,16 +2549,16 @@ class TupleNode(SequenceNode): ...@@ -2520,16 +2549,16 @@ class TupleNode(SequenceNode):
return return
code.putln( code.putln(
"%s = PyTuple_New(%s); %s" % ( "%s = PyTuple_New(%s); %s" % (
self.result_code, self.result(),
len(self.args), len(self.args),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
for i in range(len(self.args)): for i in range(len(self.args)):
arg = self.args[i] arg = self.args[i]
if not arg.result_in_temp(): if not arg.result_in_temp():
code.put_incref(arg.result_code, arg.ctype()) code.put_incref(arg.result(), arg.ctype())
code.putln( code.putln(
"PyTuple_SET_ITEM(%s, %s, %s);" % ( "PyTuple_SET_ITEM(%s, %s, %s);" % (
self.result_code, self.result(),
i, i,
arg.py_result())) arg.py_result()))
...@@ -2555,16 +2584,16 @@ class ListNode(SequenceNode): ...@@ -2555,16 +2584,16 @@ class ListNode(SequenceNode):
def generate_operation_code(self, code): def generate_operation_code(self, code):
code.putln("%s = PyList_New(%s); %s" % code.putln("%s = PyList_New(%s); %s" %
(self.result_code, (self.result(),
len(self.args), len(self.args),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
for i in range(len(self.args)): for i in range(len(self.args)):
arg = self.args[i] arg = self.args[i]
#if not arg.is_temp: #if not arg.is_temp:
if not arg.result_in_temp(): if not arg.result_in_temp():
code.put_incref(arg.result_code, arg.ctype()) code.put_incref(arg.result(), arg.ctype())
code.putln("PyList_SET_ITEM(%s, %s, %s);" % code.putln("PyList_SET_ITEM(%s, %s, %s);" %
(self.result_code, (self.result(),
i, i,
arg.py_result())) arg.py_result()))
...@@ -2597,9 +2626,9 @@ class ListComprehensionNode(SequenceNode): ...@@ -2597,9 +2626,9 @@ class ListComprehensionNode(SequenceNode):
def generate_operation_code(self, code): def generate_operation_code(self, code):
code.putln("%s = PyList_New(%s); %s" % code.putln("%s = PyList_New(%s); %s" %
(self.result_code, (self.result(),
0, 0,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
self.loop.generate_execution_code(code) self.loop.generate_execution_code(code)
def annotate(self, code): def annotate(self, code):
...@@ -2621,10 +2650,10 @@ class ListComprehensionAppendNode(ExprNode): ...@@ -2621,10 +2650,10 @@ class ListComprehensionAppendNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln("%s = PyList_Append(%s, (PyObject*)%s); %s" % code.putln("%s = PyList_Append(%s, (PyObject*)%s); %s" %
(self.result_code, (self.result(),
self.target.result_code, self.target.result(),
self.expr.result_code, self.expr.result(),
code.error_goto_if(self.result_code, self.pos))) code.error_goto_if(self.result(), self.pos)))
class DictNode(ExprNode): class DictNode(ExprNode):
...@@ -2666,13 +2695,13 @@ class DictNode(ExprNode): ...@@ -2666,13 +2695,13 @@ class DictNode(ExprNode):
# pairs are evaluated and used one at a time. # pairs are evaluated and used one at a time.
code.putln( code.putln(
"%s = PyDict_New(); %s" % ( "%s = PyDict_New(); %s" % (
self.result_code, self.result(),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
for item in self.key_value_pairs: for item in self.key_value_pairs:
item.generate_evaluation_code(code) item.generate_evaluation_code(code)
code.put_error_if_neg(self.pos, code.put_error_if_neg(self.pos,
"PyDict_SetItem(%s, %s, %s)" % ( "PyDict_SetItem(%s, %s, %s)" % (
self.result_code, self.result(),
item.key.py_result(), item.key.py_result(),
item.value.py_result())) item.value.py_result()))
item.generate_disposal_code(code) item.generate_disposal_code(code)
...@@ -2739,12 +2768,12 @@ class ClassNode(ExprNode): ...@@ -2739,12 +2768,12 @@ class ClassNode(ExprNode):
self.doc.py_result())) self.doc.py_result()))
code.putln( code.putln(
'%s = __Pyx_CreateClass(%s, %s, %s, "%s"); %s' % ( '%s = __Pyx_CreateClass(%s, %s, %s, "%s"); %s' % (
self.result_code, self.result(),
self.bases.py_result(), self.bases.py_result(),
self.dict.py_result(), self.dict.py_result(),
self.cname, self.cname,
self.module_name, self.module_name,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class UnboundMethodNode(ExprNode): class UnboundMethodNode(ExprNode):
...@@ -2768,10 +2797,10 @@ class UnboundMethodNode(ExprNode): ...@@ -2768,10 +2797,10 @@ class UnboundMethodNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln( code.putln(
"%s = PyMethod_New(%s, 0, %s); %s" % ( "%s = PyMethod_New(%s, 0, %s); %s" % (
self.result_code, self.result(),
self.function.py_result(), self.function.py_result(),
self.class_cname, self.class_cname,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class PyCFunctionNode(AtomicExprNode): class PyCFunctionNode(AtomicExprNode):
...@@ -2791,9 +2820,9 @@ class PyCFunctionNode(AtomicExprNode): ...@@ -2791,9 +2820,9 @@ class PyCFunctionNode(AtomicExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln( code.putln(
"%s = PyCFunction_New(&%s, 0); %s" % ( "%s = PyCFunction_New(&%s, 0); %s" % (
self.result_code, self.result(),
self.pymethdef_cname, self.pymethdef_cname,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
#------------------------------------------------------------------- #-------------------------------------------------------------------
# #
...@@ -2864,10 +2893,10 @@ class UnopNode(ExprNode): ...@@ -2864,10 +2893,10 @@ class UnopNode(ExprNode):
function = self.py_operation_function() function = self.py_operation_function()
code.putln( code.putln(
"%s = %s(%s); %s" % ( "%s = %s(%s); %s" % (
self.result_code, self.result(),
function, function,
self.operand.py_result(), self.operand.py_result(),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
def type_error(self): def type_error(self):
if not self.operand.type.is_error: if not self.operand.type.is_error:
...@@ -2896,7 +2925,7 @@ class NotNode(ExprNode): ...@@ -2896,7 +2925,7 @@ class NotNode(ExprNode):
self.type = PyrexTypes.c_bint_type self.type = PyrexTypes.c_bint_type
def calculate_result_code(self): def calculate_result_code(self):
return "(!%s)" % self.operand.result_code return "(!%s)" % self.operand.result()
def generate_result_code(self, code): def generate_result_code(self, code):
pass pass
...@@ -2914,7 +2943,7 @@ class UnaryPlusNode(UnopNode): ...@@ -2914,7 +2943,7 @@ class UnaryPlusNode(UnopNode):
return "PyNumber_Positive" return "PyNumber_Positive"
def calculate_result_code(self): def calculate_result_code(self):
return self.operand.result_code return self.operand.result()
class UnaryMinusNode(UnopNode): class UnaryMinusNode(UnopNode):
...@@ -2932,7 +2961,7 @@ class UnaryMinusNode(UnopNode): ...@@ -2932,7 +2961,7 @@ class UnaryMinusNode(UnopNode):
return "PyNumber_Negative" return "PyNumber_Negative"
def calculate_result_code(self): def calculate_result_code(self):
return "(-%s)" % self.operand.result_code return "(-%s)" % self.operand.result()
class TildeNode(UnopNode): class TildeNode(UnopNode):
...@@ -2948,7 +2977,7 @@ class TildeNode(UnopNode): ...@@ -2948,7 +2977,7 @@ class TildeNode(UnopNode):
return "PyNumber_Invert" return "PyNumber_Invert"
def calculate_result_code(self): def calculate_result_code(self):
return "(~%s)" % self.operand.result_code return "(~%s)" % self.operand.result()
class AmpersandNode(ExprNode): class AmpersandNode(ExprNode):
...@@ -2978,7 +3007,7 @@ class AmpersandNode(ExprNode): ...@@ -2978,7 +3007,7 @@ class AmpersandNode(ExprNode):
self.result_code = "<error>" self.result_code = "<error>"
def calculate_result_code(self): def calculate_result_code(self):
return "(&%s)" % self.operand.result_code return "(&%s)" % self.operand.result()
def generate_result_code(self, code): def generate_result_code(self, code):
pass pass
...@@ -3049,8 +3078,7 @@ class TypecastNode(ExprNode): ...@@ -3049,8 +3078,7 @@ class TypecastNode(ExprNode):
def calculate_result_code(self): def calculate_result_code(self):
opnd = self.operand opnd = self.operand
result_code = self.type.cast_code(opnd.result_code) return self.type.cast_code(opnd.result())
return result_code
def result_as(self, type): def result_as(self, type):
if self.type.is_pyobject and not self.is_temp: if self.type.is_pyobject and not self.is_temp:
...@@ -3063,9 +3091,9 @@ class TypecastNode(ExprNode): ...@@ -3063,9 +3091,9 @@ class TypecastNode(ExprNode):
if self.is_temp: if self.is_temp:
code.putln( code.putln(
"%s = (PyObject *)%s;" % ( "%s = (PyObject *)%s;" % (
self.result_code, self.result(),
self.operand.result_code)) self.operand.result()))
code.put_incref(self.result_code, self.ctype()) code.put_incref(self.result(), self.ctype())
class SizeofNode(ExprNode): class SizeofNode(ExprNode):
...@@ -3134,7 +3162,7 @@ class SizeofVarNode(SizeofNode): ...@@ -3134,7 +3162,7 @@ class SizeofVarNode(SizeofNode):
self.type = PyrexTypes.c_int_type self.type = PyrexTypes.c_int_type
def calculate_result_code(self): def calculate_result_code(self):
return "(sizeof(%s))" % self.operand.result_code return "(sizeof(%s))" % self.operand.result()
def generate_result_code(self, code): def generate_result_code(self, code):
pass pass
...@@ -3242,12 +3270,12 @@ class BinopNode(ExprNode): ...@@ -3242,12 +3270,12 @@ class BinopNode(ExprNode):
extra_args = "" extra_args = ""
code.putln( code.putln(
"%s = %s(%s, %s%s); %s" % ( "%s = %s(%s, %s%s); %s" % (
self.result_code, self.result(),
function, function,
self.operand1.py_result(), self.operand1.py_result(),
self.operand2.py_result(), self.operand2.py_result(),
extra_args, extra_args,
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
else: else:
if self.is_temp: if self.is_temp:
self.generate_c_operation_code(code) self.generate_c_operation_code(code)
...@@ -3288,9 +3316,9 @@ class NumBinopNode(BinopNode): ...@@ -3288,9 +3316,9 @@ class NumBinopNode(BinopNode):
def calculate_result_code(self): def calculate_result_code(self):
return "(%s %s %s)" % ( return "(%s %s %s)" % (
self.operand1.result_code, self.operand1.result(),
self.operator, self.operator,
self.operand2.result_code) self.operand2.result())
def py_operation_function(self): def py_operation_function(self):
return self.py_functions[self.operator] return self.py_functions[self.operator]
...@@ -3372,9 +3400,9 @@ class FloorDivNode(NumBinopNode): ...@@ -3372,9 +3400,9 @@ class FloorDivNode(NumBinopNode):
def calculate_result_code(self): def calculate_result_code(self):
return "(%s %s %s)" % ( return "(%s %s %s)" % (
self.operand1.result_code, self.operand1.result(),
"/", # c division is by default floor-div "/", # c division is by default floor-div
self.operand2.result_code) self.operand2.result())
class ModNode(IntBinopNode): class ModNode(IntBinopNode):
...@@ -3414,7 +3442,7 @@ class PowNode(NumBinopNode): ...@@ -3414,7 +3442,7 @@ class PowNode(NumBinopNode):
def calculate_result_code(self): def calculate_result_code(self):
return "pow(%s, %s)" % ( return "pow(%s, %s)" % (
self.operand1.result_code, self.operand2.result_code) self.operand1.result(), self.operand2.result())
class BoolBinopNode(ExprNode): class BoolBinopNode(ExprNode):
...@@ -3468,11 +3496,11 @@ class BoolBinopNode(ExprNode): ...@@ -3468,11 +3496,11 @@ class BoolBinopNode(ExprNode):
# assignments and increfs/decrefs that would otherwise # assignments and increfs/decrefs that would otherwise
# be necessary. # be necessary.
self.allocate_temp(env, result_code) self.allocate_temp(env, result_code)
self.operand1.allocate_temps(env, self.result_code) self.operand1.allocate_temps(env, self.result())
if self.temp_bool: if self.temp_bool:
self.temp_bool.allocate_temp(env) self.temp_bool.allocate_temp(env)
self.temp_bool.release_temp(env) self.temp_bool.release_temp(env)
self.operand2.allocate_temps(env, self.result_code) self.operand2.allocate_temps(env, self.result())
# We haven't called release_temp on either operand, # We haven't called release_temp on either operand,
# because although they are temp nodes, they don't own # because although they are temp nodes, they don't own
# their result variable. And because they are temp # their result variable. And because they are temp
...@@ -3487,9 +3515,9 @@ class BoolBinopNode(ExprNode): ...@@ -3487,9 +3515,9 @@ class BoolBinopNode(ExprNode):
def calculate_result_code(self): def calculate_result_code(self):
return "(%s %s %s)" % ( return "(%s %s %s)" % (
self.operand1.result_code, self.operand1.result(),
self.py_to_c_op[self.operator], self.py_to_c_op[self.operator],
self.operand2.result_code) self.operand2.result())
py_to_c_op = {'and': "&&", 'or': "||"} py_to_c_op = {'and': "&&", 'or': "||"}
...@@ -3512,14 +3540,14 @@ class BoolBinopNode(ExprNode): ...@@ -3512,14 +3540,14 @@ class BoolBinopNode(ExprNode):
def generate_operand1_test(self, code): def generate_operand1_test(self, code):
# Generate code to test the truth of the first operand. # Generate code to test the truth of the first operand.
if self.type.is_pyobject: if self.type.is_pyobject:
test_result = self.temp_bool.result_code test_result = self.temp_bool.result()
code.putln( code.putln(
"%s = __Pyx_PyObject_IsTrue(%s); %s" % ( "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
test_result, test_result,
self.operand1.py_result(), self.operand1.py_result(),
code.error_goto_if_neg(test_result, self.pos))) code.error_goto_if_neg(test_result, self.pos)))
else: else:
test_result = self.operand1.result_code test_result = self.operand1.result()
return test_result return test_result
...@@ -3562,8 +3590,8 @@ class CondExprNode(ExprNode): ...@@ -3562,8 +3590,8 @@ class CondExprNode(ExprNode):
# be necessary. # be necessary.
self.allocate_temp(env, result_code) self.allocate_temp(env, result_code)
self.test.allocate_temps(env, result_code) self.test.allocate_temps(env, result_code)
self.true_val.allocate_temps(env, self.result_code) self.true_val.allocate_temps(env, self.result())
self.false_val.allocate_temps(env, self.result_code) self.false_val.allocate_temps(env, self.result())
# We haven't called release_temp on either value, # We haven't called release_temp on either value,
# because although they are temp nodes, they don't own # because although they are temp nodes, they don't own
# their result variable. And because they are temp # their result variable. And because they are temp
...@@ -3603,7 +3631,7 @@ class CondExprNode(ExprNode): ...@@ -3603,7 +3631,7 @@ class CondExprNode(ExprNode):
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
self.test.generate_evaluation_code(code) self.test.generate_evaluation_code(code)
code.putln("if (%s) {" % self.test.result_code ) code.putln("if (%s) {" % self.test.result() )
self.true_val.generate_evaluation_code(code) self.true_val.generate_evaluation_code(code)
code.putln("} else {") code.putln("} else {")
self.false_val.generate_evaluation_code(code) self.false_val.generate_evaluation_code(code)
...@@ -3820,19 +3848,19 @@ class PrimaryCmpNode(ExprNode, CmpNode): ...@@ -3820,19 +3848,19 @@ class PrimaryCmpNode(ExprNode, CmpNode):
def calculate_result_code(self): def calculate_result_code(self):
return "(%s %s %s)" % ( return "(%s %s %s)" % (
self.operand1.result_code, self.operand1.result(),
self.c_operator(self.operator), self.c_operator(self.operator),
self.operand2.result_code) self.operand2.result())
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
self.operand1.generate_evaluation_code(code) self.operand1.generate_evaluation_code(code)
self.operand2.generate_evaluation_code(code) self.operand2.generate_evaluation_code(code)
if self.is_temp: if self.is_temp:
self.generate_operation_code(code, self.result_code, self.generate_operation_code(code, self.result(),
self.operand1, self.operator, self.operand2) self.operand1, self.operator, self.operand2)
if self.cascade: if self.cascade:
self.cascade.generate_evaluation_code(code, self.cascade.generate_evaluation_code(code,
self.result_code, self.operand2) self.result(), self.operand2)
self.operand1.generate_disposal_code(code) self.operand1.generate_disposal_code(code)
self.operand2.generate_disposal_code(code) self.operand2.generate_disposal_code(code)
...@@ -3972,7 +4000,6 @@ class CoercionNode(ExprNode): ...@@ -3972,7 +4000,6 @@ class CoercionNode(ExprNode):
def __init__(self, arg): def __init__(self, arg):
self.pos = arg.pos self.pos = arg.pos
self.arg = arg self.arg = arg
self.options = arg.options
if debug_coercion: if debug_coercion:
print("%s Coercing %s" % (self, self.arg)) print("%s Coercing %s" % (self, self.arg))
...@@ -4024,7 +4051,7 @@ class PyTypeTestNode(CoercionNode): ...@@ -4024,7 +4051,7 @@ class PyTypeTestNode(CoercionNode):
return self.arg.is_ephemeral() return self.arg.is_ephemeral()
def calculate_result_code(self): def calculate_result_code(self):
return self.arg.result_code return self.arg.result()
def generate_result_code(self, code): def generate_result_code(self, code):
if self.type.typeobj_is_available(): if self.type.typeobj_is_available():
...@@ -4062,10 +4089,10 @@ class CoerceToPyTypeNode(CoercionNode): ...@@ -4062,10 +4089,10 @@ class CoerceToPyTypeNode(CoercionNode):
def generate_result_code(self, code): def generate_result_code(self, code):
function = self.arg.type.to_py_function function = self.arg.type.to_py_function
code.putln('%s = %s(%s); %s' % ( code.putln('%s = %s(%s); %s' % (
self.result_code, self.result(),
function, function,
self.arg.result_code, self.arg.result(),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result(), self.pos)))
class CoerceFromPyTypeNode(CoercionNode): class CoerceFromPyTypeNode(CoercionNode):
...@@ -4094,9 +4121,9 @@ class CoerceFromPyTypeNode(CoercionNode): ...@@ -4094,9 +4121,9 @@ class CoerceFromPyTypeNode(CoercionNode):
if self.type.is_enum: if self.type.is_enum:
rhs = typecast(self.type, c_long_type, rhs) rhs = typecast(self.type, c_long_type, rhs)
code.putln('%s = %s; %s' % ( code.putln('%s = %s; %s' % (
self.result_code, self.result(),
rhs, rhs,
code.error_goto_if(self.type.error_condition(self.result_code), self.pos))) code.error_goto_if(self.type.error_condition(self.result()), self.pos)))
class CoerceToBooleanNode(CoercionNode): class CoerceToBooleanNode(CoercionNode):
...@@ -4119,15 +4146,15 @@ class CoerceToBooleanNode(CoercionNode): ...@@ -4119,15 +4146,15 @@ class CoerceToBooleanNode(CoercionNode):
self.arg.check_const() self.arg.check_const()
def calculate_result_code(self): def calculate_result_code(self):
return "(%s != 0)" % self.arg.result_code return "(%s != 0)" % self.arg.result()
def generate_result_code(self, code): def generate_result_code(self, code):
if self.arg.type.is_pyobject: if self.arg.type.is_pyobject:
code.putln( code.putln(
"%s = __Pyx_PyObject_IsTrue(%s); %s" % ( "%s = __Pyx_PyObject_IsTrue(%s); %s" % (
self.result_code, self.result(),
self.arg.py_result(), self.arg.py_result(),
code.error_goto_if_neg(self.result_code, self.pos))) code.error_goto_if_neg(self.result(), self.pos)))
class CoerceToTempNode(CoercionNode): class CoerceToTempNode(CoercionNode):
...@@ -4153,9 +4180,9 @@ class CoerceToTempNode(CoercionNode): ...@@ -4153,9 +4180,9 @@ class CoerceToTempNode(CoercionNode):
#self.arg.generate_evaluation_code(code) # Already done #self.arg.generate_evaluation_code(code) # Already done
# by generic generate_subexpr_evaluation_code! # by generic generate_subexpr_evaluation_code!
code.putln("%s = %s;" % ( code.putln("%s = %s;" % (
self.result_code, self.arg.result_as(self.ctype()))) self.result(), self.arg.result_as(self.ctype())))
if self.type.is_pyobject: if self.type.is_pyobject:
code.put_incref(self.result_code, self.ctype()) code.put_incref(self.result(), self.ctype())
class CloneNode(CoercionNode): class CloneNode(CoercionNode):
...@@ -4177,7 +4204,7 @@ class CloneNode(CoercionNode): ...@@ -4177,7 +4204,7 @@ class CloneNode(CoercionNode):
self.entry = arg.entry self.entry = arg.entry
def calculate_result_code(self): def calculate_result_code(self):
return self.arg.result_code return self.arg.result()
def analyse_types(self, env): def analyse_types(self, env):
self.type = self.arg.type self.type = self.arg.type
...@@ -4226,22 +4253,22 @@ class PersistentNode(ExprNode): ...@@ -4226,22 +4253,22 @@ class PersistentNode(ExprNode):
self.analyse_counter += 1 self.analyse_counter += 1
def calculate_result_code(self): def calculate_result_code(self):
return self.result_code return self.result()
def generate_evaluation_code(self, code): def generate_evaluation_code(self, code):
if self.generate_counter == 0: if self.generate_counter == 0:
self.arg.generate_evaluation_code(code) self.arg.generate_evaluation_code(code)
code.putln("%s = %s;" % ( code.putln("%s = %s;" % (
self.result_code, self.arg.result_as(self.ctype()))) self.result(), self.arg.result_as(self.ctype())))
if self.type.is_pyobject: if self.type.is_pyobject:
code.put_incref(self.result_code, self.ctype()) code.put_incref(self.result(), self.ctype())
self.arg.generate_disposal_code(code) self.arg.generate_disposal_code(code)
self.generate_counter += 1 self.generate_counter += 1
def generate_disposal_code(self, code): def generate_disposal_code(self, code):
if self.generate_counter == self.uses: if self.generate_counter == self.uses:
if self.type.is_pyobject: if self.type.is_pyobject:
code.put_decref_clear(self.result_code, self.ctype()) code.put_decref_clear(self.result(), self.ctype())
def allocate_temps(self, env, result=None): def allocate_temps(self, env, result=None):
if self.temp_counter == 0: if self.temp_counter == 0:
...@@ -4258,7 +4285,7 @@ class PersistentNode(ExprNode): ...@@ -4258,7 +4285,7 @@ class PersistentNode(ExprNode):
def release_temp(self, env): def release_temp(self, env):
if self.temp_counter == self.uses: if self.temp_counter == self.uses:
env.release_temp(self.result_code) env.release_temp(self.result())
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
# #
...@@ -4552,3 +4579,13 @@ static INLINE int __Pyx_SetItemInt(PyObject *o, Py_ssize_t i, PyObject *v, int i ...@@ -4552,3 +4579,13 @@ static INLINE int __Pyx_SetItemInt(PyObject *o, Py_ssize_t i, PyObject *v, int i
""", """,
""" """
"""] """]
#------------------------------------------------------------------------------------
raise_noneattr_error_utility_code = [
"""
static INLINE void __Pyx_RaiseNoneAttributeError(char* attrname);
""", """
static INLINE void __Pyx_RaiseNoneAttributeError(char* attrname) {
PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", attrname);
}
"""]
...@@ -79,8 +79,9 @@ class Context: ...@@ -79,8 +79,9 @@ class Context:
from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform
from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
from ParseTreeTransforms import ResolveOptions from ParseTreeTransforms import InterpretCompilerDirectives
from Optimize import FlattenInListTransform, SwitchTransform, OptimizeRefcounting from AutoDocTransforms import EmbedSignature
from Optimize import FlattenInListTransform, SwitchTransform, FinalOptimizePhase
from Buffer import IntroduceBufferAuxiliaryVars from Buffer import IntroduceBufferAuxiliaryVars
from ModuleNode import check_c_classes from ModuleNode import check_c_classes
...@@ -95,7 +96,8 @@ class Context: ...@@ -95,7 +96,8 @@ class Context:
NormalizeTree(self), NormalizeTree(self),
PostParse(self), PostParse(self),
_specific_post_parse, _specific_post_parse,
ResolveOptions(self, self.pragma_overrides), InterpretCompilerDirectives(self, self.pragma_overrides),
EmbedSignature(self),
FlattenInListTransform(), FlattenInListTransform(),
WithTransform(self), WithTransform(self),
DecoratorTransform(self), DecoratorTransform(self),
...@@ -104,7 +106,7 @@ class Context: ...@@ -104,7 +106,7 @@ class Context:
_check_c_classes, _check_c_classes,
AnalyseExpressionsTransform(self), AnalyseExpressionsTransform(self),
SwitchTransform(), SwitchTransform(),
OptimizeRefcounting(self), FinalOptimizePhase(self),
# SpecialFunctions(self), # SpecialFunctions(self),
# CreateClosureClasses(context), # CreateClosureClasses(context),
] ]
...@@ -727,7 +729,8 @@ default_options = dict( ...@@ -727,7 +729,8 @@ default_options = dict(
timestamps = None, timestamps = None,
verbose = 0, verbose = 0,
quiet = 0, quiet = 0,
pragma_overrides = {} pragma_overrides = {},
emit_linenums = False,
) )
if sys.platform == "mac": if sys.platform == "mac":
from Cython.Mac.MacSystem import c_compile, c_link, CCompilerError from Cython.Mac.MacSystem import c_compile, c_link, CCompilerError
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
# #
import os, time import os, time
from cStringIO import StringIO
from PyrexTypes import CPtrType from PyrexTypes import CPtrType
import Future import Future
...@@ -41,8 +40,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -41,8 +40,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# #
# scope The module scope. # scope The module scope.
# compilation_source A CompilationSource (see Main) # compilation_source A CompilationSource (see Main)
# directives Top-level compiler directives
child_attrs = ["body"] child_attrs = ["body"]
directives = None
def analyse_declarations(self, env): def analyse_declarations(self, env):
if Options.embed_pos_in_docstring: if Options.embed_pos_in_docstring:
...@@ -52,6 +53,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -52,6 +53,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
env.doc.encoding = self.doc.encoding env.doc.encoding = self.doc.encoding
else: else:
env.doc = self.doc env.doc = self.doc
env.directives = self.directives
self.body.analyse_declarations(env) self.body.analyse_declarations(env)
def process_implementation(self, options, result): def process_implementation(self, options, result):
...@@ -240,11 +242,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -240,11 +242,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if Options.annotate or options.annotate: if Options.annotate or options.annotate:
code = Annotate.AnnotationCCodeWriter() code = Annotate.AnnotationCCodeWriter()
else: else:
code = Code.CCodeWriter() code = Code.CCodeWriter(emit_linenums=options.emit_linenums)
h_code = code.insertion_point() h_code = code.insertion_point()
self.generate_module_preamble(env, modules, h_code) self.generate_module_preamble(env, modules, h_code)
code.globalstate.module_pos = self.pos code.globalstate.module_pos = self.pos
code.globalstate.directives = self.directives
code.putln("") code.putln("")
code.putln("/* Implementation of %s */" % env.qualified_name) code.putln("/* Implementation of %s */" % env.qualified_name)
...@@ -872,7 +875,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -872,7 +875,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
else: else:
code.put_init_var_to_py_none(entry, "p->%s") code.put_init_var_to_py_none(entry, "p->%s")
entry = scope.lookup_here("__new__") entry = scope.lookup_here("__new__")
if entry: if entry and entry.is_special:
if entry.trivial_signature: if entry.trivial_signature:
cinit_args = "o, %s, NULL" % Naming.empty_tuple cinit_args = "o, %s, NULL" % Naming.empty_tuple
else: else:
...@@ -1958,11 +1961,11 @@ builtin_module_name_utility_code = [ ...@@ -1958,11 +1961,11 @@ builtin_module_name_utility_code = [
import_module_utility_code = [ import_module_utility_code = [
""" """
static PyObject *__Pyx_ImportModule(char *name); /*proto*/ static PyObject *__Pyx_ImportModule(const char *name); /*proto*/
""",""" ""","""
#ifndef __PYX_HAVE_RT_ImportModule #ifndef __PYX_HAVE_RT_ImportModule
#define __PYX_HAVE_RT_ImportModule #define __PYX_HAVE_RT_ImportModule
static PyObject *__Pyx_ImportModule(char *name) { static PyObject *__Pyx_ImportModule(const char *name) {
PyObject *py_name = 0; PyObject *py_name = 0;
PyObject *py_module = 0; PyObject *py_module = 0;
...@@ -1987,29 +1990,32 @@ bad: ...@@ -1987,29 +1990,32 @@ bad:
type_import_utility_code = [ type_import_utility_code = [
""" """
static PyTypeObject *__Pyx_ImportType(char *module_name, char *class_name, long size); /*proto*/ static PyTypeObject *__Pyx_ImportType(const char *module_name, const char *class_name, long size); /*proto*/
""",""" ""","""
#ifndef __PYX_HAVE_RT_ImportType #ifndef __PYX_HAVE_RT_ImportType
#define __PYX_HAVE_RT_ImportType #define __PYX_HAVE_RT_ImportType
static PyTypeObject *__Pyx_ImportType(char *module_name, char *class_name, static PyTypeObject *__Pyx_ImportType(const char *module_name, const char *class_name,
long size) long size)
{ {
PyObject *py_module = 0; PyObject *py_module = 0;
PyObject *result = 0; PyObject *result = 0;
PyObject *py_name = 0; PyObject *py_name = 0;
py_module = __Pyx_ImportModule(module_name);
if (!py_module)
goto bad;
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
py_name = PyString_FromString(module_name); py_name = PyString_FromString(class_name);
#else #else
py_name = PyUnicode_FromString(module_name); py_name = PyUnicode_FromString(class_name);
#endif #endif
if (!py_name) if (!py_name)
goto bad; goto bad;
result = PyObject_GetAttr(py_module, py_name);
py_module = __Pyx_ImportModule(module_name); Py_DECREF(py_name);
if (!py_module) py_name = 0;
goto bad; Py_DECREF(py_module);
result = PyObject_GetAttrString(py_module, class_name); py_module = 0;
if (!result) if (!result)
goto bad; goto bad;
if (!PyType_Check(result)) { if (!PyType_Check(result)) {
...@@ -2026,7 +2032,7 @@ static PyTypeObject *__Pyx_ImportType(char *module_name, char *class_name, ...@@ -2026,7 +2032,7 @@ static PyTypeObject *__Pyx_ImportType(char *module_name, char *class_name,
} }
return (PyTypeObject *)result; return (PyTypeObject *)result;
bad: bad:
Py_XDECREF(py_name); Py_XDECREF(py_module);
Py_XDECREF(result); Py_XDECREF(result);
return 0; return 0;
} }
......
...@@ -76,7 +76,6 @@ print_function_kwargs = pyrex_prefix + "print_kwargs" ...@@ -76,7 +76,6 @@ print_function_kwargs = pyrex_prefix + "print_kwargs"
cleanup_cname = pyrex_prefix + "module_cleanup" cleanup_cname = pyrex_prefix + "module_cleanup"
pymoduledef_cname = pyrex_prefix + "moduledef" pymoduledef_cname = pyrex_prefix + "moduledef"
optional_args_cname = pyrex_prefix + "optional_args" optional_args_cname = pyrex_prefix + "optional_args"
no_opt_args = pyrex_prefix + "no_opt_args"
import_star = pyrex_prefix + "import_star" import_star = pyrex_prefix + "import_star"
import_star_set = pyrex_prefix + "import_star_set" import_star_set = pyrex_prefix + "import_star_set"
cur_scope_cname = pyrex_prefix + "cur_scope" cur_scope_cname = pyrex_prefix + "cur_scope"
......
...@@ -71,12 +71,10 @@ class Node(object): ...@@ -71,12 +71,10 @@ class Node(object):
# pos (string, int, int) Source file position # pos (string, int, int) Source file position
# is_name boolean Is a NameNode # is_name boolean Is a NameNode
# is_literal boolean Is a ConstNode # is_literal boolean Is a ConstNode
# options dict Compiler directives in effect for this node
is_name = 0 is_name = 0
is_literal = 0 is_literal = 0
temps = None temps = None
options = None
# All descandants should set child_attrs to a list of the attributes # All descandants should set child_attrs to a list of the attributes
# containing nodes considered "children" in the tree. Each such attribute # containing nodes considered "children" in the tree. Each such attribute
...@@ -205,6 +203,53 @@ class Node(object): ...@@ -205,6 +203,53 @@ class Node(object):
res += "%s>" % indent res += "%s>" % indent
return res return res
class CompilerDirectivesNode(Node):
"""
Sets compiler directives for the children nodes
"""
# directives {string:value} A dictionary holding the right value for
# *all* possible directives.
# body Node
child_attrs = ["body"]
def analyse_control_flow(self, env):
old = env.directives
env.directives = self.directives
self.body.analyse_control_flow(env)
env.directives = old
def analyse_declarations(self, env):
old = env.directives
env.directives = self.directives
self.body.analyse_declarations(env)
env.directives = old
def analyse_expressions(self, env):
old = env.directives
env.directives = self.directives
self.body.analyse_expressions(env)
env.directives = old
def generate_function_definitions(self, env, code):
env_old = env.directives
code_old = code.globalstate.directives
code.globalstate.directives = self.directives
self.body.generate_function_definitions(env, code)
env.directives = env_old
code.globalstate.directives = code_old
def generate_execution_code(self, code):
old = code.globalstate.directives
code.globalstate.directives = self.directives
self.body.generate_execution_code(code)
code.globalstate.directives = old
def annotate(self, code):
old = code.globalstate.directives
code.globalstate.directives = self.directives
self.body.annotate(code)
code.globalstate.directives = old
class BlockNode: class BlockNode:
# Mixin class for nodes representing a declaration block. # Mixin class for nodes representing a declaration block.
...@@ -394,7 +439,7 @@ class CArrayDeclaratorNode(CDeclaratorNode): ...@@ -394,7 +439,7 @@ class CArrayDeclaratorNode(CDeclaratorNode):
self.dimension.analyse_const_expression(env) self.dimension.analyse_const_expression(env)
if not self.dimension.type.is_int: if not self.dimension.type.is_int:
error(self.dimension.pos, "Array dimension not integer") error(self.dimension.pos, "Array dimension not integer")
size = self.dimension.result_code size = self.dimension.result()
else: else:
size = None size = None
if not base_type.is_complete(): if not base_type.is_complete():
...@@ -484,7 +529,7 @@ class CFuncDeclaratorNode(CDeclaratorNode): ...@@ -484,7 +529,7 @@ class CFuncDeclaratorNode(CDeclaratorNode):
"Exception value must be a Python exception or cdef function with no arguments.") "Exception value must be a Python exception or cdef function with no arguments.")
exc_val = self.exception_value exc_val = self.exception_value
else: else:
exc_val = self.exception_value.result_code exc_val = self.exception_value.result()
if not return_type.assignable_from(self.exception_value.type): if not return_type.assignable_from(self.exception_value.type):
error(self.exception_value.pos, error(self.exception_value.pos,
"Exception value incompatible with function return type") "Exception value incompatible with function return type")
...@@ -770,7 +815,7 @@ class CEnumDefItemNode(StatNode): ...@@ -770,7 +815,7 @@ class CEnumDefItemNode(StatNode):
if not self.value.type.is_int: if not self.value.type.is_int:
self.value = self.value.coerce_to(PyrexTypes.c_int_type, env) self.value = self.value.coerce_to(PyrexTypes.c_int_type, env)
self.value.analyse_const_expression(env) self.value.analyse_const_expression(env)
value = self.value.result_code value = self.value.result()
else: else:
value = self.name value = self.name
entry = env.declare_const(self.name, enum_entry.type, entry = env.declare_const(self.name, enum_entry.type,
...@@ -1027,7 +1072,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1027,7 +1072,7 @@ class FuncDefNode(StatNode, BlockNode):
code.exit_cfunc_scope() code.exit_cfunc_scope()
if self.py_func: if self.py_func:
self.py_func.generate_function_definitions(env, code) self.py_func.generate_function_definitions(env, code)
self.generate_optarg_wrapper_function(env, code) self.generate_wrapper_functions(code)
def declare_argument(self, env, arg): def declare_argument(self, env, arg):
if arg.type.is_void: if arg.type.is_void:
...@@ -1036,7 +1081,8 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1036,7 +1081,8 @@ class FuncDefNode(StatNode, BlockNode):
error(arg.pos, error(arg.pos,
"Argument type '%s' is incomplete" % arg.type) "Argument type '%s' is incomplete" % arg.type)
return env.declare_arg(arg.name, arg.type, arg.pos) return env.declare_arg(arg.name, arg.type, arg.pos)
def generate_optarg_wrapper_function(self, env, code):
def generate_wrapper_functions(self, code):
pass pass
def generate_execution_code(self, code): def generate_execution_code(self, code):
...@@ -1054,7 +1100,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1054,7 +1100,7 @@ class FuncDefNode(StatNode, BlockNode):
if default.is_temp and default.type.is_pyobject: if default.is_temp and default.type.is_pyobject:
code.putln( code.putln(
"%s = 0;" % "%s = 0;" %
default.result_code) default.result())
# For Python class methods, create and store function object # For Python class methods, create and store function object
if self.assmt: if self.assmt:
self.assmt.generate_execution_code(code) self.assmt.generate_execution_code(code)
...@@ -1093,6 +1139,7 @@ class CFuncDefNode(FuncDefNode): ...@@ -1093,6 +1139,7 @@ class CFuncDefNode(FuncDefNode):
# with_gil boolean Acquire GIL around body # with_gil boolean Acquire GIL around body
# type CFuncType # type CFuncType
# py_func wrapper for calling from Python # py_func wrapper for calling from Python
# overridable whether or not this is a cpdef function
child_attrs = ["base_type", "declarator", "body", "py_func"] child_attrs = ["base_type", "declarator", "body", "py_func"]
...@@ -1188,21 +1235,22 @@ class CFuncDefNode(FuncDefNode): ...@@ -1188,21 +1235,22 @@ class CFuncDefNode(FuncDefNode):
if self.overridable: if self.overridable:
self.py_func.analyse_expressions(env) self.py_func.analyse_expressions(env)
def generate_function_header(self, code, with_pymethdef, with_opt_args = 1): def generate_function_header(self, code, with_pymethdef, with_opt_args = 1, with_dispatch = 1, cname = None):
arg_decls = [] arg_decls = []
type = self.type type = self.type
visibility = self.entry.visibility visibility = self.entry.visibility
for arg in type.args[:len(type.args)-type.optional_arg_count]: for arg in type.args[:len(type.args)-type.optional_arg_count]:
arg_decls.append(arg.declaration_code()) arg_decls.append(arg.declaration_code())
if with_dispatch and self.overridable:
arg_decls.append(PyrexTypes.c_int_type.declaration_code(Naming.skip_dispatch_cname))
if type.optional_arg_count and with_opt_args: if type.optional_arg_count and with_opt_args:
arg_decls.append(type.op_arg_struct.declaration_code(Naming.optional_args_cname)) arg_decls.append(type.op_arg_struct.declaration_code(Naming.optional_args_cname))
if type.has_varargs: if type.has_varargs:
arg_decls.append("...") arg_decls.append("...")
if not arg_decls: if not arg_decls:
arg_decls = ["void"] arg_decls = ["void"]
if cname is None:
cname = self.entry.func_cname cname = self.entry.func_cname
if not with_opt_args:
cname += Naming.no_opt_args
entity = type.function_header_code(cname, string.join(arg_decls, ", ")) entity = type.function_header_code(cname, string.join(arg_decls, ", "))
if visibility == 'public': if visibility == 'public':
dll_linkage = "DL_EXPORT" dll_linkage = "DL_EXPORT"
...@@ -1280,15 +1328,33 @@ class CFuncDefNode(FuncDefNode): ...@@ -1280,15 +1328,33 @@ class CFuncDefNode(FuncDefNode):
def caller_will_check_exceptions(self): def caller_will_check_exceptions(self):
return self.entry.type.exception_check return self.entry.type.exception_check
def generate_optarg_wrapper_function(self, env, code): def generate_wrapper_functions(self, code):
if self.type.optional_arg_count and \ # If the C signature of a function has changed, we need to generate
self.type.original_sig and not self.type.original_sig.optional_arg_count: # wrappers to put in the slots here.
k = 0
entry = self.entry
func_type = entry.type
while entry.prev_entry is not None:
k += 1
entry = entry.prev_entry
entry.func_cname = "%s%swrap_%s" % (self.entry.func_cname, Naming.pyrex_prefix, k)
code.putln() code.putln()
self.generate_function_header(code, 0, with_opt_args = 0) self.generate_function_header(code,
0,
with_dispatch = entry.type.is_overridable,
with_opt_args = entry.type.optional_arg_count,
cname = entry.func_cname)
if not self.return_type.is_void: if not self.return_type.is_void:
code.put('return ') code.put('return ')
args = self.type.args args = self.type.args
arglist = [arg.cname for arg in args[:len(args)-self.type.optional_arg_count]] arglist = [arg.cname for arg in args[:len(args)-self.type.optional_arg_count]]
if entry.type.is_overridable:
arglist.append(Naming.skip_dispatch_cname)
elif func_type.is_overridable:
arglist.append('0')
if entry.type.optional_arg_count:
arglist.append(Naming.optional_args_cname)
elif func_type.optional_arg_count:
arglist.append('NULL') arglist.append('NULL')
code.putln('%s(%s);' % (self.entry.func_cname, ', '.join(arglist))) code.putln('%s(%s);' % (self.entry.func_cname, ', '.join(arglist)))
code.putln('}') code.putln('}')
...@@ -2070,22 +2136,22 @@ class OverrideCheckNode(StatNode): ...@@ -2070,22 +2136,22 @@ class OverrideCheckNode(StatNode):
else: else:
self_arg = "((PyObject *)%s)" % self.args[0].cname self_arg = "((PyObject *)%s)" % self.args[0].cname
code.putln("/* Check if called by wrapper */") code.putln("/* Check if called by wrapper */")
code.putln("if (unlikely(%s)) %s = 0;" % (Naming.skip_dispatch_cname, Naming.skip_dispatch_cname)) code.putln("if (unlikely(%s)) ;" % Naming.skip_dispatch_cname)
code.putln("/* Check if overriden in Python */") code.putln("/* Check if overriden in Python */")
if self.py_func.is_module_scope: if self.py_func.is_module_scope:
code.putln("else {") code.putln("else {")
else: else:
code.putln("else if (unlikely(Py_TYPE(%s)->tp_dictoffset != 0)) {" % self_arg) code.putln("else if (unlikely(Py_TYPE(%s)->tp_dictoffset != 0)) {" % self_arg)
err = code.error_goto_if_null(self.func_node.result_code, self.pos) err = code.error_goto_if_null(self.func_node.result(), self.pos)
# need to get attribute manually--scope would return cdef method # need to get attribute manually--scope would return cdef method
code.putln("%s = PyObject_GetAttr(%s, %s); %s" % (self.func_node.result_code, self_arg, self.py_func.interned_attr_cname, err)) code.putln("%s = PyObject_GetAttr(%s, %s); %s" % (self.func_node.result(), self_arg, self.py_func.interned_attr_cname, err))
# It appears that this type is not anywhere exposed in the Python/C API # It appears that this type is not anywhere exposed in the Python/C API
is_builtin_function_or_method = '(strcmp(Py_TYPE(%s)->tp_name, "builtin_function_or_method") == 0)' % self.func_node.result_code is_builtin_function_or_method = '(strcmp(Py_TYPE(%s)->tp_name, "builtin_function_or_method") == 0)' % self.func_node.result()
is_overridden = '(PyCFunction_GET_FUNCTION(%s) != (void *)&%s)' % (self.func_node.result_code, self.py_func.entry.func_cname) is_overridden = '(PyCFunction_GET_FUNCTION(%s) != (void *)&%s)' % (self.func_node.result(), self.py_func.entry.func_cname)
code.putln('if (!%s || %s) {' % (is_builtin_function_or_method, is_overridden)) code.putln('if (!%s || %s) {' % (is_builtin_function_or_method, is_overridden))
self.body.generate_execution_code(code) self.body.generate_execution_code(code)
code.putln('}') code.putln('}')
code.put_decref_clear(self.func_node.result_code, PyrexTypes.py_object_type) code.put_decref_clear(self.func_node.result(), PyrexTypes.py_object_type)
code.putln("}") code.putln("}")
class ClassDefNode(StatNode, BlockNode): class ClassDefNode(StatNode, BlockNode):
...@@ -2142,8 +2208,8 @@ class PyClassDefNode(ClassDefNode): ...@@ -2142,8 +2208,8 @@ class PyClassDefNode(ClassDefNode):
self.classobj.analyse_expressions(env) self.classobj.analyse_expressions(env)
genv = env.global_scope() genv = env.global_scope()
cenv = self.scope cenv = self.scope
cenv.class_dict_cname = self.dict.result_code cenv.class_dict_cname = self.dict.result()
cenv.namespace_cname = cenv.class_obj_cname = self.classobj.result_code cenv.namespace_cname = cenv.class_obj_cname = self.classobj.result()
self.body.analyse_expressions(cenv) self.body.analyse_expressions(cenv)
self.target.analyse_target_expression(env, self.classobj) self.target.analyse_target_expression(env, self.classobj)
self.dict.release_temp(env) self.dict.release_temp(env)
...@@ -2352,8 +2418,8 @@ class ExprStatNode(StatNode): ...@@ -2352,8 +2418,8 @@ class ExprStatNode(StatNode):
def generate_execution_code(self, code): def generate_execution_code(self, code):
self.expr.generate_evaluation_code(code) self.expr.generate_evaluation_code(code)
if not self.expr.is_temp and self.expr.result_code: if not self.expr.is_temp and self.expr.result():
code.putln("%s;" % self.expr.result_code) code.putln("%s;" % self.expr.result())
self.expr.generate_disposal_code(code) self.expr.generate_disposal_code(code)
def annotate(self, code): def annotate(self, code):
...@@ -2607,8 +2673,8 @@ class InPlaceAssignmentNode(AssignmentNode): ...@@ -2607,8 +2673,8 @@ class InPlaceAssignmentNode(AssignmentNode):
elif self.rhs.type.is_pyobject: elif self.rhs.type.is_pyobject:
self.rhs = self.rhs.coerce_to(self.lhs.type, env) self.rhs = self.rhs.coerce_to(self.lhs.type, env)
if self.lhs.type.is_pyobject: if self.lhs.type.is_pyobject:
self.result = ExprNodes.PyTempNode(self.pos, env).coerce_to(self.lhs.type, env) self.result_value = ExprNodes.PyTempNode(self.pos, env).coerce_to(self.lhs.type, env)
self.result.allocate_temps(env) self.result_value.allocate_temps(env)
# if use_temp: # if use_temp:
# self.rhs = self.rhs.coerce_to_temp(env) # self.rhs = self.rhs.coerce_to_temp(env)
self.rhs.allocate_temps(env) self.rhs.allocate_temps(env)
...@@ -2623,7 +2689,7 @@ class InPlaceAssignmentNode(AssignmentNode): ...@@ -2623,7 +2689,7 @@ class InPlaceAssignmentNode(AssignmentNode):
self.dup.release_subexpr_temps(env) self.dup.release_subexpr_temps(env)
# self.rhs.release_temp(env) # self.rhs.release_temp(env)
if self.lhs.type.is_pyobject: if self.lhs.type.is_pyobject:
self.result.release_temp(env) self.result_value.release_temp(env)
def generate_execution_code(self, code): def generate_execution_code(self, code):
self.rhs.generate_evaluation_code(code) self.rhs.generate_evaluation_code(code)
...@@ -2640,16 +2706,16 @@ class InPlaceAssignmentNode(AssignmentNode): ...@@ -2640,16 +2706,16 @@ class InPlaceAssignmentNode(AssignmentNode):
self.dup.generate_result_code(code) self.dup.generate_result_code(code)
code.putln( code.putln(
"%s = %s(%s, %s%s); %s" % ( "%s = %s(%s, %s%s); %s" % (
self.result.result_code, self.result_value.result(),
self.py_operation_function(), self.py_operation_function(),
self.dup.py_result(), self.dup.py_result(),
self.rhs.py_result(), self.rhs.py_result(),
extra, extra,
code.error_goto_if_null(self.result.py_result(), self.pos))) code.error_goto_if_null(self.result_value.py_result(), self.pos)))
self.result.generate_evaluation_code(code) # May be a type check... self.result_value.generate_evaluation_code(code) # May be a type check...
self.rhs.generate_disposal_code(code) self.rhs.generate_disposal_code(code)
self.dup.generate_disposal_code(code) self.dup.generate_disposal_code(code)
self.lhs.generate_assignment_code(self.result, code) self.lhs.generate_assignment_code(self.result_value, code)
else: else:
c_op = self.operator c_op = self.operator
if c_op == "//": if c_op == "//":
...@@ -2664,7 +2730,7 @@ class InPlaceAssignmentNode(AssignmentNode): ...@@ -2664,7 +2730,7 @@ class InPlaceAssignmentNode(AssignmentNode):
self.lhs.generate_buffer_setitem_code(self.rhs, code, c_op) self.lhs.generate_buffer_setitem_code(self.rhs, code, c_op)
else: else:
self.dup.generate_result_code(code) self.dup.generate_result_code(code)
code.putln("%s %s= %s;" % (self.lhs.result_code, c_op, self.rhs.result_code) ) code.putln("%s %s= %s;" % (self.lhs.result(), c_op, self.rhs.result()) )
self.rhs.generate_disposal_code(code) self.rhs.generate_disposal_code(code)
if self.dup.is_temp: if self.dup.is_temp:
self.dup.generate_subexpr_disposal_code(code) self.dup.generate_subexpr_disposal_code(code)
...@@ -2677,14 +2743,12 @@ class InPlaceAssignmentNode(AssignmentNode): ...@@ -2677,14 +2743,12 @@ class InPlaceAssignmentNode(AssignmentNode):
target_lhs = ExprNodes.NameNode(self.dup.pos, target_lhs = ExprNodes.NameNode(self.dup.pos,
name = self.dup.name, name = self.dup.name,
is_temp = self.dup.is_temp, is_temp = self.dup.is_temp,
entry = self.dup.entry, entry = self.dup.entry)
options = self.dup.options)
elif isinstance(self.lhs, ExprNodes.AttributeNode): elif isinstance(self.lhs, ExprNodes.AttributeNode):
target_lhs = ExprNodes.AttributeNode(self.dup.pos, target_lhs = ExprNodes.AttributeNode(self.dup.pos,
obj = ExprNodes.CloneNode(self.lhs.obj), obj = ExprNodes.CloneNode(self.lhs.obj),
attribute = self.dup.attribute, attribute = self.dup.attribute,
is_temp = self.dup.is_temp, is_temp = self.dup.is_temp)
options = self.dup.options)
elif isinstance(self.lhs, ExprNodes.IndexNode): elif isinstance(self.lhs, ExprNodes.IndexNode):
if self.lhs.index: if self.lhs.index:
index = ExprNodes.CloneNode(self.lhs.index) index = ExprNodes.CloneNode(self.lhs.index)
...@@ -2698,8 +2762,7 @@ class InPlaceAssignmentNode(AssignmentNode): ...@@ -2698,8 +2762,7 @@ class InPlaceAssignmentNode(AssignmentNode):
base = ExprNodes.CloneNode(self.dup.base), base = ExprNodes.CloneNode(self.dup.base),
index = index, index = index,
indices = indices, indices = indices,
is_temp = self.dup.is_temp, is_temp = self.dup.is_temp)
options = self.dup.options)
self.lhs = target_lhs self.lhs = target_lhs
return self.dup return self.dup
...@@ -3027,7 +3090,7 @@ class AssertStatNode(StatNode): ...@@ -3027,7 +3090,7 @@ class AssertStatNode(StatNode):
self.cond.generate_evaluation_code(code) self.cond.generate_evaluation_code(code)
code.putln( code.putln(
"if (unlikely(!%s)) {" % "if (unlikely(!%s)) {" %
self.cond.result_code) self.cond.result())
if self.value: if self.value:
self.value.generate_evaluation_code(code) self.value.generate_evaluation_code(code)
code.putln( code.putln(
...@@ -3122,7 +3185,7 @@ class IfClauseNode(Node): ...@@ -3122,7 +3185,7 @@ class IfClauseNode(Node):
self.condition.generate_evaluation_code(code) self.condition.generate_evaluation_code(code)
code.putln( code.putln(
"if (%s) {" % "if (%s) {" %
self.condition.result_code) self.condition.result())
self.body.generate_execution_code(code) self.body.generate_execution_code(code)
#code.putln( #code.putln(
# "goto %s;" % # "goto %s;" %
...@@ -3220,7 +3283,7 @@ class WhileStatNode(LoopNode, StatNode): ...@@ -3220,7 +3283,7 @@ class WhileStatNode(LoopNode, StatNode):
self.condition.generate_evaluation_code(code) self.condition.generate_evaluation_code(code)
code.putln( code.putln(
"if (!%s) break;" % "if (!%s) break;" %
self.condition.result_code) self.condition.result())
self.body.generate_execution_code(code) self.body.generate_execution_code(code)
code.put_label(code.continue_label) code.put_label(code.continue_label)
code.putln("}") code.putln("}")
...@@ -3417,7 +3480,7 @@ class ForFromStatNode(LoopNode, StatNode): ...@@ -3417,7 +3480,7 @@ class ForFromStatNode(LoopNode, StatNode):
c_loopvar_node = ExprNodes.TempNode(self.pos, c_loopvar_node = ExprNodes.TempNode(self.pos,
PyrexTypes.c_long_type, env) PyrexTypes.c_long_type, env)
c_loopvar_node.allocate_temps(env) c_loopvar_node.allocate_temps(env)
self.loopvar_name = c_loopvar_node.result_code self.loopvar_name = c_loopvar_node.result()
self.py_loopvar_node = \ self.py_loopvar_node = \
ExprNodes.CloneNode(c_loopvar_node).coerce_to_pyobject(env) ExprNodes.CloneNode(c_loopvar_node).coerce_to_pyobject(env)
self.bound1.allocate_temps(env) self.bound1.allocate_temps(env)
...@@ -3446,12 +3509,12 @@ class ForFromStatNode(LoopNode, StatNode): ...@@ -3446,12 +3509,12 @@ class ForFromStatNode(LoopNode, StatNode):
offset, incop = self.relation_table[self.relation1] offset, incop = self.relation_table[self.relation1]
if self.step is not None: if self.step is not None:
self.step.generate_evaluation_code(code) self.step.generate_evaluation_code(code)
incop = "%s=%s" % (incop[0], self.step.result_code) incop = "%s=%s" % (incop[0], self.step.result())
code.putln( code.putln(
"for (%s = %s%s; %s %s %s; %s%s) {" % ( "for (%s = %s%s; %s %s %s; %s%s) {" % (
self.loopvar_name, self.loopvar_name,
self.bound1.result_code, offset, self.bound1.result(), offset,
self.loopvar_name, self.relation2, self.bound2.result_code, self.loopvar_name, self.relation2, self.bound2.result(),
self.loopvar_name, incop)) self.loopvar_name, incop))
if self.py_loopvar_node: if self.py_loopvar_node:
self.py_loopvar_node.generate_evaluation_code(code) self.py_loopvar_node.generate_evaluation_code(code)
...@@ -4117,10 +4180,10 @@ class FromImportStatNode(StatNode): ...@@ -4117,10 +4180,10 @@ class FromImportStatNode(StatNode):
for cname, target in self.interned_items: for cname, target in self.interned_items:
code.putln( code.putln(
'%s = PyObject_GetAttr(%s, %s); %s' % ( '%s = PyObject_GetAttr(%s, %s); %s' % (
self.item.result_code, self.item.result(),
self.module.py_result(), self.module.py_result(),
cname, cname,
code.error_goto_if_null(self.item.result_code, self.pos))) code.error_goto_if_null(self.item.result(), self.pos)))
target.generate_assignment_code(self.item, code) target.generate_assignment_code(self.item, code)
self.module.generate_disposal_code(code) self.module.generate_disposal_code(code)
...@@ -4364,9 +4427,12 @@ static void __Pyx_ReRaise(void) { ...@@ -4364,9 +4427,12 @@ static void __Pyx_ReRaise(void) {
arg_type_test_utility_code = [ arg_type_test_utility_code = [
""" """
static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed, char *name, int exact); /*proto*/ static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed,
const char *name, int exact); /*proto*/
""",""" ""","""
static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed, char *name, int exact) { static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed,
const char *name, int exact)
{
if (!type) { if (!type) {
PyErr_Format(PyExc_SystemError, "Missing type object"); PyErr_Format(PyExc_SystemError, "Missing type object");
return 0; return 0;
......
...@@ -56,9 +56,8 @@ class SwitchTransform(Visitor.VisitorTransform): ...@@ -56,9 +56,8 @@ class SwitchTransform(Visitor.VisitorTransform):
def visit_IfStatNode(self, node): def visit_IfStatNode(self, node):
self.visitchildren(node) self.visitchildren(node)
if len(node.if_clauses) < 3:
return node
common_var = None common_var = None
case_count = 0
cases = [] cases = []
for if_clause in node.if_clauses: for if_clause in node.if_clauses:
var, conditions = self.extract_conditions(if_clause.condition) var, conditions = self.extract_conditions(if_clause.condition)
...@@ -70,9 +69,12 @@ class SwitchTransform(Visitor.VisitorTransform): ...@@ -70,9 +69,12 @@ class SwitchTransform(Visitor.VisitorTransform):
return node return node
else: else:
common_var = var common_var = var
case_count += len(conditions)
cases.append(Nodes.SwitchCaseNode(pos = if_clause.pos, cases.append(Nodes.SwitchCaseNode(pos = if_clause.pos,
conditions = conditions, conditions = conditions,
body = if_clause.body)) body = if_clause.body))
if case_count < 2:
return node
common_var = unwrap_node(common_var) common_var = unwrap_node(common_var)
return Nodes.SwitchStatNode(pos = node.pos, return Nodes.SwitchStatNode(pos = node.pos,
...@@ -138,7 +140,15 @@ class FlattenInListTransform(Visitor.VisitorTransform): ...@@ -138,7 +140,15 @@ class FlattenInListTransform(Visitor.VisitorTransform):
return node return node
class OptimizeRefcounting(Visitor.CythonTransform): class FinalOptimizePhase(Visitor.CythonTransform):
"""
This visitor handles several commuting optimizations, and is run
just before the C code generation phase.
The optimizations currently implemented in this class are:
- Eliminate None assignment and refcounting for first assignment.
- isinstance -> typecheck for cdef types
"""
def visit_SingleAssignmentNode(self, node): def visit_SingleAssignmentNode(self, node):
if node.first: if node.first:
lhs = node.lhs lhs = node.lhs
...@@ -149,3 +159,16 @@ class OptimizeRefcounting(Visitor.CythonTransform): ...@@ -149,3 +159,16 @@ class OptimizeRefcounting(Visitor.CythonTransform):
# Set a flag in NameNode to skip the decref # Set a flag in NameNode to skip the decref
lhs.skip_assignment_decref = True lhs.skip_assignment_decref = True
return node return node
def visit_SimpleCallNode(self, node):
self.visitchildren(node)
if node.function.type.is_cfunction and isinstance(node.function, ExprNodes.NameNode):
if node.function.name == 'isinstance':
type_arg = node.args[1]
if type_arg.type.is_builtin_type and type_arg.type.name == 'type':
object_module = self.context.find_module('python_object')
node.function.entry = object_module.lookup('PyObject_TypeCheck')
node.function.type = node.function.entry.type
PyTypeObjectPtr = PyrexTypes.CPtrType(object_module.lookup('PyTypeObject').type)
node.args[1] = ExprNodes.CastNode(node.args[1], PyTypeObjectPtr)
return node
...@@ -56,11 +56,15 @@ c_line_in_traceback = 1 ...@@ -56,11 +56,15 @@ c_line_in_traceback = 1
# Declare pragmas # Declare pragmas
option_types = { option_types = {
'boundscheck' : bool 'boundscheck' : bool,
'nonecheck' : bool,
'embedsignature' : bool
} }
option_defaults = { option_defaults = {
'boundscheck' : True 'boundscheck' : True,
'nonecheck' : False,
'embedsignature' : False,
} }
def parse_option_value(name, value): def parse_option_value(name, value):
......
...@@ -240,7 +240,7 @@ class PxdPostParse(CythonTransform): ...@@ -240,7 +240,7 @@ class PxdPostParse(CythonTransform):
else: else:
return node return node
class ResolveOptions(CythonTransform): class InterpretCompilerDirectives(CythonTransform):
""" """
After parsing, options can be stored in a number of places: After parsing, options can be stored in a number of places:
- #cython-comments at the top of the file (stored in ModuleNode) - #cython-comments at the top of the file (stored in ModuleNode)
...@@ -248,28 +248,38 @@ class ResolveOptions(CythonTransform): ...@@ -248,28 +248,38 @@ class ResolveOptions(CythonTransform):
- @cython.optionname decorators - @cython.optionname decorators
- with cython.optionname: statements - with cython.optionname: statements
This transform is responsible for annotating each node with an This transform is responsible for interpreting these various sources
"options" attribute linking it to a dict containing the exact and store the option in two ways:
options that are in effect for that node. Any corresponding decorators - Set the directives attribute of the ModuleNode for global directives.
or with statements are removed in the process. - Use a CompilerDirectivesNode to override directives for a subtree.
(The first one is primarily to not have to modify with the tree
structure, so that ModuleNode stay on top.)
The directives are stored in dictionaries from name to value in effect.
Each such dictionary is always filled in for all possible directives,
using default values where no value is given by the user.
The available directives are controlled in Options.py.
Note that we have to run this prior to analysis, and so some minor Note that we have to run this prior to analysis, and so some minor
duplication of functionality has to occur: We manually track cimports duplication of functionality has to occur: We manually track cimports
to correctly intercept @cython... and with cython... and which names the "cython" module may have been imported to.
""" """
def __init__(self, context, compilation_option_overrides): def __init__(self, context, compilation_option_overrides):
super(ResolveOptions, self).__init__(context) super(InterpretCompilerDirectives, self).__init__(context)
self.compilation_option_overrides = compilation_option_overrides self.compilation_option_overrides = compilation_option_overrides
self.cython_module_names = set() self.cython_module_names = set()
self.option_names = {} self.option_names = {}
# Set up processing and handle the cython: comments.
def visit_ModuleNode(self, node): def visit_ModuleNode(self, node):
options = copy.copy(Options.option_defaults) options = copy.copy(Options.option_defaults)
options.update(node.option_comments) options.update(node.option_comments)
options.update(self.compilation_option_overrides) options.update(self.compilation_option_overrides)
self.options = options self.options = options
node.options = options node.directives = options
self.visitchildren(node) self.visitchildren(node)
return node return node
...@@ -299,7 +309,6 @@ class ResolveOptions(CythonTransform): ...@@ -299,7 +309,6 @@ class ResolveOptions(CythonTransform):
return node return node
def visit_Node(self, node): def visit_Node(self, node):
node.options = self.options
self.visitchildren(node) self.visitchildren(node)
return node return node
...@@ -331,14 +340,17 @@ class ResolveOptions(CythonTransform): ...@@ -331,14 +340,17 @@ class ResolveOptions(CythonTransform):
return None return None
def visit_with_options(self, node, options): def visit_with_options(self, body, options):
oldoptions = self.options oldoptions = self.options
newoptions = copy.copy(oldoptions) newoptions = copy.copy(oldoptions)
newoptions.update(options) newoptions.update(options)
self.options = newoptions self.options = newoptions
node = self.visit_Node(node) assert isinstance(body, StatListNode), body
retbody = self.visit_Node(body)
directive = CompilerDirectivesNode(pos=retbody.pos, body=retbody,
directives=newoptions)
self.options = oldoptions self.options = oldoptions
return node return directive
# Handle decorators # Handle decorators
def visit_DefNode(self, node): def visit_DefNode(self, node):
...@@ -361,7 +373,8 @@ class ResolveOptions(CythonTransform): ...@@ -361,7 +373,8 @@ class ResolveOptions(CythonTransform):
for option in options: for option in options:
name, value = option name, value = option
optdict[name] = value optdict[name] = value
return self.visit_with_options(node, optdict) body = StatListNode(node.pos, stats=[node])
return self.visit_with_options(body, optdict)
else: else:
return self.visit_Node(node) return self.visit_Node(node)
...@@ -372,8 +385,7 @@ class ResolveOptions(CythonTransform): ...@@ -372,8 +385,7 @@ class ResolveOptions(CythonTransform):
if node.target is not None: if node.target is not None:
raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'") raise PostParseError(node.pos, "Compiler option with statements cannot contain 'as'")
name, value = option name, value = option
self.visit_with_options(node.body, {name:value}) return self.visit_with_options(node.body, {name:value})
return node.body.stats
else: else:
return self.visit_Node(node) return self.visit_Node(node)
...@@ -429,7 +441,7 @@ class WithTransform(CythonTransform): ...@@ -429,7 +441,7 @@ class WithTransform(CythonTransform):
u'BODY' : node.body, u'BODY' : node.body,
u'TARGET' : node.target, u'TARGET' : node.target,
u'EXCINFO' : excinfo_namenode u'EXCINFO' : excinfo_namenode
}, pos = node.pos) }, pos=node.pos)
# Set except excinfo target to EXCINFO # Set except excinfo target to EXCINFO
result.stats[4].body.stats[0].except_clauses[0].excinfo_target = excinfo_target result.stats[4].body.stats[0].except_clauses[0].excinfo_target = excinfo_target
else: else:
...@@ -437,7 +449,7 @@ class WithTransform(CythonTransform): ...@@ -437,7 +449,7 @@ class WithTransform(CythonTransform):
u'EXPR' : node.manager, u'EXPR' : node.manager,
u'BODY' : node.body, u'BODY' : node.body,
u'EXCINFO' : excinfo_namenode u'EXCINFO' : excinfo_namenode
}, pos = node.pos) }, pos=node.pos)
# Set except excinfo target to EXCINFO # Set except excinfo target to EXCINFO
result.stats[4].body.stats[0].except_clauses[0].excinfo_target = excinfo_target result.stats[4].body.stats[0].except_clauses[0].excinfo_target = excinfo_target
......
...@@ -229,6 +229,8 @@ def p_typecast(s): ...@@ -229,6 +229,8 @@ def p_typecast(s):
pos = s.position() pos = s.position()
s.next() s.next()
base_type = p_c_base_type(s) base_type = p_c_base_type(s)
if base_type.name is None:
s.error("Unknown type")
declarator = p_c_declarator(s, empty = 1) declarator = p_c_declarator(s, empty = 1)
if s.sy == '?': if s.sy == '?':
s.next() s.next()
......
...@@ -200,17 +200,21 @@ class BufferType(BaseType): ...@@ -200,17 +200,21 @@ class BufferType(BaseType):
# dtype PyrexType # dtype PyrexType
# ndim int # ndim int
# mode str # mode str
# is_buffer boolean # negative_indices bool
# writable boolean # cast bool
# is_buffer bool
# writable bool
is_buffer = 1 is_buffer = 1
writable = True writable = True
def __init__(self, base, dtype, ndim, mode): def __init__(self, base, dtype, ndim, mode, negative_indices, cast):
self.base = base self.base = base
self.dtype = dtype self.dtype = dtype
self.ndim = ndim self.ndim = ndim
self.buffer_ptr_type = CPtrType(dtype) self.buffer_ptr_type = CPtrType(dtype)
self.mode = mode self.mode = mode
self.negative_indices = negative_indices
self.cast = cast
def as_argument_type(self): def as_argument_type(self):
return self return self
...@@ -732,7 +736,7 @@ class CFuncType(CType): ...@@ -732,7 +736,7 @@ class CFuncType(CType):
return 1 return 1
if not other_type.is_cfunction: if not other_type.is_cfunction:
return 0 return 0
if not self.is_overridable and other_type.is_overridable: if self.is_overridable != other_type.is_overridable:
return 0 return 0
nargs = len(self.args) nargs = len(self.args)
if nargs != len(other_type.args): if nargs != len(other_type.args):
...@@ -846,6 +850,8 @@ class CFuncType(CType): ...@@ -846,6 +850,8 @@ class CFuncType(CType):
for arg in self.args[:len(self.args)-self.optional_arg_count]: for arg in self.args[:len(self.args)-self.optional_arg_count]:
arg_decl_list.append( arg_decl_list.append(
arg.type.declaration_code("", for_display, pyrex = pyrex)) arg.type.declaration_code("", for_display, pyrex = pyrex))
if self.is_overridable:
arg_decl_list.append("int %s" % Naming.skip_dispatch_cname)
if self.optional_arg_count: if self.optional_arg_count:
arg_decl_list.append(self.op_arg_struct.declaration_code(Naming.optional_args_cname)) arg_decl_list.append(self.op_arg_struct.declaration_code(Naming.optional_args_cname))
if self.has_varargs: if self.has_varargs:
...@@ -1184,11 +1190,16 @@ def widest_numeric_type(type1, type2): ...@@ -1184,11 +1190,16 @@ def widest_numeric_type(type1, type2):
# Given two numeric types, return the narrowest type # Given two numeric types, return the narrowest type
# encompassing both of them. # encompassing both of them.
if type1.is_enum and type2.is_enum: if type1.is_enum and type2.is_enum:
widest_type = c_int_type return c_int_type
elif type2.rank > type1.rank: elif type1 is type2:
widest_type = type2 return type1
elif (type1.signed and type2.signed) or (not type1.signed and not type2.signed):
if type2.rank > type1.rank:
return type2
else:
return type1
else: else:
widest_type = type1 return sign_and_rank_to_type[min(type1.signed, type2.signed), max(type1.rank, type2.rank)]
return widest_type return widest_type
def simple_c_type(signed, longness, name): def simple_c_type(signed, longness, name):
......
...@@ -6,8 +6,7 @@ import re ...@@ -6,8 +6,7 @@ import re
from Cython import Utils from Cython import Utils
from Errors import warning, error, InternalError from Errors import warning, error, InternalError
from StringEncoding import EncodedString from StringEncoding import EncodedString
import Options import Options, Naming
import Naming
import PyrexTypes import PyrexTypes
from PyrexTypes import py_object_type from PyrexTypes import py_object_type
import TypeSlots import TypeSlots
...@@ -138,6 +137,7 @@ class Entry: ...@@ -138,6 +137,7 @@ class Entry:
utility_code = None utility_code = None
is_overridable = 0 is_overridable = 0
buffer_aux = None buffer_aux = None
prev_entry = None
def __init__(self, name, cname, type, pos = None, init = None): def __init__(self, name, cname, type, pos = None, init = None):
self.name = name self.name = name
...@@ -178,6 +178,8 @@ class Scope: ...@@ -178,6 +178,8 @@ class Scope:
# Python strings in this scope # Python strings in this scope
# control_flow ControlFlow Used for keeping track of environment state # control_flow ControlFlow Used for keeping track of environment state
# nogil boolean In a nogil section # nogil boolean In a nogil section
# directives dict Helper variable for the recursive
# analysis, contains directive values.
is_py_class_scope = 0 is_py_class_scope = 0
is_c_class_scope = 0 is_c_class_scope = 0
...@@ -185,6 +187,7 @@ class Scope: ...@@ -185,6 +187,7 @@ class Scope:
scope_prefix = "" scope_prefix = ""
in_cinclude = 0 in_cinclude = 0
nogil = 0 nogil = 0
directives = {}
temp_prefix = Naming.pyrex_prefix temp_prefix = Naming.pyrex_prefix
...@@ -277,7 +280,7 @@ class Scope: ...@@ -277,7 +280,7 @@ class Scope:
if name and dict.has_key(name): if name and dict.has_key(name):
if visibility == 'extern': if visibility == 'extern':
warning(pos, "'%s' redeclared " % name, 0) warning(pos, "'%s' redeclared " % name, 0)
else: elif visibility != 'ignore':
error(pos, "'%s' redeclared " % name) error(pos, "'%s' redeclared " % name)
entry = Entry(name, cname, type, pos = pos) entry = Entry(name, cname, type, pos = pos)
entry.in_cinclude = self.in_cinclude entry.in_cinclude = self.in_cinclude
...@@ -707,7 +710,7 @@ class BuiltinScope(Scope): ...@@ -707,7 +710,7 @@ class BuiltinScope(Scope):
entry = self.declare_type(name, type, None, visibility='extern') entry = self.declare_type(name, type, None, visibility='extern')
var_entry = Entry(name = entry.name, var_entry = Entry(name = entry.name,
type = py_object_type, type = self.lookup('type').type, # make sure "type" is the first type declared...
pos = entry.pos, pos = entry.pos,
cname = "((PyObject*)%s)" % entry.type.typeptr_cname) cname = "((PyObject*)%s)" % entry.type.typeptr_cname)
var_entry.is_variable = 1 var_entry.is_variable = 1
...@@ -1106,8 +1109,9 @@ class ModuleScope(Scope): ...@@ -1106,8 +1109,9 @@ class ModuleScope(Scope):
# variable entry attached to it. For the variable entry, # variable entry attached to it. For the variable entry,
# we use a read-only C global variable whose name is an # we use a read-only C global variable whose name is an
# expression that refers to the type object. # expression that refers to the type object.
import Builtin
var_entry = Entry(name = entry.name, var_entry = Entry(name = entry.name,
type = py_object_type, type = Builtin.type_type,
pos = entry.pos, pos = entry.pos,
cname = "((PyObject*)%s)" % entry.type.typeptr_cname) cname = "((PyObject*)%s)" % entry.type.typeptr_cname)
var_entry.is_variable = 1 var_entry.is_variable = 1
...@@ -1411,22 +1415,8 @@ class CClassScope(ClassScope): ...@@ -1411,22 +1415,8 @@ class CClassScope(ClassScope):
if type.same_c_signature_as(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil: if type.same_c_signature_as(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil:
pass pass
elif type.compatible_signature_with(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil: elif type.compatible_signature_with(entry.type, as_cmethod = 1) and type.nogil == entry.type.nogil:
if type.optional_arg_count and not type.original_sig.optional_arg_count: entry = self.add_cfunction(name, type, pos, cname or name, visibility='ignore')
# Need to put a wrapper taking no optional arguments
# into the method table.
wrapper_func_cname = self.mangle(Naming.func_prefix, name) + Naming.no_opt_args
wrapper_func_name = name + Naming.no_opt_args
if entry.type.optional_arg_count:
old_entry = self.lookup_here(wrapper_func_name)
old_entry.func_cname = wrapper_func_cname
else:
entry.func_cname = wrapper_func_cname
entry.name = wrapper_func_name
entry = self.add_cfunction(name, type, pos, cname or name, visibility)
defining = 1 defining = 1
entry.type = type
# if type.narrower_c_signature_than(entry.type, as_cmethod = 1):
# entry.type = type
else: else:
error(pos, "Signature not compatible with previous declaration") error(pos, "Signature not compatible with previous declaration")
error(entry.pos, "Previous declaration is here") error(entry.pos, "Previous declaration is here")
...@@ -1442,8 +1432,10 @@ class CClassScope(ClassScope): ...@@ -1442,8 +1432,10 @@ class CClassScope(ClassScope):
def add_cfunction(self, name, type, pos, cname, visibility): def add_cfunction(self, name, type, pos, cname, visibility):
# Add a cfunction entry without giving it a func_cname. # Add a cfunction entry without giving it a func_cname.
prev_entry = self.lookup_here(name)
entry = ClassScope.add_cfunction(self, name, type, pos, cname, visibility) entry = ClassScope.add_cfunction(self, name, type, pos, cname, visibility)
entry.is_cmethod = 1 entry.is_cmethod = 1
entry.prev_entry = prev_entry
return entry return entry
def declare_property(self, name, doc, pos): def declare_property(self, name, doc, pos):
......
...@@ -16,12 +16,11 @@ class BasicVisitor(object): ...@@ -16,12 +16,11 @@ class BasicVisitor(object):
self.dispatch_table = {} self.dispatch_table = {}
def visit(self, obj): def visit(self, obj):
pattern = "visit_%s"
cls = obj.__class__ cls = obj.__class__
mname = pattern % cls.__name__ m = self.dispatch_table.get(cls.__name__)
m = self.dispatch_table.get(mname)
if m is None: if m is None:
# Must resolve, try entire hierarchy # Must resolve, try entire hierarchy
pattern = "visit_%s"
mro = inspect.getmro(cls) mro = inspect.getmro(cls)
for cls in mro: for cls in mro:
m = getattr(self, pattern % cls.__name__, None) m = getattr(self, pattern % cls.__name__, None)
...@@ -33,7 +32,7 @@ class BasicVisitor(object): ...@@ -33,7 +32,7 @@ class BasicVisitor(object):
print self.access_path[-1][0].pos print self.access_path[-1][0].pos
print self.access_path[-1][0].__dict__ print self.access_path[-1][0].__dict__
raise RuntimeError("Visitor does not accept object: %s" % obj) raise RuntimeError("Visitor does not accept object: %s" % obj)
self.dispatch_table[mname] = m self.dispatch_table[cls.__name__] = m
return m(obj) return m(obj)
class TreeVisitor(BasicVisitor): class TreeVisitor(BasicVisitor):
...@@ -111,6 +110,7 @@ class TreeVisitor(BasicVisitor): ...@@ -111,6 +110,7 @@ class TreeVisitor(BasicVisitor):
childretval = [self.visitchild(x, parent, attr, idx) for idx, x in enumerate(child)] childretval = [self.visitchild(x, parent, attr, idx) for idx, x in enumerate(child)]
else: else:
childretval = self.visitchild(child, parent, attr, None) childretval = self.visitchild(child, parent, attr, None)
assert not isinstance(childretval, list), 'Cannot insert list here: %s in %r' % (attr, parent)
result[attr] = childretval result[attr] = childretval
return result return result
...@@ -163,11 +163,26 @@ class CythonTransform(VisitorTransform): ...@@ -163,11 +163,26 @@ class CythonTransform(VisitorTransform):
super(CythonTransform, self).__init__() super(CythonTransform, self).__init__()
self.context = context self.context = context
def __call__(self, node):
import ModuleNode
if isinstance(node, ModuleNode.ModuleNode):
self.current_directives = node.directives
return super(CythonTransform, self).__call__(node)
def visit_CompilerDirectivesNode(self, node):
old = self.current_directives
self.current_directives = node.directives
self.visitchildren(node)
self.current_directives = old
return node
def visit_Node(self, node): def visit_Node(self, node):
self.visitchildren(node) self.visitchildren(node)
return node return node
# Utils # Utils
def ensure_statlist(node): def ensure_statlist(node):
if not isinstance(node, Nodes.StatListNode): if not isinstance(node, Nodes.StatListNode):
......
cimport python_buffer as pybuf
cdef extern from "Python.h": cdef extern from "Python.h":
ctypedef int Py_intptr_t ctypedef int Py_intptr_t
...@@ -19,7 +21,11 @@ cdef extern from "numpy/arrayobject.h": ...@@ -19,7 +21,11 @@ cdef extern from "numpy/arrayobject.h":
NPY_NTYPES, NPY_NTYPES,
NPY_NOTYPE, NPY_NOTYPE,
NPY_CHAR, NPY_CHAR,
NPY_USERDEF NPY_USERDEF,
NPY_C_CONTIGUOUS,
NPY_F_CONTIGUOUS
ctypedef class numpy.ndarray [object PyArrayObject]: ctypedef class numpy.ndarray [object PyArrayObject]:
cdef __cythonbufferdefaults__ = {"mode": "strided"} cdef __cythonbufferdefaults__ = {"mode": "strided"}
...@@ -29,19 +35,27 @@ cdef extern from "numpy/arrayobject.h": ...@@ -29,19 +35,27 @@ cdef extern from "numpy/arrayobject.h":
int ndim "nd" int ndim "nd"
npy_intp *shape "dimensions" npy_intp *shape "dimensions"
npy_intp *strides npy_intp *strides
int flags
# Note: This syntax (function definition in pxd files) is an # Note: This syntax (function definition in pxd files) is an
# experimental exception made for __getbuffer__ and __releasebuffer__ # experimental exception made for __getbuffer__ and __releasebuffer__
# -- the details of this may change. # -- the details of this may change.
def __getbuffer__(ndarray self, Py_buffer* info, int flags): def __getbuffer__(ndarray self, Py_buffer* info, int flags):
# This implementation of getbuffer is geared towards Cython # This implementation of getbuffer is geared towards Cython
# requirements, and does not yet fullfill the PEP (specifically, # requirements, and does not yet fullfill the PEP.
# Cython always requests and we always provide strided access, # In particular strided access is always provided regardless
# so the flags are not even checked). # of flags
if sizeof(npy_intp) != sizeof(Py_ssize_t): if sizeof(npy_intp) != sizeof(Py_ssize_t):
raise RuntimeError("Py_intptr_t and Py_ssize_t differs in size, numpy.pxd does not support this") raise RuntimeError("Py_intptr_t and Py_ssize_t differs in size, numpy.pxd does not support this")
if ((flags & pybuf.PyBUF_C_CONTIGUOUS == pybuf.PyBUF_C_CONTIGUOUS)
and not PyArray_CHKFLAGS(self, NPY_C_CONTIGUOUS)):
raise ValueError("ndarray is not C contiguous")
if ((flags & pybuf.PyBUF_F_CONTIGUOUS == pybuf.PyBUF_F_CONTIGUOUS)
and not PyArray_CHKFLAGS(self, NPY_F_CONTIGUOUS)):
raise ValueError("ndarray is not Fortran contiguous")
info.buf = PyArray_DATA(self) info.buf = PyArray_DATA(self)
# info.obj = None # this is automatic # info.obj = None # this is automatic
info.ndim = PyArray_NDIM(self) info.ndim = PyArray_NDIM(self)
...@@ -85,6 +99,7 @@ cdef extern from "numpy/arrayobject.h": ...@@ -85,6 +99,7 @@ cdef extern from "numpy/arrayobject.h":
cdef npy_intp PyArray_STRIDES(ndarray arr) cdef npy_intp PyArray_STRIDES(ndarray arr)
cdef npy_intp PyArray_DIMS(ndarray arr) cdef npy_intp PyArray_DIMS(ndarray arr)
cdef Py_ssize_t PyArray_ITEMSIZE(ndarray arr) cdef Py_ssize_t PyArray_ITEMSIZE(ndarray arr)
cdef int PyArray_CHKFLAGS(ndarray arr, int flags)
ctypedef signed int npy_byte ctypedef signed int npy_byte
ctypedef signed int npy_short ctypedef signed int npy_short
......
...@@ -233,7 +233,7 @@ cdef extern from "Python.h": ...@@ -233,7 +233,7 @@ cdef extern from "Python.h":
# pointer of type PyTypeObject*, except when the incremented # pointer of type PyTypeObject*, except when the incremented
# reference count is needed. # reference count is needed.
bint PyObject_TypeCheck(object o, object type) # object o, PyTypeObject *type) bint PyObject_TypeCheck(object o, PyTypeObject *type)
# Return true if the object o is of type type or a subtype of # Return true if the object o is of type type or a subtype of
# type. Both parameters must be non-NULL. # type. Both parameters must be non-NULL.
......
The PYTHON SOFTWARE FOUNDATION LICENSE: Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS
\ No newline at end of file
PSF LICENSE AGREEMENT FOR PYTHON 2.4
------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using Python 2.4 software in source or binary form and its
associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 2.4
alone or in any derivative version, provided, however, that PSF's
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
2001, 2002, 2003, 2004 Python Software Foundation; All Rights Reserved"
are retained in Python 2.4 alone or in any derivative version prepared
by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 2.4 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 2.4.
4. PSF is making Python 2.4 available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.4 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
2.4 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.4,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python 2.4, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
......
#! /usr/bin/env python
# --------------------------------------------------------------------
import re
from epydoc import docstringparser as dsp
CYTHON_SIGNATURE_RE = re.compile(
# Class name (for builtin methods)
r'^\s*((?P<class>\w+)\.)?' +
# The function name
r'(?P<func>\w+)' +
# The parameters
r'\(((?P<self>(?:self|cls|mcs)),?)?(?P<params>.*)\)' +
# The return value (optional)
r'(\s*(->)\s*(?P<return>\w+(?:\s*\w+)))?' +
# The end marker
r'\s*(?:\n|$)')
parse_signature = dsp.parse_function_signature
def parse_function_signature(func_doc, doc_source,
docformat, parse_errors):
PYTHON_SIGNATURE_RE = dsp._SIGNATURE_RE
assert PYTHON_SIGNATURE_RE is not CYTHON_SIGNATURE_RE
try:
dsp._SIGNATURE_RE = CYTHON_SIGNATURE_RE
found = parse_signature(func_doc, doc_source,
docformat, parse_errors)
dsp._SIGNATURE_RE = PYTHON_SIGNATURE_RE
if not found:
found = parse_signature(func_doc, doc_source,
docformat, parse_errors)
return found
finally:
dsp._SIGNATURE_RE = PYTHON_SIGNATURE_RE
dsp.parse_function_signature = parse_function_signature
# --------------------------------------------------------------------
from epydoc.cli import cli
cli()
# --------------------------------------------------------------------
...@@ -460,8 +460,11 @@ if __name__ == '__main__': ...@@ -460,8 +460,11 @@ if __name__ == '__main__':
action="store_true", default=False, action="store_true", default=False,
help="collect source coverage data for the Compiler") help="collect source coverage data for the Compiler")
parser.add_option("-A", "--annotate", dest="annotate_source", parser.add_option("-A", "--annotate", dest="annotate_source",
action="store_true", default=False, action="store_true", default=True,
help="generate annotated HTML versions of the test source files") help="generate annotated HTML versions of the test source files")
parser.add_option("--no-annotate", dest="annotate_source",
action="store_false",
help="do not generate annotated HTML versions of the test source files")
parser.add_option("-v", "--verbose", dest="verbosity", parser.add_option("-v", "--verbose", dest="verbosity",
action="count", default=0, action="count", default=0,
help="display test progress, pass twice to print test names") help="display test progress, pass twice to print test names")
......
cdef int x = 3
if x == NONEXISTING:
print 2
elif x == 2:
print 2342
elif x == 4:
print 34
_ERRORS = """
4:23: undeclared name not builtin: NONEXISTING
"""
cdef class Test:
cdef __cinit__(self):
pass
cdef __len__(self):
pass
_ERRORS = u"""
3:9: Special methods must be declared with 'def', not 'cdef'
6:9: Special methods must be declared with 'def', not 'cdef'
"""
def f():
a = <foao>x
_ERRORS = """
2:13: Unknown type
"""
\ No newline at end of file
...@@ -487,6 +487,25 @@ def list_comprehension(object[int] buf, len): ...@@ -487,6 +487,25 @@ def list_comprehension(object[int] buf, len):
cdef int i cdef int i
print u"|".join([unicode(buf[i]) for i in range(len)]) print u"|".join([unicode(buf[i]) for i in range(len)])
#
# The negative_indices buffer option
#
@testcase
def no_negative_indices(object[int, negative_indices=False] buf, int idx):
"""
The most interesting thing here is to inspect the C source and
make sure optimal code is produced.
>>> A = IntMockBuffer(None, range(6))
>>> no_negative_indices(A, 3)
3
>>> no_negative_indices(A, -1)
Traceback (most recent call last):
...
IndexError: Out of bounds on buffer access (axis 0)
"""
return buf[idx]
# #
# Buffer type mismatch examples. Varying the type and access # Buffer type mismatch examples. Varying the type and access
# method simultaneously, the odds of an interaction is virtually # method simultaneously, the odds of an interaction is virtually
...@@ -567,6 +586,54 @@ def strided(object[int, ndim=1, mode='strided'] buf): ...@@ -567,6 +586,54 @@ def strided(object[int, ndim=1, mode='strided'] buf):
""" """
return buf[2] return buf[2]
@testcase
def c_contig(object[int, ndim=1, mode='c'] buf):
"""
>>> A = IntMockBuffer(None, range(4))
>>> c_contig(A)
2
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
"""
return buf[2]
@testcase
def c_contig_2d(object[int, ndim=2, mode='c'] buf):
"""
Multi-dim has seperate implementation
>>> A = IntMockBuffer(None, range(12), shape=(3,4))
>>> c_contig_2d(A)
7
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
"""
return buf[1, 3]
@testcase
def f_contig(object[int, ndim=1, mode='fortran'] buf):
"""
>>> A = IntMockBuffer(None, range(4))
>>> f_contig(A)
2
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
"""
return buf[2]
@testcase
def f_contig_2d(object[int, ndim=2, mode='fortran'] buf):
"""
Must set up strides manually to ensure Fortran ordering.
>>> A = IntMockBuffer(None, range(12), shape=(4,3), strides=(1, 4))
>>> f_contig_2d(A)
7
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
"""
return buf[3, 1]
# #
# Test compiler options for bounds checking. We create an array with a # Test compiler options for bounds checking. We create an array with a
# safe "boundary" (memory # safe "boundary" (memory
...@@ -601,7 +668,7 @@ def safe_get(object[int] buf, int idx): ...@@ -601,7 +668,7 @@ def safe_get(object[int] buf, int idx):
return buf[idx] return buf[idx]
@testcase @testcase
@cython.boundscheck(False) @cython.boundscheck(False) # outer decorators should take precedence
@cython.boundscheck(True) @cython.boundscheck(True)
def unsafe_get(object[int] buf, int idx): def unsafe_get(object[int] buf, int idx):
""" """
...@@ -616,6 +683,18 @@ def unsafe_get(object[int] buf, int idx): ...@@ -616,6 +683,18 @@ def unsafe_get(object[int] buf, int idx):
""" """
return buf[idx] return buf[idx]
@testcase
@cython.boundscheck(False)
def unsafe_get_nonegative(object[int, negative_indices=False] buf, int idx):
"""
Also inspect the C source to see that it is optimal...
>>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5)
>>> unsafe_get_nonegative(A, -2)
3
"""
return buf[idx]
@testcase @testcase
def mixed_get(object[int] buf, int unsafe_idx, int safe_idx): def mixed_get(object[int] buf, int unsafe_idx, int safe_idx):
""" """
...@@ -878,7 +957,32 @@ def assign_to_object(object[object] buf, int idx, obj): ...@@ -878,7 +957,32 @@ def assign_to_object(object[object] buf, int idx, obj):
""" """
buf[idx] = obj buf[idx] = obj
#
# cast option
#
@testcase
def buffer_cast(object[unsigned int, cast=True] buf, int idx):
"""
Round-trip a signed int through unsigned int buffer access.
>>> A = IntMockBuffer(None, [-100])
>>> buffer_cast(A, 0)
-100
"""
cdef unsigned int data = buf[idx]
return <int>data
@testcase
def buffer_cast_fails(object[char, cast=True] buf):
"""
Cannot cast between datatype of different sizes.
>>> buffer_cast_fails(IntMockBuffer(None, [0]))
Traceback (most recent call last):
...
ValueError: Attempted cast of buffer to datatype of different size.
"""
return buf[0]
# #
...@@ -891,6 +995,8 @@ available_flags = ( ...@@ -891,6 +995,8 @@ available_flags = (
('INDIRECT', python_buffer.PyBUF_INDIRECT), ('INDIRECT', python_buffer.PyBUF_INDIRECT),
('ND', python_buffer.PyBUF_ND), ('ND', python_buffer.PyBUF_ND),
('STRIDES', python_buffer.PyBUF_STRIDES), ('STRIDES', python_buffer.PyBUF_STRIDES),
('C_CONTIGUOUS', python_buffer.PyBUF_C_CONTIGUOUS),
('F_CONTIGUOUS', python_buffer.PyBUF_F_CONTIGUOUS),
('WRITABLE', python_buffer.PyBUF_WRITABLE) ('WRITABLE', python_buffer.PyBUF_WRITABLE)
) )
...@@ -927,7 +1033,6 @@ cdef class MockBuffer: ...@@ -927,7 +1033,6 @@ cdef class MockBuffer:
strides.reverse() strides.reverse()
strides = [x * self.itemsize for x in strides] strides = [x * self.itemsize for x in strides]
suboffsets = [-1] * len(shape) suboffsets = [-1] * len(shape)
datashape = [len(data)] datashape = [len(data)]
p = data p = data
while True: while True:
...@@ -1035,6 +1140,13 @@ cdef class MockBuffer: ...@@ -1035,6 +1140,13 @@ cdef class MockBuffer:
cdef get_default_format(self): cdef get_default_format(self):
print "ERROR, not subclassed", self.__class__ print "ERROR, not subclassed", self.__class__
cdef class CharMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1:
(<char*>buf)[0] = <int>value
return 0
cdef get_itemsize(self): return sizeof(char)
cdef get_default_format(self): return b"@b"
cdef class IntMockBuffer(MockBuffer): cdef class IntMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1: cdef int write(self, char* buf, object value) except -1:
(<int*>buf)[0] = <int>value (<int*>buf)[0] = <int>value
...@@ -1042,6 +1154,13 @@ cdef class IntMockBuffer(MockBuffer): ...@@ -1042,6 +1154,13 @@ cdef class IntMockBuffer(MockBuffer):
cdef get_itemsize(self): return sizeof(int) cdef get_itemsize(self): return sizeof(int)
cdef get_default_format(self): return b"@i" cdef get_default_format(self): return b"@i"
cdef class UnsignedIntMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1:
(<unsigned int*>buf)[0] = <unsigned int>value
return 0
cdef get_itemsize(self): return sizeof(unsigned int)
cdef get_default_format(self): return b"@I"
cdef class ShortMockBuffer(MockBuffer): cdef class ShortMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1: cdef int write(self, char* buf, object value) except -1:
(<short*>buf)[0] = <short>value (<short*>buf)[0] = <short>value
......
#cython: embedsignature=True
# note the r, we use \n below
__doc__ = ur"""
>>> print (Ext.a.__doc__)
Ext.a(self)
>>> print (Ext.b.__doc__)
Ext.b(self, a, b, c)
>>> print (Ext.c.__doc__)
Ext.c(self, a, b, c=1)
>>> print (Ext.d.__doc__)
Ext.d(self, a, b, *, c=88)
>>> print (Ext.e.__doc__)
Ext.e(self, a, b, c=88, **kwds)
>>> print (Ext.f.__doc__)
Ext.f(self, a, b, *, c, d=42)
>>> print (Ext.g.__doc__)
Ext.g(self, a, b, *, c, d=42, e=17, f, **kwds)
>>> print (Ext.h.__doc__)
Ext.h(self, a, b, *args, c, d=42, e=17, f, **kwds)
>>> print (Ext.k.__doc__)
Ext.k(self, a, b, c=1, *args, d=42, e=17, f, **kwds)
>>> print (Ext.get_int.__doc__)
Ext.get_int(self) -> int
>>> print (Ext.get_float.__doc__)
Ext.get_float(self) -> float
>>> print (Ext.clone.__doc__)
Ext.clone(self) -> Ext
>>> print (foo.__doc__)
foo()
>>> with_doc_1.__doc__
'with_doc_1(a, b, c)\nExisting string'
>>> with_doc_2.__doc__
'with_doc_2(a, b, c)\n\n Existing string\n '
>>> types.__doc__
'types(Ext a, int b, unsigned short int c, float d, e)'
>>> print (f_c.__doc__)
f_c(char c) -> char
>>> print (f_uc.__doc__)
f_uc(unsigned char c) -> unsigned char
>>> print (f_sc.__doc__)
f_sc(signed char c) -> signed char
>>> print (f_s.__doc__)
f_s(short int s) -> short int
>>> print (f_us.__doc__)
f_us(unsigned short int s) -> unsigned short int
>>> print (f_ss.__doc__)
f_ss(signed short int s) -> signed short int
>>> print (f_i.__doc__)
f_i(int i) -> int
>>> print (f_ui.__doc__)
f_ui(unsigned int i) -> unsigned int
>>> print (f_si.__doc__)
f_si(signed int i) -> signed int
>>> print (f_l.__doc__)
f_l(long int l) -> long int
>>> print (f_ul.__doc__)
f_ul(unsigned long int l) -> unsigned long int
>>> print (f_sl.__doc__)
f_sl(signed long int l) -> signed long int
>>> print (f_L.__doc__)
f_L(long long int L) -> long long int
>>> print (f_uL.__doc__)
f_uL(unsigned long long int L) -> unsigned long long int
>>> print (f_sL.__doc__)
f_sL(signed long long int L) -> signed long long int
>>> print (f_f.__doc__)
f_f(float f) -> float
>>> print (f_d.__doc__)
f_d(double d) -> double
>>> print (f_D.__doc__)
f_D(long double D) -> long double
"""
cdef class Ext:
def a(self):
pass
def b(self, a, b, c):
pass
def c(self, a, b, c=1):
pass
def d(self, a, b, *, c = 88):
pass
def e(self, a, b, c = 88, **kwds):
pass
def f(self, a, b, *, c, d = 42):
pass
def g(self, a, b, *, c, d = 42, e = 17, f, **kwds):
pass
def h(self, a, b, *args, c, d = 42, e = 17, f, **kwds):
pass
def k(self, a, b, c=1, *args, d = 42, e = 17, f, **kwds):
pass
cpdef int get_int(self):
return 0
cpdef float get_float(self):
return 0.0
cpdef Ext clone(self):
return Ext()
def foo():
pass
def types(Ext a, int b, unsigned short c, float d, e):
pass
def with_doc_1(a, b, c):
"""Existing string"""
pass
def with_doc_2(a, b, c):
"""
Existing string
"""
pass
cpdef char f_c(char c):
return c
cpdef unsigned char f_uc(unsigned char c):
return c
cpdef signed char f_sc(signed char c):
return c
cpdef short f_s(short s):
return s
cpdef unsigned short f_us(unsigned short s):
return s
cpdef signed short f_ss(signed short s):
return s
cpdef int f_i(int i):
return i
cpdef unsigned int f_ui(unsigned int i):
return i
cpdef signed int f_si(signed int i):
return i
cpdef long f_l(long l):
return l
cpdef unsigned long f_ul(unsigned long l):
return l
cpdef signed long f_sl(signed long l):
return l
cpdef long long f_L(long long L):
return L
cpdef unsigned long long f_uL(unsigned long long L):
return L
cpdef signed long long f_sL(signed long long L):
return L
cpdef float f_f(float f):
return f
cpdef double f_d(double d):
return d
cpdef long double f_D(long double D):
return D
"""
Tests accessing attributes of extension type variables
set to None
>>> obj = MyClass(2, 3)
>>> getattr_(obj)
2
>>> getattr_(None)
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'a'
>>> setattr_(obj)
>>> getattr_(obj)
10
>>> setattr_(None)
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'a'
>>> obj = MyClass(2, 3)
>>> checking(obj)
2
2
>>> checking(None)
var is None
>>> check_and_assign(obj)
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'a'
"""
cimport cython
cdef class MyClass:
cdef int a, b
def __init__(self, a, b):
self.a = a
self.b = b
@cython.nonecheck(True)
def getattr_(MyClass var):
print var.a
@cython.nonecheck(True)
def setattr_(MyClass var):
var.a = 10
def some():
return MyClass(4, 5)
@cython.nonecheck(True)
def checking(MyClass var):
state = (var is None)
if not state:
print var.a
if var is not None:
print var.a
else:
print "var is None"
@cython.nonecheck(True)
def check_and_assign(MyClass var):
if var is not None:
print var.a
var = None
print var.a
...@@ -79,6 +79,30 @@ try: ...@@ -79,6 +79,30 @@ try:
[[0 0 0 0 0] [[0 0 0 0 0]
[0 0 0 0 0]] [0 0 0 0 0]]
Test contiguous access modes:
>>> c_arr = np.array(np.arange(12, dtype='i').reshape(3,4), order='C')
>>> f_arr = np.array(np.arange(12, dtype='i').reshape(3,4), order='F')
>>> test_c_contig(c_arr)
0 1 2 3
4 5 6 7
8 9 10 11
>>> test_f_contig(f_arr)
0 1 2 3
4 5 6 7
8 9 10 11
>>> test_c_contig(f_arr)
Traceback (most recent call last):
...
ValueError: ndarray is not C contiguous
>>> test_f_contig(c_arr)
Traceback (most recent call last):
...
ValueError: ndarray is not Fortran contiguous
>>> test_c_contig(c_arr[::2,::2])
Traceback (most recent call last):
...
ValueError: ndarray is not C contiguous
>>> test_dtype('b', inc1_byte) >>> test_dtype('b', inc1_byte)
>>> test_dtype('B', inc1_ubyte) >>> test_dtype('B', inc1_ubyte)
>>> test_dtype('h', inc1_short) >>> test_dtype('h', inc1_short)
...@@ -112,6 +136,13 @@ try: ...@@ -112,6 +136,13 @@ try:
... ...
ValueError: only objects, int and float dtypes supported for ndarray buffer access so far (dtype is 20) ValueError: only objects, int and float dtypes supported for ndarray buffer access so far (dtype is 20)
>>> test_good_cast()
True
>>> test_bad_cast()
Traceback (most recent call last):
...
ValueError: Attempted cast of buffer to datatype of different size.
""" """
except: except:
__doc__ = "" __doc__ = ""
...@@ -151,6 +182,15 @@ def put_range_long_1d(np.ndarray[long] arr): ...@@ -151,6 +182,15 @@ def put_range_long_1d(np.ndarray[long] arr):
arr[i] = value arr[i] = value
value += 1 value += 1
def test_c_contig(np.ndarray[int, ndim=2, mode='c'] arr):
cdef int i, j
for i in range(arr.shape[0]):
print " ".join([str(arr[i, j]) for j in range(arr.shape[1])])
def test_f_contig(np.ndarray[int, ndim=2, mode='fortran'] arr):
cdef int i, j
for i in range(arr.shape[0]):
print " ".join([str(arr[i, j]) for j in range(arr.shape[1])])
cdef struct cfloat: cdef struct cfloat:
float real float real
...@@ -224,3 +264,13 @@ def test_dtype(dtype, inc1): ...@@ -224,3 +264,13 @@ def test_dtype(dtype, inc1):
a = np.array([0, 10], dtype=dtype) a = np.array([0, 10], dtype=dtype)
inc1(a) inc1(a)
if a[1] != 11: print "failed!" if a[1] != 11: print "failed!"
def test_good_cast():
# Check that a signed int can round-trip through casted unsigned int access
cdef np.ndarray[unsigned int, cast=True] arr = np.array([-100], dtype='i')
cdef unsigned int data = arr[0]
return -100 == <int>data
def test_bad_cast():
# This should raise an exception
cdef np.ndarray[long, cast=True] arr = np.array([1], dtype='b')
...@@ -62,6 +62,33 @@ __doc__ = u""" ...@@ -62,6 +62,33 @@ __doc__ = u"""
12 12
>>> switch_c(13) >>> switch_c(13)
0 0
>>> switch_or(0)
0
>>> switch_or(1)
1
>>> switch_or(2)
1
>>> switch_or(3)
1
>>> switch_or(4)
0
>>> switch_short(0)
0
>>> switch_short(1)
1
>>> switch_short(2)
2
>>> switch_short(3)
0
>>> switch_off(0)
0
>>> switch_off(1)
1
>>> switch_off(2)
0
""" """
def switch_simple_py(x): def switch_simple_py(x):
...@@ -123,3 +150,26 @@ def switch_c(int x): ...@@ -123,3 +150,26 @@ def switch_c(int x):
else: else:
return 0 return 0
return -1 return -1
def switch_or(int x):
if x == 1 or x == 2 or x == 3:
return 1
else:
return 0
return -1
def switch_short(int x):
if x == 1:
return 1
elif 2 == x:
return 2
else:
return 0
return -1
def switch_off(int x):
if x == 1:
return 1
else:
return 0
return -1
__doc__ = """
>>> test_signed()
3 <type 'int'>
9 <type 'long'>
6 <type 'long'>
12 <type 'long'>
"""
cdef int i = 1
cdef long l = 2
cdef unsigned int ui = 4
cdef unsigned long ul = 8
def test_signed():
print i + l, type(i+l)
print i + ul, type(i+ul)
print ui + l, type(ui+l)
print ui + ul, type(ui+ul)
__doc__ = u"""
>>> class PyTest(object):
... def __private(self): pass
>>> py = PyTest()
>>> '_PyTest__private' in dir(py)
True
>>> '__private' in dir(py)
False
>>> cy = CyTest()
>>> '_PyTest__private' in dir(cy)
True
>>> '__private' in dir(cy)
False
"""
class CyTest(object):
def __private(self): pass
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