" % (k, color, code))
f.write(u'\n')
f.close()
# TODO: make this cleaner
def escape(raw_string):
raw_string = raw_string.replace(u"\'", ur"")
raw_string = raw_string.replace(u'\"', ur'"')
raw_string = raw_string.replace(u'\n', ur' \n')
raw_string = raw_string.replace(u'\t', ur'\t')
return raw_string
class AnnotationItem:
def __init__(self, style, text, tag="", size=0):
self.style = style
self.text = text
self.tag = tag
self.size = size
def start(self):
return u"%s" % (self.style, self.text, self.tag)
def end(self):
return self.size, u""
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/AutoDocTransforms.py 0000664 0000000 0000000 00000013616 11136717635 0027136 0 ustar 00root root 0000000 0000000 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
from Cython.Compiler import PyrexTypes
class EmbedSignature(CythonTransform):
def __init__(self, context):
super(EmbedSignature, self).__init__(context)
self.denv = None # XXX
self.class_name = None
self.class_node = None
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 '%r' % ctval
except Exception:
try:
return arg.default.name # XXX
except AttributeError:
return '??>'
def _fmt_arg(self, arg):
if arg.type is PyrexTypes.py_object_type or arg.is_self_arg:
doc = arg.name
else:
doc = arg.type.declaration_code(arg.name, for_display=1)
if arg.default:
arg_defv = self._fmt_arg_defv(arg)
if arg_defv:
doc = doc + ('=%s' % arg_defv)
return doc
def _fmt_arglist(self, args,
npargs=0, pargs=None,
nkargs=0, kargs=None,
hide_self=False):
arglist = []
for arg in args:
if not hide_self or not arg.entry.is_self_arg:
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):
if ret is PyrexTypes.py_object_type:
return None
else:
return ret.declaration_code("", for_display=1)
def _fmt_signature(self, cls_name, func_name, args,
npargs=0, pargs=None,
nkargs=0, kargs=None,
return_type=None, hide_self=False):
arglist = self._fmt_arglist(args,
npargs, pargs,
nkargs, kargs,
hide_self=hide_self)
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):
oldname = self.class_name
oldclass = self.class_node
self.class_node = node
try:
# PyClassDefNode
self.class_name = node.name
except AttributeError:
# CClassDefNode
self.class_name = node.class_name
self.visitchildren(node)
self.class_name = oldname
self.class_node = oldclass
return node
def visit_DefNode(self, node):
if not self.current_directives['embedsignature']:
return node
is_constructor = False
hide_self = False
if node.entry.is_special:
is_constructor = self.class_node and node.name == '__init__'
if not is_constructor:
return node
class_name, func_name = None, self.class_name
hide_self = True
else:
class_name, func_name = self.class_name, node.name
nkargs = getattr(node, 'num_kwonly_args', 0)
npargs = len(node.args) - nkargs
signature = self._fmt_signature(
class_name, func_name, node.args,
npargs, node.star_arg,
nkargs, node.starstar_arg,
return_type=None, hide_self=hide_self)
if signature:
if is_constructor:
doc_holder = self.class_node.entry.type.scope
else:
doc_holder = node.entry
if doc_holder.doc is not None:
old_doc = doc_holder.doc
elif not is_constructor and getattr(node, 'py_func', None) is not None:
old_doc = node.py_func.entry.doc
else:
old_doc = None
new_doc = self._embed_signature(signature, old_doc)
doc_holder.doc = EncodedString(new_doc)
if not is_constructor and getattr(node, 'py_func', None) is not None:
node.py_func.entry.doc = EncodedString(new_doc)
return node
def visit_CFuncDefNode(self, node):
if not self.current_directives['embedsignature']:
return node
if not node.overridable: # not cpdef FOO(...):
return node
signature = self._fmt_signature(
self.class_name, node.declarator.base.name,
node.declarator.args,
return_type=node.return_type)
if signature:
if node.entry.doc is not None:
old_doc = node.entry.doc
elif getattr(node, 'py_func', None) is not None:
old_doc = node.py_func.entry.doc
else:
old_doc = None
new_doc = self._embed_signature(signature, old_doc)
node.entry.doc = EncodedString(new_doc)
if hasattr(node, 'py_func') and node.py_func is not None:
node.py_func.entry.doc = EncodedString(new_doc)
return node
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/Buffer.py 0000664 0000000 0000000 00000105203 11136717635 0024724 0 ustar 00root root 0000000 0000000 from Cython.Compiler.Visitor import VisitorTransform, CythonTransform
from Cython.Compiler.ModuleNode import ModuleNode
from Cython.Compiler.Nodes import *
from Cython.Compiler.ExprNodes import *
from Cython.Compiler.StringEncoding import EncodedString
from Cython.Compiler.Errors import CompileError
from Cython.Utils import UtilityCode
import Interpreter
import PyrexTypes
try:
set
except NameError:
from sets import Set as set
import textwrap
# Code cleanup ideas:
# - One could be more smart about casting in some places
# - Start using CCodeWriters to generate utility functions
# - Create a struct type per ndim rather than keeping loose local vars
def dedent(text, reindent=0):
text = textwrap.dedent(text)
if reindent > 0:
indent = " " * reindent
text = '\n'.join([indent + x for x in text.split('\n')])
return text
class IntroduceBufferAuxiliaryVars(CythonTransform):
#
# Entry point
#
buffers_exists = False
def __call__(self, node):
assert isinstance(node, ModuleNode)
self.max_ndim = 0
result = super(IntroduceBufferAuxiliaryVars, self).__call__(node)
if self.buffers_exists:
use_py2_buffer_functions(node.scope)
use_empty_bufstruct_code(node.scope, self.max_ndim)
return result
#
# Basic operations for transforms
#
def handle_scope(self, node, scope):
# For all buffers, insert extra variables in the scope.
# The variables are also accessible from the buffer_info
# on the buffer entry
bufvars = [entry for name, entry
in scope.entries.iteritems()
if entry.type.is_buffer]
if len(bufvars) > 0:
self.buffers_exists = True
if isinstance(node, ModuleNode) and len(bufvars) > 0:
# for now...note that pos is wrong
raise CompileError(node.pos, "Buffer vars not allowed in module scope")
for entry in bufvars:
if entry.type.dtype.is_ptr:
raise CompileError(node.pos, "Buffers with pointer types not yet supported.")
name = entry.name
buftype = entry.type
if buftype.ndim > self.max_ndim:
self.max_ndim = buftype.ndim
# Declare auxiliary vars
cname = scope.mangle(Naming.bufstruct_prefix, name)
bufinfo = scope.declare_var(name="$%s" % cname, cname=cname,
type=PyrexTypes.c_py_buffer_type, pos=node.pos)
if entry.is_arg:
bufinfo.used = True # otherwise, NameNode will mark whether it is used
def var(prefix, idx, initval):
cname = scope.mangle(prefix, "%d_%s" % (idx, name))
result = scope.declare_var("$%s" % cname, PyrexTypes.c_py_ssize_t_type,
node.pos, cname=cname, is_cdef=True)
result.init = initval
if entry.is_arg:
result.used = True
return result
stridevars = [var(Naming.bufstride_prefix, i, "0") for i in range(entry.type.ndim)]
shapevars = [var(Naming.bufshape_prefix, i, "0") for i in range(entry.type.ndim)]
mode = entry.type.mode
if mode == 'full':
suboffsetvars = [var(Naming.bufsuboffset_prefix, i, "-1") for i in range(entry.type.ndim)]
else:
suboffsetvars = None
entry.buffer_aux = Symtab.BufferAux(bufinfo, stridevars, shapevars, suboffsetvars)
scope.buffer_entries = bufvars
self.scope = scope
def visit_ModuleNode(self, node):
self.handle_scope(node, node.scope)
self.visitchildren(node)
return node
def visit_FuncDefNode(self, node):
self.handle_scope(node, node.local_scope)
self.visitchildren(node)
return node
#
# Analysis
#
buffer_options = ("dtype", "ndim", "mode", "negative_indices", "cast") # ordered!
buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True, "cast": False}
buffer_positional_options_count = 1 # anything beyond this needs keyword argument
ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option'
ERR_BUF_TOO_MANY = 'Too many buffer options'
ERR_BUF_DUP = '"%s" buffer option already supplied'
ERR_BUF_MISSING = '"%s" missing'
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_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):
"""
Must be called during type analysis, as analyse is called
on the dtype argument.
posargs and dictargs should consist of a list and a dict
of tuples (value, pos). Defaults should be a dict of values.
Returns a dict containing all the options a buffer can have and
its value (with the positions stripped).
"""
if defaults is None:
defaults = buffer_defaults
posargs, dictargs = Interpreter.interpret_compiletime_options(posargs, dictargs, type_env=env)
if len(posargs) > buffer_positional_options_count:
raise CompileError(posargs[-1][1], ERR_BUF_TOO_MANY)
options = {}
for name, (value, pos) in dictargs.iteritems():
if not name in buffer_options:
raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
options[name.encode("ASCII")] = value
for name, (value, pos) in zip(buffer_options, posargs):
if not name in buffer_options:
raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
if name in options:
raise CompileError(pos, ERR_BUF_DUP % name)
options[name] = value
# Check that they are all there and copy defaults
for name in buffer_options:
if not name in options:
try:
options[name] = defaults[name]
except KeyError:
if need_complete:
raise CompileError(globalpos, ERR_BUF_MISSING % name)
dtype = options.get("dtype")
if dtype and dtype.is_extension_type:
raise CompileError(globalpos, ERR_BUF_DTYPE)
ndim = options.get("ndim")
if ndim and (not isinstance(ndim, int) or ndim < 0):
raise CompileError(globalpos, ERR_BUF_NDIM)
mode = options.get("mode")
if mode and not (mode in ('full', 'strided', 'c', 'fortran')):
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
#
# Code generation
#
def get_flags(buffer_aux, buffer_type):
flags = 'PyBUF_FORMAT'
mode = buffer_type.mode
if mode == 'full':
flags += '| PyBUF_INDIRECT'
elif mode == 'strided':
flags += '| PyBUF_STRIDES'
elif mode == 'c':
flags += '| PyBUF_C_CONTIGUOUS'
elif mode == 'fortran':
flags += '| PyBUF_F_CONTIGUOUS'
else:
assert False
if buffer_aux.writable_needed: flags += "| PyBUF_WRITABLE"
return flags
def used_buffer_aux_vars(entry):
buffer_aux = entry.buffer_aux
buffer_aux.buffer_info_var.used = True
for s in buffer_aux.shapevars: s.used = True
for s in buffer_aux.stridevars: s.used = True
if buffer_aux.suboffsetvars:
for s in buffer_aux.suboffsetvars: s.used = True
def put_unpack_buffer_aux_into_scope(buffer_aux, mode, code):
# Generate code to copy the needed struct info into local
# variables.
bufstruct = buffer_aux.buffer_info_var.cname
varspec = [("strides", buffer_aux.stridevars),
("shape", buffer_aux.shapevars)]
if mode == 'full':
varspec.append(("suboffsets", buffer_aux.suboffsetvars))
for field, vars in varspec:
code.putln(" ".join(["%s = %s.%s[%d];" %
(s.cname, bufstruct, field, idx)
for idx, s in enumerate(vars)]))
def put_acquire_arg_buffer(entry, code, pos):
code.globalstate.use_utility_code(acquire_utility_code)
buffer_aux = entry.buffer_aux
getbuffer_cname = get_getbuffer_code(entry.type.dtype, code)
# Acquire any new buffer
code.putln(code.error_goto_if("%s((PyObject*)%s, &%s, %s, %d, %d) == -1" % (
getbuffer_cname,
entry.cname,
entry.buffer_aux.buffer_info_var.cname,
get_flags(buffer_aux, entry.type),
entry.type.ndim,
int(entry.type.cast)), pos))
# An exception raised in arg parsing cannot be catched, so no
# need to care about the buffer then.
put_unpack_buffer_aux_into_scope(buffer_aux, entry.type.mode, code)
#def put_release_buffer_normal(entry, code):
# code.putln("if (%s != Py_None) PyObject_ReleaseBuffer(%s, &%s);" % (
# entry.cname,
# entry.cname,
# entry.buffer_aux.buffer_info_var.cname))
def get_release_buffer_code(entry):
return "__Pyx_SafeReleaseBuffer(&%s)" % entry.buffer_aux.buffer_info_var.cname
def put_assign_to_buffer(lhs_cname, rhs_cname, buffer_aux, buffer_type,
is_initialized, pos, code):
"""
Generate code for reassigning a buffer variables. This only deals with getting
the buffer auxiliary structure and variables set up correctly, the assignment
itself and refcounting is the responsibility of the caller.
However, the assignment operation may throw an exception so that the reassignment
never happens.
Depending on the circumstances there are two possible outcomes:
- Old buffer released, new acquired, rhs assigned to lhs
- Old buffer released, new acquired which fails, reaqcuire old lhs buffer
(which may or may not succeed).
"""
code.globalstate.use_utility_code(acquire_utility_code)
bufstruct = buffer_aux.buffer_info_var.cname
flags = get_flags(buffer_aux, buffer_type)
getbuffer = "%s((PyObject*)%%s, &%s, %s, %d, %d)" % (get_getbuffer_code(buffer_type.dtype, code),
# note: object is filled in later (%%s)
bufstruct,
flags,
buffer_type.ndim,
int(buffer_type.cast))
if is_initialized:
# Release any existing buffer
code.putln('__Pyx_SafeReleaseBuffer(&%s);' % bufstruct)
# Acquire
retcode_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
code.putln("%s = %s;" % (retcode_cname, getbuffer % rhs_cname))
code.putln('if (%s) {' % (code.unlikely("%s < 0" % retcode_cname)))
# If acquisition failed, attempt to reacquire the old buffer
# before raising the exception. A failure of reacquisition
# will cause the reacquisition exception to be reported, one
# can consider working around this later.
type, value, tb = [code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False)
for i in range(3)]
code.putln('PyErr_Fetch(&%s, &%s, &%s);' % (type, value, tb))
code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % lhs_cname)))
code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % (type, value, tb))
code.globalstate.use_utility_code(raise_buffer_fallback_code)
code.putln('__Pyx_RaiseBufferFallbackError();')
code.putln('} else {')
code.putln('PyErr_Restore(%s, %s, %s);' % (type, value, tb))
for t in (type, value, tb):
code.funcstate.release_temp(t)
code.putln('}')
code.putln('}')
# Unpack indices
put_unpack_buffer_aux_into_scope(buffer_aux, buffer_type.mode, code)
code.putln(code.error_goto_if_neg(retcode_cname, pos))
code.funcstate.release_temp(retcode_cname)
else:
# Our entry had no previous value, so set to None when acquisition fails.
# In this case, auxiliary vars should be set up right in initialization to a zero-buffer,
# so it suffices to set the buf field to NULL.
code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % rhs_cname)))
code.putln('%s = %s; Py_INCREF(Py_None); %s.buf = NULL;' %
(lhs_cname,
PyrexTypes.typecast(buffer_type, PyrexTypes.py_object_type, "Py_None"),
bufstruct))
code.putln(code.error_goto(pos))
code.put('} else {')
# Unpack indices
put_unpack_buffer_aux_into_scope(buffer_aux, buffer_type.mode, code)
code.putln('}')
def put_buffer_lookup_code(entry, index_signeds, index_cnames, options, pos, code):
"""
Generates code to process indices and calculate an offset into
a buffer. Returns a C string which gives a pointer which can be
read from or written to at will (it is an expression so caller should
store it in a temporary if it is used more than once).
As the bounds checking can have any number of combinations of unsigned
arguments, smart optimizations etc. we insert it directly in the function
body. The lookup however is delegated to a inline function that is instantiated
once per ndim (lookup with suboffsets tend to get quite complicated).
"""
bufaux = entry.buffer_aux
bufstruct = bufaux.buffer_info_var.cname
negative_indices = entry.type.negative_indices
if options['boundscheck']:
# Check bounds and fix negative indices.
# We allocate a temporary which is initialized to -1, meaning OK (!).
# If an error occurs, the temp is set to the dimension index the
# error is occuring at.
tmp_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
code.putln("%s = -1;" % tmp_cname)
for dim, (signed, cname, shape) in enumerate(zip(index_signeds, index_cnames,
bufaux.shapevars)):
if signed != 0:
# not unsigned, deal with negative index
code.putln("if (%s < 0) {" % cname)
if negative_indices:
code.putln("%s += %s;" % (cname, shape.cname))
code.putln("if (%s) %s = %d;" % (
code.unlikely("%s < 0" % cname), tmp_cname, dim))
else:
code.putln("%s = %d;" % (tmp_cname, dim))
code.put("} else ")
# check bounds in positive direction
code.putln("if (%s) %s = %d;" % (
code.unlikely("%s >= %s" % (cname, shape.cname)),
tmp_cname, dim))
code.globalstate.use_utility_code(raise_indexerror_code)
code.putln("if (%s) {" % code.unlikely("%s != -1" % tmp_cname))
code.putln('__Pyx_RaiseBufferIndexError(%s);' % tmp_cname)
code.putln(code.error_goto(pos))
code.putln('}')
code.funcstate.release_temp(tmp_cname)
elif negative_indices:
# Only fix negative indices.
for signed, cname, shape in zip(index_signeds, index_cnames,
bufaux.shapevars):
if signed != 0:
code.putln("if (%s < 0) %s += %s;" % (cname, cname, shape.cname))
# Create buffer lookup and return it
# This is done via utility macros/inline functions, which vary
# according to the access mode used.
params = []
nd = entry.type.ndim
mode = entry.type.mode
if mode == 'full':
for i, s, o in zip(index_cnames, bufaux.stridevars, bufaux.suboffsetvars):
params.append(i)
params.append(s.cname)
params.append(o.cname)
funcname = "__Pyx_BufPtrFull%dd" % nd
funcgen = buf_lookup_full_code
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):
params.append(i)
params.append(s.cname)
# Make sure the utility code is available
code.globalstate.use_code_from(funcgen, name=funcname, nd=nd)
ptr_type = entry.type.buffer_ptr_type
ptrcode = "%s(%s, %s.buf, %s)" % (funcname,
ptr_type.declaration_code(""),
bufstruct,
", ".join(params))
return ptrcode
def use_empty_bufstruct_code(env, max_ndim):
code = dedent("""
Py_ssize_t __Pyx_zeros[] = {%s};
Py_ssize_t __Pyx_minusones[] = {%s};
""") % (", ".join(["0"] * max_ndim), ", ".join(["-1"] * max_ndim))
env.use_utility_code(UtilityCode(proto=code), "empty_bufstruct_code")
def buf_lookup_full_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, sub_o_ffset
macroargs = ", ".join(["i%d, s%d, o%d" % (i, i, i) for i in range(nd)])
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("""
static INLINE void* %s_imp(void* buf, %s) {
char* ptr = (char*)buf;
""") % (name, funcargs) + "".join([dedent("""\
ptr += s%d * i%d;
if (o%d >= 0) ptr = *((char**)ptr) + o%d;
""") % (i, i, i, i) for i in range(nd)]
) + "\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
#
def mangle_dtype_name(dtype):
# Use prefixes to seperate user defined types from builtins
# (consider "typedef float unsigned_int")
if dtype.is_pyobject:
return "object"
elif dtype.is_ptr:
return "ptr"
else:
if dtype.is_typedef or dtype.is_struct_or_union:
prefix = "nn_"
else:
prefix = ""
return prefix + dtype.declaration_code("").replace(" ", "_")
def get_typestringchecker(code, dtype):
"""
Returns the name of a typestring checker with the given type; emitting
it to code if needed.
"""
name = "__Pyx_CheckTypestring_%s" % mangle_dtype_name(dtype)
code.globalstate.use_code_from(create_typestringchecker,
name,
dtype=dtype)
return name
def create_typestringchecker(protocode, defcode, name, dtype):
def put_assert(cond, msg):
defcode.putln("if (!(%s)) {" % cond)
defcode.putln('PyErr_Format(PyExc_ValueError, "Buffer dtype mismatch (%s)", __Pyx_DescribeTokenInFormatString(ts));' % msg)
defcode.putln("return NULL;")
defcode.putln("}")
if dtype.is_error: return
simple = dtype.is_simple_buffer_dtype()
complex_possible = dtype.is_struct_or_union and dtype.can_be_complex()
# Cannot add utility code recursively...
if not simple:
dtype_t = dtype.declaration_code("")
protocode.globalstate.use_utility_code(parse_typestring_repeat_code)
fields = dtype.scope.var_entries
# divide fields into blocks of equal type (for repeat count)
field_blocks = [] # of (n, type, checkerfunc)
n = 0
prevtype = None
for f in fields:
if n and f.type != prevtype:
field_blocks.append((n, prevtype, get_typestringchecker(protocode, prevtype)))
n = 0
prevtype = f.type
n += 1
field_blocks.append((n, f.type, get_typestringchecker(protocode, f.type)))
protocode.putln("static const char* %s(const char* ts); /*proto*/" % name)
defcode.putln("static const char* %s(const char* ts) {" % name)
if simple:
defcode.putln("int ok;")
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
defcode.putln("if (*ts == '1') ++ts;")
if dtype.is_pyobject:
defcode.putln("ok = (*ts == 'O');")
else:
# Cannot trust declared size; but rely on int vs float and
# signed/unsigned to be correctly declared. Use a switch statement
# on all possible format codes to validate that the size is ok.
# (Note that many codes may map to same size, e.g. 'i' and 'l'
# may both be four bytes).
ctype = dtype.declaration_code("")
defcode.putln("switch (*ts) {")
if dtype.is_int:
types = [
('b', 'char'), ('h', 'short'), ('i', 'int'),
('l', 'long'), ('q', 'long long')
]
elif dtype.is_float:
types = [('f', 'float'), ('d', 'double'), ('g', 'long double')]
else:
assert False
if dtype.signed == 0:
for char, against in types:
defcode.putln("case '%s': ok = (sizeof(%s) == sizeof(unsigned %s) && (%s)-1 > 0); break;" %
(char.upper(), ctype, against, ctype))
else:
for char, against in types:
defcode.putln("case '%s': ok = (sizeof(%s) == sizeof(%s) && (%s)-1 < 0); break;" %
(char, ctype, against, ctype))
defcode.putln("default: ok = 0;")
defcode.putln("}")
put_assert("ok", "expected %s, got %%s" % dtype)
defcode.putln("++ts;")
elif complex_possible:
# Could be a struct representing a complex number, so allow
# for parsing a "Zf" spec.
real_t, imag_t = [x.type for x in fields]
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
defcode.putln("if (*ts == '1') ++ts;")
defcode.putln("if (*ts == 'Z') {")
if len(field_blocks) == 2:
# Different float type, sizeof check needed
defcode.putln("if (sizeof(%s) != sizeof(%s)) {" % (
real_t.declaration_code(""),
imag_t.declaration_code("")))
defcode.putln('PyErr_SetString(PyExc_ValueError, "Cannot store complex number in \'%s\' as \'%s\' differs from \'%s\' in size.");' % (
dtype, real_t, imag_t))
defcode.putln("return NULL;")
defcode.putln("}")
check_real, check_imag = [x[2] for x in field_blocks]
else:
assert len(field_blocks) == 1
check_real = check_imag = field_blocks[0][2]
defcode.putln("ts = %s(ts + 1); if (!ts) return NULL;" % check_real)
defcode.putln("} else {")
defcode.putln("ts = %s(ts); if (!ts) return NULL;" % check_real)
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
defcode.putln("ts = %s(ts); if (!ts) return NULL;" % check_imag)
defcode.putln("}")
else:
defcode.putln("int n, count;")
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
next_types = [x[1] for x in field_blocks[1:]] + ["end"]
for (n, type, checker), next_type in zip(field_blocks, next_types):
if n == 1:
defcode.putln("if (*ts == '1') ++ts;")
else:
defcode.putln("n = %d;" % n);
defcode.putln("do {")
defcode.putln("ts = __Pyx_ParseTypestringRepeat(ts, &count); n -= count;")
put_assert("n >= 0", "expected %s, got %%s" % next_type)
simple = type.is_simple_buffer_dtype()
if not simple:
put_assert("*ts == 'T' && *(ts+1) == '{'", "expected %s, got %%s" % type)
defcode.putln("ts += 2;")
defcode.putln("ts = %s(ts); if (!ts) return NULL;" % checker)
if not simple:
put_assert("*ts == '}'", "expected end of %s struct, got %%s" % type)
defcode.putln("++ts;")
if n > 1:
defcode.putln("} while (n > 0);");
defcode.putln("ts = __Pyx_ConsumeWhitespace(ts); if (!ts) return NULL;")
defcode.putln("return ts;")
defcode.putln("}")
def get_getbuffer_code(dtype, code):
"""
Generate a utility function for getting a buffer for the given dtype.
The function will:
- Call PyObject_GetBuffer
- Check that ndim matched the expected value
- Check that the format string is right
- Set suboffsets to all -1 if it is returned as NULL.
"""
name = "__Pyx_GetBuffer_%s" % mangle_dtype_name(dtype)
if not code.globalstate.has_code(name):
code.globalstate.use_utility_code(acquire_utility_code)
typestringchecker = get_typestringchecker(code, dtype)
dtype_name = str(dtype)
dtype_cname = dtype.declaration_code("")
utilcode = UtilityCode(proto = dedent("""
static int %s(PyObject* obj, Py_buffer* buf, int flags, int nd, int cast); /*proto*/
""") % name, impl = dedent("""
static int %(name)s(PyObject* obj, Py_buffer* buf, int flags, int nd, int cast) {
const char* ts;
if (obj == Py_None) {
__Pyx_ZeroBuffer(buf);
return 0;
}
buf->buf = NULL;
if (__Pyx_GetBuffer(obj, buf, flags) == -1) goto fail;
if (buf->ndim != nd) {
__Pyx_BufferNdimError(buf, nd);
goto fail;
}
if (!cast) {
ts = buf->format;
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) {
PyErr_Format(PyExc_ValueError,
"Buffer dtype mismatch (expected end, got %%s)",
__Pyx_DescribeTokenInFormatString(ts));
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;
return 0;
fail:;
__Pyx_ZeroBuffer(buf);
return -1;
}""") % locals())
code.globalstate.use_utility_code(utilcode, name)
return name
def use_py2_buffer_functions(env):
# Emulation of PyObject_GetBuffer and PyBuffer_Release for Python 2.
# For >= 2.6 we do double mode -- use the new buffer interface on objects
# which has the right tp_flags set, but emulation otherwise.
codename = "PyObject_GetBuffer" # just a representative unique key
# Search all types for __getbuffer__ overloads
types = []
visited_scopes = set()
def find_buffer_types(scope):
if scope in visited_scopes:
return
visited_scopes.add(scope)
for m in scope.cimported_modules:
find_buffer_types(m)
for e in scope.type_entries:
t = e.type
if t.is_extension_type:
release = get = None
for x in t.scope.pyfunc_entries:
if x.name == u"__getbuffer__": get = x.func_cname
elif x.name == u"__releasebuffer__": release = x.func_cname
if get:
types.append((t.typeptr_cname, get, release))
find_buffer_types(env)
code = dedent("""
#if PY_MAJOR_VERSION < 3
static int __Pyx_GetBuffer(PyObject *obj, Py_buffer *view, int flags) {
#if PY_VERSION_HEX >= 0x02060000
if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_HAVE_NEWBUFFER)
return PyObject_GetBuffer(obj, view, flags);
#endif
""")
if len(types) > 0:
clause = "if"
for t, get, release in types:
code += " %s (PyObject_TypeCheck(obj, %s)) return %s(obj, view, flags);\n" % (clause, t, get)
clause = "else if"
code += " else {\n"
code += dedent("""\
PyErr_Format(PyExc_TypeError, "'%100s' does not have the buffer interface", Py_TYPE(obj)->tp_name);
return -1;
""", 2)
if len(types) > 0: code += " }"
code += dedent("""
}
static void __Pyx_ReleaseBuffer(Py_buffer *view) {
PyObject* obj = view->obj;
if (obj) {
""")
if len(types) > 0:
clause = "if"
for t, get, release in types:
if release:
code += "%s (PyObject_TypeCheck(obj, %s)) %s(obj, view);" % (clause, t, release)
clause = "else if"
code += dedent("""
Py_DECREF(obj);
view->obj = NULL;
}
}
#endif
""")
env.use_utility_code(UtilityCode(
proto = dedent("""\
#if PY_MAJOR_VERSION < 3
static int __Pyx_GetBuffer(PyObject *obj, Py_buffer *view, int flags);
static void __Pyx_ReleaseBuffer(Py_buffer *view);
#else
#define __Pyx_GetBuffer PyObject_GetBuffer
#define __Pyx_ReleaseBuffer PyBuffer_Release
#endif
"""), impl = code), codename)
#
# Static utility code
#
# Utility function to set the right exception
# The caller should immediately goto_error
raise_indexerror_code = UtilityCode(
proto = """\
static void __Pyx_RaiseBufferIndexError(int axis); /*proto*/
""",
impl = """\
static void __Pyx_RaiseBufferIndexError(int axis) {
PyErr_Format(PyExc_IndexError,
"Out of bounds on buffer access (axis %d)", axis);
}
""")
#
# Buffer type checking. Utility code for checking that acquired
# buffers match our assumptions. We only need to check ndim and
# the format string; the access mode/flags is checked by the
# exporter.
#
acquire_utility_code = UtilityCode(
proto = """\
static INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info);
static INLINE void __Pyx_ZeroBuffer(Py_buffer* buf); /*proto*/
static INLINE const char* __Pyx_ConsumeWhitespace(const char* ts); /*proto*/
static void __Pyx_BufferNdimError(Py_buffer* buffer, int expected_ndim); /*proto*/
static const char* __Pyx_DescribeTokenInFormatString(const char* ts); /*proto*/
""",
impl = """
static INLINE void __Pyx_SafeReleaseBuffer(Py_buffer* info) {
if (info->buf == NULL) return;
if (info->suboffsets == __Pyx_minusones) info->suboffsets = NULL;
__Pyx_ReleaseBuffer(info);
}
static INLINE void __Pyx_ZeroBuffer(Py_buffer* buf) {
buf->buf = NULL;
buf->obj = NULL;
buf->strides = __Pyx_zeros;
buf->shape = __Pyx_zeros;
buf->suboffsets = __Pyx_minusones;
}
static INLINE const char* __Pyx_ConsumeWhitespace(const char* ts) {
while (1) {
switch (*ts) {
case '@':
case 10:
case 13:
case ' ':
++ts;
break;
case '=':
case '<':
case '>':
case '!':
PyErr_SetString(PyExc_ValueError, "Buffer acquisition error: Only native byte order, size and alignment supported.");
return NULL;
default:
return ts;
}
}
}
static void __Pyx_BufferNdimError(Py_buffer* buffer, int expected_ndim) {
PyErr_Format(PyExc_ValueError,
"Buffer has wrong number of dimensions (expected %d, got %d)",
expected_ndim, buffer->ndim);
}
static const char* __Pyx_DescribeTokenInFormatString(const char* ts) {
switch (*ts) {
case 'b': return "char";
case 'B': return "unsigned char";
case 'h': return "short";
case 'H': return "unsigned short";
case 'i': return "int";
case 'I': return "unsigned int";
case 'l': return "long";
case 'L': return "unsigned long";
case 'q': return "long long";
case 'Q': return "unsigned long long";
case 'f': return "float";
case 'd': return "double";
case 'g': return "long double";
case 'Z': switch (*(ts+1)) {
case 'f': return "complex float";
case 'd': return "complex double";
case 'g': return "complex long double";
default: return "unparseable format string";
}
case 'T': return "a struct";
case 'O': return "Python object";
case 'P': return "a pointer";
default: return "unparseable format string";
}
}
""")
parse_typestring_repeat_code = UtilityCode(
proto = """
static INLINE const char* __Pyx_ParseTypestringRepeat(const char* ts, int* out_count); /*proto*/
""",
impl = """
static INLINE const char* __Pyx_ParseTypestringRepeat(const char* ts, int* out_count) {
int count;
if (*ts < '0' || *ts > '9') {
count = 1;
} else {
count = *ts++ - '0';
while (*ts >= '0' && *ts < '9') {
count *= 10;
count += *ts++ - '0';
}
}
*out_count = count;
return ts;
}
""")
raise_buffer_fallback_code = UtilityCode(
proto = """
static void __Pyx_RaiseBufferFallbackError(void); /*proto*/
""",
impl = """
static void __Pyx_RaiseBufferFallbackError(void) {
PyErr_Format(PyExc_ValueError,
"Buffer acquisition failed on assignment; and then reacquiring the old buffer failed too!");
}
""")
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/Builtin.py 0000664 0000000 0000000 00000027373 11136717635 0025134 0 ustar 00root root 0000000 0000000 #
# Pyrex - Builtin Definitions
#
from Symtab import BuiltinScope, StructOrUnionScope
from Cython.Utils import UtilityCode
from TypeSlots import Signature
import PyrexTypes
import Naming
builtin_function_table = [
# name, args, return, C API func, py equiv = "*"
('abs', "O", "O", "PyNumber_Absolute"),
#('chr', "", "", ""),
#('cmp', "", "", "", ""), # int PyObject_Cmp(PyObject *o1, PyObject *o2, int *result)
#('compile', "", "", ""), # PyObject* Py_CompileString( char *str, char *filename, int start)
('delattr', "OO", "r", "PyObject_DelAttr"),
('dir', "O", "O", "PyObject_Dir"),
('divmod', "OO", "O", "PyNumber_Divmod"),
('exec', "OOO", "O", "__Pyx_PyRun"),
#('eval', "", "", ""),
#('execfile', "", "", ""),
#('filter', "", "", ""),
('getattr', "OO", "O", "PyObject_GetAttr"),
('getattr3', "OOO", "O", "__Pyx_GetAttr3", "getattr"),
('hasattr', "OO", "b", "PyObject_HasAttr"),
('hash', "O", "l", "PyObject_Hash"),
#('hex', "", "", ""),
#('id', "", "", ""),
#('input', "", "", ""),
('intern', "s", "O", "__Pyx_InternFromString"),
('isinstance', "OO", "b", "PyObject_IsInstance"),
('issubclass', "OO", "b", "PyObject_IsSubclass"),
('iter', "O", "O", "PyObject_GetIter"),
('len', "O", "Z", "PyObject_Length"),
('locals', "", "O", "__pyx_locals"),
#('map', "", "", ""),
#('max', "", "", ""),
#('min', "", "", ""),
#('oct', "", "", ""),
# Not worth doing open, when second argument would become mandatory
#('open', "ss", "O", "PyFile_FromString"),
#('ord', "", "", ""),
('pow', "OOO", "O", "PyNumber_Power"),
#('range', "", "", ""),
#('raw_input', "", "", ""),
#('reduce', "", "", ""),
('reload', "O", "O", "PyImport_ReloadModule"),
('repr', "O", "O", "PyObject_Repr"),
#('round', "", "", ""),
('setattr', "OOO", "r", "PyObject_SetAttr"),
#('sum', "", "", ""),
#('unichr', "", "", ""),
#('unicode', "", "", ""),
#('vars', "", "", ""),
#('zip', "", "", ""),
# Can't do these easily until we have builtin type entries.
#('typecheck', "OO", "i", "PyObject_TypeCheck", False),
#('issubtype', "OO", "i", "PyType_IsSubtype", False),
# Put in namespace append optimization.
('__Pyx_PyObject_Append', "OO", "O", "__Pyx_PyObject_Append"),
]
# Builtin types
# bool
# buffer
# classmethod
# dict
# enumerate
# file
# float
# int
# list
# long
# object
# property
# slice
# staticmethod
# super
# str
# tuple
# type
# xrange
builtin_types_table = [
("type", "PyType_Type", []),
("bool", "PyBool_Type", []),
("int", "PyInt_Type", []),
("long", "PyLong_Type", []),
("float", "PyFloat_Type", []),
("complex", "PyComplex_Type", []),
("bytes", "PyBytes_Type", []),
("str", "PyString_Type", []),
("unicode", "PyUnicode_Type", []),
("tuple", "PyTuple_Type", []),
("list", "PyList_Type", [("append", "OO", "i", "PyList_Append"),
("insert", "OZO", "i", "PyList_Insert"),
# ("sort", "O", "i", "PyList_Sort"), # has optional arguments
("reverse","O", "i", "PyList_Reverse")]),
("dict", "PyDict_Type", [("items", "O", "O", "PyDict_Items"),
("keys", "O", "O", "PyDict_Keys"),
("values","O", "O", "PyDict_Values"),
("copy", "O", "O", "PyDict_Copy")]),
("slice", "PySlice_Type", []),
("file", "PyFile_Type", []),
("set", "PySet_Type", [("clear", "O", "i", "PySet_Clear"),
("discard", "OO", "i", "PySet_Discard"),
("add", "OO", "i", "PySet_Add"),
("pop", "O", "O", "PySet_Pop")]),
("frozenset", "PyFrozenSet_Type", []),
]
builtin_structs_table = [
('Py_buffer', 'Py_buffer',
[("buf", PyrexTypes.c_void_ptr_type),
("obj", PyrexTypes.py_object_type),
("len", PyrexTypes.c_py_ssize_t_type),
("itemsize", PyrexTypes.c_py_ssize_t_type),
("readonly", PyrexTypes.c_bint_type),
("ndim", PyrexTypes.c_int_type),
("format", PyrexTypes.c_char_ptr_type),
("shape", PyrexTypes.c_py_ssize_t_ptr_type),
("strides", PyrexTypes.c_py_ssize_t_ptr_type),
("suboffsets", PyrexTypes.c_py_ssize_t_ptr_type),
("internal", PyrexTypes.c_void_ptr_type),
])
]
getattr3_utility_code = UtilityCode(
proto = """
static PyObject *__Pyx_GetAttr3(PyObject *, PyObject *, PyObject *); /*proto*/
""",
impl = """
static PyObject *__Pyx_GetAttr3(PyObject *o, PyObject *n, PyObject *d) {
PyObject *r = PyObject_GetAttr(o, n);
if (!r) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
goto bad;
PyErr_Clear();
r = d;
Py_INCREF(d);
}
return r;
bad:
return 0;
}
""")
pyexec_utility_code = UtilityCode(
proto = """
static PyObject* __Pyx_PyRun(PyObject*, PyObject*, PyObject*);
""",
impl = """
static PyObject* __Pyx_PyRun(PyObject* o, PyObject* globals, PyObject* locals) {
PyObject* result;
PyObject* s = 0;
if (!locals && !globals) {
globals = PyModule_GetDict(%s);""" % Naming.module_cname + """
if (!globals)
goto bad;
locals = globals;
} else if (!locals) {
locals = globals;
} else if (!globals) {
globals = locals;
}
if (PyUnicode_Check(o)) {
s = PyUnicode_AsUTF8String(o);
if (!s) goto bad;
o = s;
#if PY_MAJOR_VERSION >= 3
} else if (!PyBytes_Check(o)) {
#else
} else if (!PyString_Check(o)) {
#endif
/* FIXME: support file objects and code objects */
PyErr_SetString(PyExc_TypeError,
"exec currently requires a string as code input.");
goto bad;
}
result = PyRun_String(
#if PY_MAJOR_VERSION >= 3
PyBytes_AS_STRING(o),
#else
PyString_AS_STRING(o),
#endif
Py_file_input, globals, locals);
Py_XDECREF(s);
return result;
bad:
Py_XDECREF(s);
return 0;
}
""")
intern_utility_code = UtilityCode(
proto = """
#if PY_MAJOR_VERSION >= 3
# define __Pyx_InternFromString(s) PyUnicode_InternFromString(s)
#else
# define __Pyx_InternFromString(s) PyString_InternFromString(s)
#endif
""")
def put_py23_set_init_utility_code(code, pos):
code.putln("#if PY_VERSION_HEX < 0x02040000")
code.putln(code.error_goto_if_neg("__Pyx_Py23SetsImport()", pos))
code.putln("#endif")
py23_set_utility_code = UtilityCode(
proto = """
#if PY_VERSION_HEX < 0x02050000
#ifndef PyAnySet_CheckExact
#define PyAnySet_CheckExact(ob) \\
((ob)->ob_type == &PySet_Type || \\
(ob)->ob_type == &PyFrozenSet_Type)
#define PySet_New(iterable) \\
PyObject_CallFunctionObjArgs((PyObject *)&PySet_Type, (iterable), NULL)
#define Pyx_PyFrozenSet_New(iterable) \\
PyObject_CallFunctionObjArgs((PyObject *)&PyFrozenSet_Type, (iterable), NULL)
#define PySet_Size(anyset) \\
PyObject_Size((anyset))
#define PySet_Contains(anyset, key) \\
PySequence_Contains((anyset), (key))
#define PySet_Pop(set) \\
PyObject_CallMethod(set, (char *)"pop", NULL)
static INLINE int PySet_Clear(PyObject *set) {
PyObject *ret = PyObject_CallMethod(set, (char *)"clear", NULL);
if (!ret) return -1;
Py_DECREF(ret); return 0;
}
static INLINE int PySet_Discard(PyObject *set, PyObject *key) {
PyObject *ret = PyObject_CallMethod(set, (char *)"discard", (char *)"O", key);
if (!ret) return -1;
Py_DECREF(ret); return 0;
}
static INLINE int PySet_Add(PyObject *set, PyObject *key) {
PyObject *ret = PyObject_CallMethod(set, (char *)"add", (char *)"O", key);
if (!ret) return -1;
Py_DECREF(ret); return 0;
}
#endif /* PyAnySet_CheckExact (<= Py2.4) */
#if PY_VERSION_HEX < 0x02040000
#ifndef Py_SETOBJECT_H
#define Py_SETOBJECT_H
static PyTypeObject *__Pyx_PySet_Type = NULL;
static PyTypeObject *__Pyx_PyFrozenSet_Type = NULL;
#define PySet_Type (*__Pyx_PySet_Type)
#define PyFrozenSet_Type (*__Pyx_PyFrozenSet_Type)
#define PyAnySet_Check(ob) \\
(PyAnySet_CheckExact(ob) || \\
PyType_IsSubtype((ob)->ob_type, &PySet_Type) || \\
PyType_IsSubtype((ob)->ob_type, &PyFrozenSet_Type))
#define PyFrozenSet_CheckExact(ob) ((ob)->ob_type == &PyFrozenSet_Type)
static int __Pyx_Py23SetsImport(void) {
PyObject *sets=0, *Set=0, *ImmutableSet=0;
sets = PyImport_ImportModule((char *)"sets");
if (!sets) goto bad;
Set = PyObject_GetAttrString(sets, (char *)"Set");
if (!Set) goto bad;
ImmutableSet = PyObject_GetAttrString(sets, (char *)"ImmutableSet");
if (!ImmutableSet) goto bad;
Py_DECREF(sets);
__Pyx_PySet_Type = (PyTypeObject*) Set;
__Pyx_PyFrozenSet_Type = (PyTypeObject*) ImmutableSet;
return 0;
bad:
Py_XDECREF(sets);
Py_XDECREF(Set);
Py_XDECREF(ImmutableSet);
return -1;
}
#else
static int __Pyx_Py23SetsImport(void) { return 0; }
#endif /* !Py_SETOBJECT_H */
#endif /* < Py2.4 */
#endif /* < Py2.5 */
""",
init = put_py23_set_init_utility_code,
cleanup = """
#if PY_VERSION_HEX < 0x02040000
Py_XDECREF(__Pyx_PySet_Type); __Pyx_PySet_Type = NULL;
Py_XDECREF(__Pyx_PyFrozenSet_Type); __Pyx_PyFrozenSet_Type = NULL;
#endif /* < Py2.4 */
""")
builtin_utility_code = {
'exec' : pyexec_utility_code,
'getattr3' : getattr3_utility_code,
'intern' : intern_utility_code,
'set' : py23_set_utility_code,
'frozenset' : py23_set_utility_code,
}
builtin_scope = BuiltinScope()
def declare_builtin_func(name, args, ret, cname, py_equiv = "*"):
sig = Signature(args, ret)
type = sig.function_type()
utility = builtin_utility_code.get(name)
builtin_scope.declare_builtin_cfunction(name, type, cname, py_equiv, utility)
def init_builtin_funcs():
for desc in builtin_function_table:
declare_builtin_func(*desc)
def init_builtin_types():
for name, cname, funcs in builtin_types_table:
utility = builtin_utility_code.get(name)
the_type = builtin_scope.declare_builtin_type(name, cname, utility)
for name, args, ret, cname in funcs:
sig = Signature(args, ret)
the_type.scope.declare_cfunction(name, sig.function_type(), None, cname)
def init_builtin_structs():
for name, cname, attribute_types in builtin_structs_table:
scope = StructOrUnionScope(name)
for attribute_name, attribute_type in attribute_types:
scope.declare_var(
attribute_name, attribute_type, None, attribute_name)
builtin_scope.declare_struct_or_union(
name, "struct", scope, 1, None, cname = cname)
def init_builtins():
init_builtin_funcs()
init_builtin_types()
init_builtin_structs()
global list_type, tuple_type, dict_type, set_type, unicode_type, type_type
type_type = builtin_scope.lookup('type').type
list_type = builtin_scope.lookup('list').type
tuple_type = builtin_scope.lookup('tuple').type
dict_type = builtin_scope.lookup('dict').type
set_type = builtin_scope.lookup('set').type
unicode_type = builtin_scope.lookup('unicode').type
init_builtins()
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/CmdLine.py 0000664 0000000 0000000 00000014433 11136717635 0025032 0 ustar 00root root 0000000 0000000 #
# Cython - Command Line Parsing
#
import sys
import Options
usage = """\
Cython (http://cython.org) is a compiler for code written in the
Cython language. Cython is based on Pyrex by Greg Ewing.
Usage: cython [options] sourcefile.pyx ...
Options:
-V, --version Display version number of cython compiler
-l, --create-listing Write error messages to a listing file
-I, --include-dir Search for include files in named directory
(multiply include directories are allowed).
-o, --output-file Specify name of generated C file
-r, --recursive Recursively find and compile dependencies
-t, --timestamps Only compile newer source files (implied with -r)
-f, --force Compile all source files (overrides implied -t)
-q, --quiet Don't print module names in recursive mode
-v, --verbose Be verbose, print file names on multiple compilation
-p, --embed-positions If specified, the positions in Cython files of each
function definition is embedded in its docstring.
-z, --pre-import If specified, assume undeclared names in this
module. Emulates the behavior of putting
"from import *" at the top of the file.
--incref-local-binop Force local an extra incref on local variables before
performing any binary operations.
--cleanup Release interned objects on python exit, for memory debugging.
Level indicates aggressiveness, default 0 releases nothing.
-w, --working Sets the working directory for Cython (the directory modules
are searched from)
-D, --no-docstrings Remove docstrings.
-a, --annotate Produce a colorized HTML version of the source.
--line-directives Produce #line directives pointing to the .pyx source
--cplus Output a c++ rather than c file.
-X, --directive =[, 1:
sys.stderr.write(
"cython: Only one source file allowed together with .o files\n")
if options.use_listing_file and len(sources) > 1:
sys.stderr.write(
"cython: Only one source file allowed when using -o\n")
sys.exit(1)
if len(sources) == 0 and not options.show_version:
bad_usage()
return options, sources
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/Code.py 0000664 0000000 0000000 00000105001 11136717635 0024361 0 ustar 00root root 0000000 0000000 #
# Pyrex - Code output module
#
import codecs
import Naming
import Options
from Cython.Utils import open_new_file, open_source_file
from PyrexTypes import py_object_type, typecast
from TypeSlots import method_coexist
from Scanning import SourceDescriptor
from Cython.StringIOTree import StringIOTree
import DebugFlags
try:
set
except NameError:
from sets import Set as set
import DebugFlags
class FunctionState(object):
# return_label string function return point label
# error_label string error catch point label
# continue_label string loop continue point label
# break_label string loop break point label
# return_from_error_cleanup_label string
# label_counter integer counter for naming labels
# in_try_finally boolean inside try of try...finally
# exc_vars (string * 3) exception variables for reraise, or None
# Not used for now, perhaps later
def __init__(self, owner, names_taken=set()):
self.names_taken = names_taken
self.owner = owner
self.error_label = None
self.label_counter = 0
self.labels_used = {}
self.return_label = self.new_label()
self.new_error_label()
self.continue_label = None
self.break_label = None
self.in_try_finally = 0
self.exc_vars = None
self.temps_allocated = [] # of (name, type, manage_ref)
self.temps_free = {} # (type, manage_ref) -> list of free vars with same type/managed status
self.temps_used_type = {} # name -> (type, manage_ref)
self.temp_counter = 0
def new_label(self, name=None):
n = self.label_counter
self.label_counter = n + 1
label = "%s%d" % (Naming.label_prefix, n)
if name is not None:
label += '_' + name
return label
def new_error_label(self):
old_err_lbl = self.error_label
self.error_label = self.new_label('error')
return old_err_lbl
def get_loop_labels(self):
return (
self.continue_label,
self.break_label)
def set_loop_labels(self, labels):
(self.continue_label,
self.break_label) = labels
def new_loop_labels(self):
old_labels = self.get_loop_labels()
self.set_loop_labels(
(self.new_label(),
self.new_label()))
return old_labels
def get_all_labels(self):
return (
self.continue_label,
self.break_label,
self.return_label,
self.error_label)
def set_all_labels(self, labels):
(self.continue_label,
self.break_label,
self.return_label,
self.error_label) = labels
def all_new_labels(self):
old_labels = self.get_all_labels()
new_labels = []
for old_label in old_labels:
if old_label:
new_labels.append(self.new_label())
else:
new_labels.append(old_label)
self.set_all_labels(new_labels)
return old_labels
def use_label(self, lbl):
self.labels_used[lbl] = 1
def label_used(self, lbl):
return lbl in self.labels_used
def allocate_temp(self, type, manage_ref):
"""
Allocates a temporary (which may create a new one or get a previously
allocated and released one of the same type). Type is simply registered
and handed back, but will usually be a PyrexType.
If type.is_pyobject, manage_ref comes into play. If manage_ref is set to
True, the temp will be decref-ed on return statements and in exception
handling clauses. Otherwise the caller has to deal with any reference
counting of the variable.
If not type.is_pyobject, then manage_ref will be ignored, but it
still has to be passed. It is recommended to pass False by convention
if it is known that type will never be a Python object.
A C string referring to the variable is returned.
"""
if not type.is_pyobject:
# Make manage_ref canonical, so that manage_ref will always mean
# a decref is needed.
manage_ref = False
freelist = self.temps_free.get((type, manage_ref))
if freelist is not None and len(freelist) > 0:
result = freelist.pop()
else:
while True:
self.temp_counter += 1
result = "%s%d" % (Naming.codewriter_temp_prefix, self.temp_counter)
if not result in self.names_taken: break
self.temps_allocated.append((result, type, manage_ref))
self.temps_used_type[result] = (type, manage_ref)
if DebugFlags.debug_temp_code_comments:
self.owner.putln("/* %s allocated */" % result)
return result
def release_temp(self, name):
"""
Releases a temporary so that it can be reused by other code needing
a temp of the same type.
"""
type, manage_ref = self.temps_used_type[name]
freelist = self.temps_free.get((type, manage_ref))
if freelist is None:
freelist = []
self.temps_free[(type, manage_ref)] = freelist
if name in freelist:
raise RuntimeError("Temp %s freed twice!" % name)
freelist.append(name)
if DebugFlags.debug_temp_code_comments:
self.owner.putln("/* %s released */" % name)
def temps_in_use(self):
"""Return a list of (cname,type,manage_ref) tuples of temp names and their type
that are currently in use.
"""
used = []
for name, type, manage_ref in self.temps_allocated:
freelist = self.temps_free.get((type, manage_ref))
if freelist is None or name not in freelist:
used.append((name, type, manage_ref))
return used
def temps_holding_reference(self):
"""Return a list of (cname,type) tuples of temp names and their type
that are currently in use. This includes only temps of a
Python object type which owns its reference.
"""
return [(name, type)
for name, type, manage_ref in self.temps_in_use()
if manage_ref]
def all_managed_temps(self):
"""Return a list of (cname, type) tuples of refcount-managed Python objects.
"""
return [(cname, type)
for cname, type, manage_ref in self.temps_allocated
if manage_ref]
def all_free_managed_temps(self):
"""Return a list of (cname, type) tuples of refcount-managed Python
objects that are not currently in use. This is used by
try-except and try-finally blocks to clean up temps in the
error case.
"""
return [(cname, type)
for (type, manage_ref), freelist in self.temps_free.iteritems()
if manage_ref
for cname in freelist]
class GlobalState(object):
# filename_table {string : int} for finding filename table indexes
# filename_list [string] filenames in filename table order
# input_file_contents dict contents (=list of lines) of any file that was used as input
# to create this output C code. This is
# used to annotate the comments.
#
# used_utility_code set(string|int) Ids of used utility code (to avoid reinsertion)
# utilprotowriter CCodeWriter
# utildefwriter CCodeWriter
#
# declared_cnames {string:Entry} used in a transition phase to merge pxd-declared
# constants etc. into the pyx-declared ones (i.e,
# check if constants are already added).
# In time, hopefully the literals etc. will be
# supplied directly instead.
# interned_strings
# consts
# py_string_decls
# interned_nums
# cached_builtins
# 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_list = []
self.input_file_contents = {}
self.used_utility_code = set()
self.declared_cnames = {}
self.pystring_table_needed = False
self.in_utility_code_generation = False
self.emit_linenums = emit_linenums
def initwriters(self, rootwriter):
self.utilprotowriter = rootwriter.new_writer()
self.utildefwriter = rootwriter.new_writer()
self.decls_writer = rootwriter.new_writer()
self.pystring_table = rootwriter.new_writer()
self.init_cached_builtins_writer = rootwriter.new_writer()
self.initwriter = rootwriter.new_writer()
self.cleanupwriter = rootwriter.new_writer()
if Options.cache_builtins:
self.init_cached_builtins_writer.enter_cfunc_scope()
self.init_cached_builtins_writer.putln("static int __Pyx_InitCachedBuiltins(void) {")
self.initwriter.enter_cfunc_scope()
self.initwriter.putln("")
self.initwriter.putln("static int __Pyx_InitGlobals(void) {")
self.cleanupwriter.enter_cfunc_scope()
self.cleanupwriter.putln("")
self.cleanupwriter.putln("static void __Pyx_CleanupGlobals(void) {")
self.pystring_table.putln("")
self.pystring_table.putln("static __Pyx_StringTabEntry %s[] = {" %
Naming.stringtab_cname)
#
# Global constants, interned objects, etc.
#
def insert_global_var_declarations_into(self, code):
code.insert(self.decls_writer)
def close_global_decls(self):
# This is called when it is known that no more global declarations will
# declared (but can be called before or after insert_XXX).
if self.pystring_table_needed:
self.pystring_table.putln("{0, 0, 0, 0, 0, 0}")
self.pystring_table.putln("};")
import Nodes
self.use_utility_code(Nodes.init_string_tab_utility_code)
self.initwriter.putln(
"if (__Pyx_InitStrings(%s) < 0) %s;" % (
Naming.stringtab_cname,
self.initwriter.error_goto(self.module_pos)))
if Options.cache_builtins:
w = self.init_cached_builtins_writer
w.putln("return 0;")
w.put_label(w.error_label)
w.putln("return -1;")
w.putln("}")
w.exit_cfunc_scope()
w = self.initwriter
w.putln("return 0;")
w.put_label(w.error_label)
w.putln("return -1;")
w.putln("}")
w.exit_cfunc_scope()
w = self.cleanupwriter
w.putln("}")
w.exit_cfunc_scope()
def insert_initcode_into(self, code):
if self.pystring_table_needed:
code.insert(self.pystring_table)
if Options.cache_builtins:
code.insert(self.init_cached_builtins_writer)
code.insert(self.initwriter)
def insert_cleanupcode_into(self, code):
code.insert(self.cleanupwriter)
def put_pyobject_decl(self, entry):
self.decls_writer.putln("static PyObject *%s;" % entry.cname)
# The functions below are there in a transition phase only
# and will be deprecated. They are called from Nodes.BlockNode.
# The copy&paste duplication is intentional in order to be able
# to see quickly how BlockNode worked, until this is replaced.
def should_declare(self, cname, entry):
if cname in self.declared_cnames:
other = self.declared_cnames[cname]
assert entry.type == other.type
assert entry.init == other.init
return False
else:
self.declared_cnames[cname] = entry
return True
def add_const_definition(self, entry):
if self.should_declare(entry.cname, entry):
self.decls_writer.put_var_declaration(entry, static = 1)
def add_interned_string_decl(self, entry):
if self.should_declare(entry.cname, entry):
self.decls_writer.put_var_declaration(entry, static = 1)
self.add_py_string_decl(entry)
def add_py_string_decl(self, entry):
if self.should_declare(entry.pystring_cname, entry):
self.decls_writer.putln("static PyObject *%s;" % entry.pystring_cname)
self.pystring_table_needed = True
self.pystring_table.putln("{&%s, %s, sizeof(%s), %d, %d, %d}," % (
entry.pystring_cname,
entry.cname,
entry.cname,
entry.type.is_unicode,
entry.is_interned,
entry.is_identifier
))
def add_interned_num_decl(self, entry):
if self.should_declare(entry.cname, entry):
if entry.init[-1] == "L":
self.initwriter.putln('%s = PyLong_FromString((char *)"%s", 0, 0); %s;' % (
entry.cname,
entry.init,
self.initwriter.error_goto_if_null(entry.cname, self.module_pos)))
else:
self.initwriter.putln("%s = PyInt_FromLong(%s); %s;" % (
entry.cname,
entry.init,
self.initwriter.error_goto_if_null(entry.cname, self.module_pos)))
self.put_pyobject_decl(entry)
def add_cached_builtin_decl(self, entry):
if Options.cache_builtins:
if self.should_declare(entry.cname, entry):
self.put_pyobject_decl(entry)
self.init_cached_builtins_writer.putln('%s = __Pyx_GetName(%s, %s); if (!%s) %s' % (
entry.cname,
Naming.builtins_cname,
entry.interned_cname,
entry.cname,
self.init_cached_builtins_writer.error_goto(entry.pos)))
#
# File name state
#
def lookup_filename(self, filename):
try:
index = self.filename_table[filename]
except KeyError:
index = len(self.filename_list)
self.filename_list.append(filename)
self.filename_table[filename] = index
return index
def commented_file_contents(self, source_desc):
try:
return self.input_file_contents[source_desc]
except KeyError:
F = [u' * ' + line.rstrip().replace(
u'*/', u'*[inserted by cython to avoid comment closer]/'
).replace(
u'/*', u'/[inserted by cython to avoid comment start]*'
).encode('ASCII', 'replace') # + Py2 auto-decode to unicode
for line in source_desc.get_lines()]
if len(F) == 0: F.append(u'')
self.input_file_contents[source_desc] = F
return F
#
# Utility code state
#
def use_utility_code(self, utility_code, name=None):
"""
Adds the given utility code to the C file if needed.
codetup should unpack into one prototype code part and one
definition code part, both strings inserted directly in C.
If name is provided, it is used as an identifier to avoid inserting
code twice. Otherwise, id(codetup) is used as such an identifier.
"""
if name is None: name = id(utility_code)
if self.check_utility_code_needed_and_register(name):
if utility_code.proto:
self.utilprotowriter.put(utility_code.proto)
if utility_code.impl:
self.utildefwriter.put(utility_code.impl)
utility_code.write_init_code(self.initwriter, self.module_pos)
utility_code.write_cleanup_code(self.cleanupwriter, self.module_pos)
def has_code(self, name):
return name in self.used_utility_code
def use_code_from(self, func, name, *args, **kw):
"""
Requests that the utility code that func can generate is used in the C
file. func is called like this:
func(proto, definition, name, *args, **kw)
where proto and definition are two CCodeWriter instances; the
former should have the prototype written to it and the other the definition.
The call might happen at some later point (if compiling multiple modules
into a cache for instance), and will only happen once per utility code.
name is used to identify the utility code, so that it isn't regenerated
when the same code is requested again.
"""
if self.check_utility_code_needed_and_register(name):
func(self.utilprotowriter, self.utildefwriter,
name, *args, **kw)
def check_utility_code_needed_and_register(self, name):
if name in self.used_utility_code:
return False
else:
self.used_utility_code.add(name)
return True
def put_utility_code_protos(self, writer):
writer.insert(self.utilprotowriter)
def put_utility_code_defs(self, writer):
if self.emit_linenums:
writer.write('\n#line 1 "cython_utility"\n')
writer.insert(self.utildefwriter)
def funccontext_property(name):
def get(self):
return getattr(self.funcstate, name)
def set(self, value):
setattr(self.funcstate, name, value)
return property(get, set)
class CCodeWriter(object):
"""
Utility class to output C code.
When creating an insertion point one must care about the state that is
kept:
- formatting state (level, bol) is cloned and used in insertion points
as well
- labels, temps, exc_vars: One must construct a scope in which these can
exist by calling enter_cfunc_scope/exit_cfunc_scope (these are for
sanity checking and forward compatabilty). Created insertion points
looses this scope and cannot access it.
- marker: Not copied to insertion point
- filename_table, filename_list, input_file_contents: All codewriters
coming from the same root share the same instances simultaneously.
"""
# f file output file
# buffer StringIOTree
# level int indentation level
# bol bool beginning of line?
# marker string comment to emit before next line
# funcstate FunctionState contains state local to a C function used for code
# generation (labels and temps state etc.)
# globalstate GlobalState contains state global for a C file (input file info,
# 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, emit_linenums=None):
if buffer is None: buffer = StringIOTree()
self.buffer = buffer
self.marker = None
self.last_marker_line = 0
self.source_desc = ""
self.funcstate = None
self.level = 0
self.call_level = 0
self.bol = 1
if create_from is None:
# Root CCodeWriter
self.globalstate = GlobalState(self, emit_linenums=emit_linenums)
self.globalstate.initwriters(self)
# ^^^ need seperate step because this will reference self.globalstate
else:
# Use same global state
self.globalstate = create_from.globalstate
# Clone formatting state
if copy_formatting:
self.level = create_from.level
self.bol = create_from.bol
self.call_level = create_from.call_level
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):
# polymorphic constructor -- very slightly more versatile
# than using __class__
return CCodeWriter(create_from, buffer, copy_formatting)
def copyto(self, f):
self.buffer.copyto(f)
def getvalue(self):
return self.buffer.getvalue()
def write(self, s):
self.buffer.write(s)
def insertion_point(self):
other = self.create_new(create_from=self, buffer=self.buffer.insertion_point(), copy_formatting=True)
return other
def new_writer(self):
"""
Creates a new CCodeWriter connected to the same global state, which
can later be inserted using insert.
"""
return CCodeWriter(create_from=self)
def insert(self, writer):
"""
Inserts the contents of another code writer (created with
the same global state) in the current location.
It is ok to write to the inserted writer also after insertion.
"""
assert writer.globalstate is self.globalstate
self.buffer.insert(writer.buffer)
# Properties delegated to function scope
label_counter = funccontext_property("label_counter")
return_label = funccontext_property("return_label")
error_label = funccontext_property("error_label")
labels_used = funccontext_property("labels_used")
continue_label = funccontext_property("continue_label")
break_label = funccontext_property("break_label")
return_from_error_cleanup_label = funccontext_property("return_from_error_cleanup_label")
# Functions delegated to function scope
def new_label(self, name=None): return self.funcstate.new_label(name)
def new_error_label(self): return self.funcstate.new_error_label()
def get_loop_labels(self): return self.funcstate.get_loop_labels()
def set_loop_labels(self, labels): return self.funcstate.set_loop_labels(labels)
def new_loop_labels(self): return self.funcstate.new_loop_labels()
def get_all_labels(self): return self.funcstate.get_all_labels()
def set_all_labels(self, labels): return self.funcstate.set_all_labels(labels)
def all_new_labels(self): return self.funcstate.all_new_labels()
def use_label(self, lbl): return self.funcstate.use_label(lbl)
def label_used(self, lbl): return self.funcstate.label_used(lbl)
def enter_cfunc_scope(self):
self.funcstate = FunctionState(self)
def exit_cfunc_scope(self):
self.funcstate = None
def putln(self, code = ""):
if self.marker and self.bol:
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:
self.put(code)
self.write("\n");
self.bol = 1
def emit_marker(self):
self.write("\n");
self.indent()
self.write("/* %s */\n" % self.marker[1])
self.last_marker_line = self.marker[0]
self.marker = None
def put_safe(self, code):
# put code, but ignore {}
self.write(code)
self.bol = 0
def put(self, code):
fix_indent = False
if "{" in code:
dl = code.count("{")
else:
dl = 0
if "}" in code:
dl -= code.count("}")
if dl < 0:
self.level += dl
elif dl == 0 and code[0] == "}":
# special cases like "} else {" need a temporary dedent
fix_indent = True
self.level -= 1
if self.bol:
self.indent()
self.write(code)
self.bol = 0
if dl > 0:
self.level += dl
elif fix_indent:
self.level += 1
def increase_indent(self):
self.level = self.level + 1
def decrease_indent(self):
self.level = self.level - 1
def begin_block(self):
self.putln("{")
self.increase_indent()
def end_block(self):
self.decrease_indent()
self.putln("}")
def indent(self):
self.write(" " * self.level)
def get_py_version_hex(self, pyversion):
return "0x%02X%02X%02X%02X" % (tuple(pyversion) + (0,0,0,0))[:4]
def mark_pos(self, pos):
if pos is None:
return
source_desc, line, col = pos
if self.last_marker_line == line:
return
assert isinstance(source_desc, SourceDescriptor)
contents = self.globalstate.commented_file_contents(source_desc)
lines = contents[max(0,line-3):line] # line numbers start at 1
lines[-1] += u' # <<<<<<<<<<<<<<'
lines += contents[line:line+2]
marker = u'"%s":%d\n%s\n' % (
source_desc.get_escaped_description(), line, u'\n'.join(lines))
self.marker = (line, marker)
if self.emit_linenums:
self.source_desc = source_desc.get_escaped_description()
def put_label(self, lbl):
if lbl in self.funcstate.labels_used:
self.putln("%s:;" % lbl)
def put_goto(self, lbl):
self.funcstate.use_label(lbl)
self.putln("goto %s;" % lbl)
def put_var_declarations(self, entries, static = 0, dll_linkage = None,
definition = True):
for entry in entries:
if not entry.in_cinclude:
self.put_var_declaration(entry, static, dll_linkage, definition)
def put_var_declaration(self, entry, static = 0, dll_linkage = None,
definition = True):
#print "Code.put_var_declaration:", entry.name, "definition =", definition ###
if entry.in_closure:
return
visibility = entry.visibility
if visibility == 'private' and not definition:
#print "...private and not definition, skipping" ###
return
if not entry.used and visibility == "private":
#print "not used and private, skipping", entry.cname ###
return
storage_class = ""
if visibility == 'extern':
storage_class = Naming.extern_c_macro
elif visibility == 'public':
if not definition:
storage_class = Naming.extern_c_macro
elif visibility == 'private':
if static:
storage_class = "static"
if storage_class:
self.put("%s " % storage_class)
if visibility != 'public':
dll_linkage = None
self.put(entry.type.declaration_code(entry.cname,
dll_linkage = dll_linkage))
if entry.init is not None:
self.put_safe(" = %s" % entry.type.literal_code(entry.init))
self.putln(";")
def put_temp_declarations(self, func_context):
for name, type, manage_ref in func_context.temps_allocated:
decl = type.declaration_code(name)
if type.is_pyobject:
self.putln("%s = NULL;" % decl)
else:
self.putln("%s;" % decl)
def entry_as_pyobject(self, entry):
type = entry.type
if (not entry.is_self_arg and not entry.type.is_complete()
or entry.type.is_extension_type):
return "(PyObject *)" + entry.cname
else:
return entry.cname
def as_pyobject(self, cname, type):
return typecast(py_object_type, type, cname)
def put_gotref(self, cname):
self.putln("__Pyx_GOTREF(%s);" % cname)
def put_giveref(self, cname):
self.putln("__Pyx_GIVEREF(%s);" % cname)
def put_xgiveref(self, cname):
self.putln("__Pyx_XGIVEREF(%s);" % cname)
def put_incref(self, cname, type, nanny=True):
if nanny:
self.putln("__Pyx_INCREF(%s);" % self.as_pyobject(cname, type))
else:
self.putln("Py_INCREF(%s);" % self.as_pyobject(cname, type))
def put_decref(self, cname, type, nanny=True):
if nanny:
self.putln("__Pyx_DECREF(%s);" % self.as_pyobject(cname, type))
else:
self.putln("Py_DECREF(%s);" % self.as_pyobject(cname, type))
def put_var_gotref(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_GOTREF(%s);" % self.entry_as_pyobject(entry))
def put_var_giveref(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_GIVEREF(%s);" % self.entry_as_pyobject(entry))
def put_var_xgiveref(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_XGIVEREF(%s);" % self.entry_as_pyobject(entry))
def put_var_incref(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_INCREF(%s);" % self.entry_as_pyobject(entry))
def put_decref_clear(self, cname, type, nanny=True):
if nanny:
self.putln("__Pyx_DECREF(%s); %s = 0;" % (
typecast(py_object_type, type, cname), cname))
else:
self.putln("Py_DECREF(%s); %s = 0;" % (
typecast(py_object_type, type, cname), cname))
def put_xdecref(self, cname, type, nanny=True):
if nanny:
self.putln("__Pyx_XDECREF(%s);" % self.as_pyobject(cname, type))
else:
self.putln("Py_XDECREF(%s);" % self.as_pyobject(cname, type))
def put_xdecref_clear(self, cname, type, nanny=True):
if nanny:
self.putln("__Pyx_XDECREF(%s); %s = 0;" % (
self.as_pyobject(cname, type), cname))
else:
self.putln("Py_XDECREF(%s); %s = 0;" % (
self.as_pyobject(cname, type), cname))
def put_var_decref(self, entry):
if entry.type.is_pyobject:
if entry.init_to_none is False:
self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
else:
self.putln("__Pyx_DECREF(%s);" % self.entry_as_pyobject(entry))
def put_var_decref_clear(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_DECREF(%s); %s = 0;" % (
self.entry_as_pyobject(entry), entry.cname))
def put_var_xdecref(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
def put_var_xdecref_clear(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_XDECREF(%s); %s = 0;" % (
self.entry_as_pyobject(entry), entry.cname))
def put_var_decrefs(self, entries, used_only = 0):
for entry in entries:
if not used_only or entry.used:
if entry.xdecref_cleanup:
self.put_var_xdecref(entry)
else:
self.put_var_decref(entry)
def put_var_xdecrefs(self, entries):
for entry in entries:
self.put_var_xdecref(entry)
def put_var_xdecrefs_clear(self, entries):
for entry in entries:
self.put_var_xdecref_clear(entry)
def put_init_to_py_none(self, cname, type, nanny=True):
py_none = typecast(type, py_object_type, "Py_None")
if nanny:
self.putln("%s = %s; __Pyx_INCREF(Py_None);" % (cname, py_none))
else:
self.putln("%s = %s; Py_INCREF(Py_None);" % (cname, py_none))
def put_init_var_to_py_none(self, entry, template = "%s", nanny=True):
code = template % entry.cname
#if entry.type.is_extension_type:
# code = "((PyObject*)%s)" % code
self.put_init_to_py_none(code, entry.type, nanny)
def put_pymethoddef(self, entry, term):
if entry.doc:
doc_code = entry.doc_cname
else:
doc_code = 0
method_flags = entry.signature.method_flags()
if method_flags:
if entry.is_special:
method_flags += [method_coexist]
self.putln(
'{__Pyx_NAMESTR("%s"), (PyCFunction)%s, %s, __Pyx_DOCSTR(%s)}%s' % (
entry.name,
entry.func_cname,
"|".join(method_flags),
doc_code,
term))
def put_error_if_neg(self, pos, value):
# return self.putln("if (unlikely(%s < 0)) %s" % (value, self.error_goto(pos))) # TODO this path is almost _never_ taken, yet this macro makes is slower!
return self.putln("if (%s < 0) %s" % (value, self.error_goto(pos)))
def put_h_guard(self, guard):
self.putln("#ifndef %s" % guard)
self.putln("#define %s" % guard)
def unlikely(self, cond):
if Options.gcc_branch_hints:
return 'unlikely(%s)' % cond
else:
return cond
def set_error_info(self, pos):
if Options.c_line_in_traceback:
cinfo = " %s = %s;" % (Naming.clineno_cname, Naming.line_c_macro)
else:
cinfo = ""
return "%s = %s[%s]; %s = %s;%s" % (
Naming.filename_cname,
Naming.filetable_cname,
self.lookup_filename(pos[0]),
Naming.lineno_cname,
pos[1],
cinfo)
def error_goto(self, pos):
lbl = self.funcstate.error_label
self.funcstate.use_label(lbl)
return "{%s goto %s;}" % (
self.set_error_info(pos),
lbl)
def error_goto_if(self, cond, pos):
return "if (%s) %s" % (self.unlikely(cond), self.error_goto(pos))
def error_goto_if_null(self, cname, pos):
return self.error_goto_if("!%s" % cname, pos)
def error_goto_if_neg(self, cname, pos):
return self.error_goto_if("%s < 0" % cname, pos)
def error_goto_if_PyErr(self, pos):
return self.error_goto_if("PyErr_Occurred()", pos)
def lookup_filename(self, filename):
return self.globalstate.lookup_filename(filename)
def put_setup_refcount_context(self, name):
self.putln('__Pyx_SetupRefcountContext("%s");' % name)
def put_finish_refcount_context(self, pos, name, retval_cname, err_val):
self.putln('if (__Pyx_FinishRefcountContext() == -1) {')
self.putln(self.set_error_info(pos))
self.putln('__Pyx_AddTraceback("%s");' % name)
if err_val is not None:
self.putln('%s = %s;' % (retval_cname, err_val))
self.putln('}')
class PyrexCodeWriter:
# f file output file
# level int indentation level
def __init__(self, outfile_name):
self.f = open_new_file(outfile_name)
self.level = 0
def putln(self, code):
self.f.write("%s%s\n" % (" " * self.level, code))
def indent(self):
self.level += 1
def dedent(self):
self.level -= 1
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/CodeGeneration.py 0000664 0000000 0000000 00000002122 11136717635 0026375 0 ustar 00root root 0000000 0000000 from Cython.Compiler.Visitor import VisitorTransform, CythonTransform
from Cython.Compiler.ModuleNode import ModuleNode
from Cython.Compiler.Nodes import *
from Cython.Compiler.ExprNodes import *
class ExtractPxdCode(CythonTransform):
"""
Finds nodes in a pxd file that should generate code, and
returns them in a StatListNode.
The result is a tuple (StatListNode, ModuleScope), i.e.
everything that is needed from the pxd after it is processed.
A purer approach would be to seperately compile the pxd code,
but the result would have to be slightly more sophisticated
than pure strings (functions + wanted interned strings +
wanted utility code + wanted cached objects) so for now this
approach is taken.
"""
def __call__(self, root):
self.funcs = []
self.visitchildren(root)
return (StatListNode(root.pos, stats=self.funcs), root.scope)
def visit_FuncDefNode(self, node):
self.funcs.append(node)
# Do not visit children, nested funcdefnodes will
# also be moved by this action...
return node
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/ControlFlow.py 0000664 0000000 0000000 00000013463 11136717635 0025771 0 ustar 00root root 0000000 0000000 import bisect, sys
# This module keeps track of arbitrary "states" at any point of the code.
# A state is considered known if every path to the given point agrees on
# its state, otherwise it is None (i.e. unknown).
# It might be useful to be able to "freeze" the set of states by pushing
# all state changes to the tips of the trees for fast reading. Perhaps this
# could be done on get_state, clearing the cache on set_state (assuming
# incoming is immutable).
# This module still needs a lot of work, and probably should totally be
# redesigned. It doesn't take return, raise, continue, or break into
# account.
_END_POS = ((unichr(sys.maxunicode)*10),())
class ControlFlow:
def __init__(self, start_pos, incoming, parent):
self.start_pos = start_pos
self.incoming = incoming
if parent is None and incoming is not None:
parent = incoming.parent
self.parent = parent
self.tip = {}
self.end_pos = _END_POS
def start_branch(self, pos):
self.end_pos = pos
branch_point = BranchingControlFlow(pos, self)
if self.parent is not None:
self.parent.branches[-1] = branch_point
return branch_point.branches[0]
def next_branch(self, pos):
self.end_pos = pos
return self.parent.new_branch(pos)
def finish_branch(self, pos):
self.end_pos = pos
self.parent.end_pos = pos
return LinearControlFlow(pos, self.parent)
def get_state(self, item, pos=_END_POS):
return self.get_pos_state(item, pos)[1]
def get_pos_state(self, item, pos=_END_POS):
# do some caching
if pos > self.end_pos:
try:
return self.tip[item]
except KeyError:
self.tip[item] = pos_state = self._get_pos_state(item, pos)
return pos_state
else:
return self._get_pos_state(item, pos)
class LinearControlFlow(ControlFlow):
def __init__(self, start_pos=(), incoming=None, parent=None):
ControlFlow.__init__(self, start_pos, incoming, parent)
self.events = {}
def set_state(self, pos, item, state):
if item in self.tip:
del self.tip[item]
if pos < self.start_pos:
if self.incoming is not None:
self.incoming.set_state(pos, item, state)
else:
if item in self.events:
event_list = self.events[item]
else:
event_list = []
self.events[item] = event_list
bisect.insort(event_list, (pos, state))
def _get_pos_state(self, item, pos):
if pos > self.start_pos:
if item in self.events:
event_list = self.events[item]
for event in event_list[::-1]:
if event[0] < pos:
return event
if self.incoming is not None:
return self.incoming.get_pos_state(item, pos)
else:
return None, None
def to_string(self, indent='', limit=None):
if len(self.events) == 0:
s = indent + "[no state changes]"
else:
all = []
for item, event_list in self.events.items():
for pos, state in event_list:
all.append((indent, pos, item, state))
all.sort()
all = ["%s%s: %s <- %s" % data for data in all]
s = "\n".join(all)
if self.incoming is not limit and self.incoming is not None:
s = "%s\n%s" % (self.incoming.to_string(indent, limit=limit), s)
return s
class BranchingControlFlow(ControlFlow):
def __init__(self, start_pos, incoming, parent=None):
ControlFlow.__init__(self, start_pos, incoming, parent)
self.branches = [LinearControlFlow(start_pos, incoming, parent=self)]
self.branch_starts = [start_pos]
def set_state(self, pos, item, state):
if item in self.tip:
del self.tip[item]
if pos < self.start_pos:
self.incoming.set_state(pos, item, state)
else:
for branch_pos, branch in zip(self.branch_starts[::-1], self.branches[::-1]):
if pos >= branch_pos:
branch.set_state(pos, item, state)
return
def _get_pos_state(self, item, pos):
if pos <= self.start_pos:
return self.incoming.get_pos_state(item, pos)
elif pos < self.end_pos:
for branch_pos, branch in zip(self.branch_starts[::-1], self.branches[::-1]):
if pos >= branch_pos:
return branch.get_pos_state(item, pos)
else:
last_pos, last_state = self.branches[0].get_pos_state(item, pos)
if last_state is None:
return None, None
for branch in self.branches[1:]:
other_pos, other_state = branch.get_pos_state(item, pos)
if other_state is None or other_state != last_state:
return None, None
elif last_pos is not other_pos:
last_pos = max(last_pos, other_pos)
return last_pos, last_state
def new_branch(self, pos):
self.branches.append(LinearControlFlow(pos, self.incoming, parent=self))
self.branch_starts.append(pos)
return self.branches[-1]
def to_string(self, indent='', limit=None):
join = "\n%sor\n" % indent
s = join.join([branch.to_string(indent+" ", limit=self.incoming) for branch in self.branches])
if self.incoming is not limit and self.incoming is not None:
s = "%s\n%s" % (self.incoming.to_string(indent, limit=limit), s)
return s
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/CythonScope.py 0000664 0000000 0000000 00000001704 11136717635 0025752 0 ustar 00root root 0000000 0000000 from Symtab import ModuleScope
from PyrexTypes import *
shape_func_type = CFuncType(
c_ptr_type(c_py_ssize_t_type),
[CFuncTypeArg("buffer", py_object_type, None)])
class CythonScope(ModuleScope):
def __init__(self, context):
ModuleScope.__init__(self, u'cython', None, context)
self.pxd_file_loaded = True
self.shape_entry = self.declare_cfunction('shape',
shape_func_type,
pos=None,
defining = 1,
cname='')
def lookup_type(self, name):
# This function should go away when types are all first-level objects.
type = parse_basic_type(name)
if type:
return type
def create_cython_scope(context):
return CythonScope(context)
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/DebugFlags.py 0000664 0000000 0000000 00000000137 11136717635 0025516 0 ustar 00root root 0000000 0000000 debug_disposal_code = 0
debug_temp_alloc = 0
debug_coercion = 0
debug_temp_code_comments = 1
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/Errors.py 0000664 0000000 0000000 00000010775 11136717635 0025000 0 ustar 00root root 0000000 0000000 #
# Pyrex - Errors
#
import sys
from Cython.Utils import open_new_file
class PyrexError(Exception):
pass
class PyrexWarning(Exception):
pass
def context(position):
source = position[0]
assert not (isinstance(source, unicode) or isinstance(source, str)), (
"Please replace filename strings with Scanning.FileSourceDescriptor instances %r" % source)
try:
F = list(source.get_lines())
except UnicodeDecodeError:
# file has an encoding problem
s = "[unprintable code]\n"
else:
s =''.join(F[max(0, position[1]-6):position[1]])
s = '...\n' + s + ' '*(position[2]-1) + '^\n'
s = '-'*60 + '\n' + s + '-'*60 + '\n'
return s
class CompileError(PyrexError):
def __init__(self, position = None, message = ""):
self.position = position
self.message_only = message
self.reported = False
# Deprecated and withdrawn in 2.6:
# self.message = message
if position:
pos_str = "%s:%d:%d: " % (position[0].get_description(), position[1], position[2])
cont = context(position)
else:
pos_str = ""
cont = ''
Exception.__init__(self, '\nError converting Pyrex file to C:\n' + cont + '\n' + pos_str + message )
class CompileWarning(PyrexWarning):
def __init__(self, position = None, message = ""):
self.position = position
# Deprecated and withdrawn in 2.6:
# self.message = message
if position:
pos_str = "%s:%d:%d: " % (position[0].get_description(), position[1], position[2])
else:
pos_str = ""
Exception.__init__(self, pos_str + message)
class InternalError(Exception):
# If this is ever raised, there is a bug in the compiler.
def __init__(self, message):
Exception.__init__(self, "Internal compiler error: %s"
% message)
class CompilerCrash(CompileError):
# raised when an unexpected exception occurs in a transform
def __init__(self, pos, context, message, cause, stacktrace=None):
if message:
message = u'\n' + message
else:
message = u'\n'
if context:
message = "Compiler crash in " + context + message
if stacktrace:
import traceback
message += (
u'\n\nCompiler crash traceback from this point on:\n' +
u''.join(traceback.format_tb(stacktrace)))
if cause:
if not stacktrace:
message += u'\n'
message += u'%s: %s' % (cause.__class__.__name__, cause)
CompileError.__init__(self, pos, message)
listing_file = None
num_errors = 0
echo_file = None
def open_listing_file(path, echo_to_stderr = 1):
# Begin a new error listing. If path is None, no file
# is opened, the error counter is just reset.
global listing_file, num_errors, echo_file
if path is not None:
listing_file = open_new_file(path)
else:
listing_file = None
if echo_to_stderr:
echo_file = sys.stderr
else:
echo_file = None
num_errors = 0
def close_listing_file():
global listing_file
if listing_file:
listing_file.close()
listing_file = None
def report_error(err):
if error_stack:
error_stack[-1].append(err)
else:
global num_errors
# See Main.py for why dual reporting occurs. Quick fix for now.
if err.reported: return
err.reported = True
line = "%s\n" % err
if listing_file:
listing_file.write(line)
if echo_file:
echo_file.write(line)
num_errors = num_errors + 1
def error(position, message):
#print "Errors.error:", repr(position), repr(message) ###
err = CompileError(position, message)
#if position is not None: raise Exception(err) # debug
report_error(err)
return err
LEVEL=1 # warn about all errors level 1 or higher
def warning(position, message, level=0):
if level < LEVEL:
return
warn = CompileWarning(position, message)
line = "warning: %s\n" % warn
if listing_file:
listing_file.write(line)
if echo_file:
echo_file.write(line)
return warn
# These functions can be used to momentarily suppress errors.
error_stack = []
def hold_errors():
error_stack.append([])
def release_errors(ignore=False):
held_errors = error_stack.pop()
if not ignore:
for err in held_errors:
report_error(err)
def held_errors():
return error_stack[-1]
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/ExprNodes.py 0000664 0000000 0000000 00000603623 11136717635 0025433 0 ustar 00root root 0000000 0000000 #
# Pyrex - Parse tree nodes for expressions
#
import operator
from Errors import error, warning, InternalError
from Errors import hold_errors, release_errors, held_errors, report_error
from Cython.Utils import UtilityCode
import StringEncoding
import Naming
from Nodes import Node
import PyrexTypes
from PyrexTypes import py_object_type, c_long_type, typecast, error_type
from Builtin import list_type, tuple_type, set_type, dict_type, unicode_type
import Builtin
import Symtab
import Options
from Annotate import AnnotationItem
from Cython.Debugging import print_call_chain
from DebugFlags import debug_disposal_code, debug_temp_alloc, \
debug_coercion
try:
set
except NameError:
from sets import Set as set
class NotConstant(object): pass # just for the name
not_a_constant = NotConstant()
constant_value_not_set = object()
class ExprNode(Node):
# subexprs [string] Class var holding names of subexpr node attrs
# type PyrexType Type of the result
# result_code string Code fragment
# result_ctype string C type of result_code if different from type
# is_temp boolean Result is in a temporary variable
# is_sequence_constructor
# boolean Is a list or tuple constructor expression
# saved_subexpr_nodes
# [ExprNode or [ExprNode or None] or None]
# Cached result of subexpr_nodes()
result_ctype = None
type = None
# The Analyse Expressions phase for expressions is split
# into two sub-phases:
#
# Analyse Types
# Determines the result type of the expression based
# on the types of its sub-expressions, and inserts
# coercion nodes into the expression tree where needed.
# Marks nodes which will need to have temporary variables
# allocated.
#
# Allocate Temps
# Allocates temporary variables where needed, and fills
# in the result_code field of each node.
#
# ExprNode provides some convenience routines which
# perform both of the above phases. These should only
# be called from statement nodes, and only when no
# coercion nodes need to be added around the expression
# being analysed. In that case, the above two phases
# should be invoked separately.
#
# Framework code in ExprNode provides much of the common
# processing for the various phases. It makes use of the
# 'subexprs' class attribute of ExprNodes, which should
# contain a list of the names of attributes which can
# hold sub-nodes or sequences of sub-nodes.
#
# The framework makes use of a number of abstract methods.
# Their responsibilities are as follows.
#
# Declaration Analysis phase
#
# analyse_target_declaration
# Called during the Analyse Declarations phase to analyse
# the LHS of an assignment or argument of a del statement.
# Nodes which cannot be the LHS of an assignment need not
# implement it.
#
# Expression Analysis phase
#
# analyse_types
# - Call analyse_types on all sub-expressions.
# - Check operand types, and wrap coercion nodes around
# sub-expressions where needed.
# - Set the type of this node.
# - If a temporary variable will be required for the
# result, set the is_temp flag of this node.
#
# analyse_target_types
# Called during the Analyse Types phase to analyse
# the LHS of an assignment or argument of a del
# statement. Similar responsibilities to analyse_types.
#
# allocate_temps
# - Call allocate_temps for all sub-nodes.
# - Call allocate_temp for this node.
# - If a temporary was allocated, call release_temp on
# all sub-expressions.
#
# allocate_target_temps
# - Call allocate_temps on sub-nodes and allocate any other
# temps used during assignment.
# - Fill in result_code with a C lvalue if needed.
# - If a rhs node is supplied, call release_temp on it.
# - Call release_temp on sub-nodes and release any other
# temps used during assignment.
#
# target_code
# Called by the default implementation of allocate_target_temps.
# Should return a C lvalue for assigning to the node. The default
# implementation calls calculate_result_code.
#
# check_const
# - Check that this node and its subnodes form a
# legal constant expression. If so, do nothing,
# otherwise call not_const.
#
# The default implementation of check_const
# assumes that the expression is not constant.
#
# check_const_addr
# - Same as check_const, except check that the
# expression is a C lvalue whose address is
# constant. Otherwise, call addr_not_const.
#
# The default implementation of calc_const_addr
# assumes that the expression is not a constant
# lvalue.
#
# Code Generation phase
#
# generate_evaluation_code
# - Call generate_evaluation_code for sub-expressions.
# - Perform the functions of generate_result_code
# (see below).
# - If result is temporary, call generate_disposal_code
# on all sub-expressions.
#
# A default implementation of generate_evaluation_code
# is provided which uses the following abstract methods:
#
# generate_result_code
# - Generate any C statements necessary to calculate
# the result of this node from the results of its
# 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
# Called on the LHS of an assignment.
# - Call generate_evaluation_code for sub-expressions.
# - Generate code to perform the assignment.
# - If the assignment absorbed a reference, call
# generate_post_assignment_code on the RHS,
# otherwise call generate_disposal_code on it.
#
# generate_deletion_code
# Called on an argument of a del statement.
# - Call generate_evaluation_code for sub-expressions.
# - Generate code to perform the deletion.
# - Call generate_disposal_code on all sub-expressions.
#
#
is_sequence_constructor = 0
is_attribute = 0
saved_subexpr_nodes = None
is_temp = 0
is_target = 0
constant_result = constant_value_not_set
try:
_get_child_attrs = operator.attrgetter('subexprs')
except AttributeError:
# Python 2.3
def _get_child_attrs(self):
return self.subexprs
child_attrs = property(fget=_get_child_attrs)
def not_implemented(self, method_name):
print_call_chain(method_name, "not implemented") ###
raise InternalError(
"%s.%s not implemented" %
(self.__class__.__name__, method_name))
def is_lvalue(self):
return 0
def is_ephemeral(self):
# An ephemeral node is one whose result is in
# a Python temporary and we suspect there are no
# other references to it. Certain operations are
# disallowed on such values, since they are
# likely to result in a dangling pointer.
return self.type.is_pyobject and self.is_temp
def subexpr_nodes(self):
# Extract a list of subexpression nodes based
# on the contents of the subexprs class attribute.
nodes = []
for name in self.subexprs:
item = getattr(self, name)
if item is not None:
if type(item) is list:
nodes.extend(item)
else:
nodes.append(item)
return nodes
def result(self):
if not self.is_temp or self.is_target:
return self.calculate_result_code()
else: # i.e. self.is_temp:
return self.result_code
def result_as(self, type = None):
# Return the result code cast to the specified C type.
return typecast(type, self.ctype(), self.result())
def py_result(self):
# Return the result code cast to PyObject *.
return self.result_as(py_object_type)
def ctype(self):
# Return the native C type of the result (i.e. the
# C type of the result_code expression).
return self.result_ctype or self.type
def calculate_constant_result(self):
# Calculate the constant result of this expression and store
# it in ``self.constant_result``. Does nothing by default,
# thus leaving ``self.constant_result`` unknown.
#
# This must only be called when it is assured that all
# sub-expressions have a valid constant_result value. The
# ConstantFolding transform will do this.
pass
def compile_time_value(self, denv):
# Return value of compile-time expression, or report error.
error(self.pos, "Invalid compile-time expression")
def compile_time_value_error(self, e):
error(self.pos, "Error in compile-time expression: %s: %s" % (
e.__class__.__name__, e))
# ------------- Declaration Analysis ----------------
def analyse_target_declaration(self, env):
error(self.pos, "Cannot assign to or delete this")
# ------------- Expression Analysis ----------------
def analyse_const_expression(self, env):
# Called during the analyse_declarations phase of a
# constant expression. Analyses the expression's type,
# checks whether it is a legal const expression,
# and determines its value.
self.analyse_types(env)
self.allocate_temps(env)
self.check_const()
def analyse_expressions(self, env):
# Convenience routine performing both the Type
# Analysis and Temp Allocation phases for a whole
# expression.
self.analyse_types(env)
self.allocate_temps(env)
def analyse_target_expression(self, env, rhs):
# Convenience routine performing both the Type
# Analysis and Temp Allocation phases for the LHS of
# an assignment.
self.analyse_target_types(env)
self.allocate_target_temps(env, rhs)
def analyse_boolean_expression(self, env):
# Analyse expression and coerce to a boolean.
self.analyse_types(env)
bool = self.coerce_to_boolean(env)
bool.allocate_temps(env)
return bool
def analyse_temp_boolean_expression(self, env):
# Analyse boolean expression and coerce result into
# a temporary. This is used when a branch is to be
# performed on the result and we won't have an
# opportunity to ensure disposal code is executed
# afterwards. By forcing the result into a temporary,
# we ensure that all disposal has been done by the
# time we get the result.
self.analyse_types(env)
bool = self.coerce_to_boolean(env)
temp_bool = bool.coerce_to_temp(env)
temp_bool.allocate_temps(env)
return temp_bool
# --------------- Type Analysis ------------------
def analyse_as_module(self, env):
# If this node can be interpreted as a reference to a
# cimported module, return its scope, else None.
return None
def analyse_as_type(self, env):
# If this node can be interpreted as a reference to a
# type, return that type, else None.
return None
def analyse_as_extension_type(self, env):
# If this node can be interpreted as a reference to an
# extension type, return its type, else None.
return None
def analyse_types(self, env):
self.not_implemented("analyse_types")
def analyse_target_types(self, env):
self.analyse_types(env)
def gil_assignment_check(self, env):
if env.nogil and self.type.is_pyobject:
error(self.pos, "Assignment of Python object not allowed without gil")
def check_const(self):
self.not_const()
def not_const(self):
error(self.pos, "Not allowed in a constant expression")
def check_const_addr(self):
self.addr_not_const()
def addr_not_const(self):
error(self.pos, "Address is not constant")
def gil_check(self, env):
if env is not None and env.nogil and self.type.is_pyobject:
self.gil_error()
# ----------------- Result Allocation -----------------
def result_in_temp(self):
# Return true if result is in a temporary owned by
# this node or one of its subexpressions. Overridden
# by certain nodes which can share the result of
# a subnode.
return self.is_temp
def allocate_target_temps(self, env, rhs):
# Perform temp allocation for the LHS of an assignment.
if debug_temp_alloc:
print("%s Allocating target temps" % self)
self.allocate_subexpr_temps(env)
self.is_target = True
if rhs:
rhs.release_temp(env)
self.release_subexpr_temps(env)
def allocate_temps(self, env, result = None):
# Allocate temporary variables for this node and
# all its sub-expressions. If a result is specified,
# this must be a temp node and the specified variable
# is used as the result instead of allocating a new
# one.
if debug_temp_alloc:
print("%s Allocating temps" % self)
self.allocate_subexpr_temps(env)
self.allocate_temp(env, result)
if self.is_temp:
self.release_subexpr_temps(env)
def allocate_subexpr_temps(self, env):
# Allocate temporary variables for all sub-expressions
# of this node.
if debug_temp_alloc:
print("%s Allocating temps for: %s" % (self, self.subexprs))
for node in self.subexpr_nodes():
if node:
if debug_temp_alloc:
print("%s Allocating temps for %s" % (self, node))
node.allocate_temps(env)
def allocate_temp(self, env, result = None):
# If this node requires a temporary variable for its
# result, allocate one, otherwise set the result to
# a C code fragment. If a result is specified,
# this must be a temp node and the specified variable
# is used as the result instead of allocating a new
# one.
if debug_temp_alloc:
print("%s Allocating temp" % self)
if result:
if not self.is_temp:
raise InternalError("Result forced on non-temp node")
self.result_code = result
elif self.is_temp:
type = self.type
if not type.is_void:
if type.is_pyobject:
type = PyrexTypes.py_object_type
self.result_code = env.allocate_temp(type)
else:
self.result_code = None
if debug_temp_alloc:
print("%s Allocated result %s" % (self, self.result_code))
def target_code(self):
# Return code fragment for use as LHS of a C assignment.
return self.calculate_result_code()
def calculate_result_code(self):
self.not_implemented("calculate_result_code")
# def release_target_temp(self, env):
# # Release temporaries used by LHS of an assignment.
# self.release_subexpr_temps(env)
def release_temp(self, env):
# If this node owns a temporary result, release it,
# otherwise release results of its sub-expressions.
if self.is_temp:
if debug_temp_alloc:
print("%s Releasing result %s" % (self, self.result_code))
env.release_temp(self.result_code)
else:
self.release_subexpr_temps(env)
def release_subexpr_temps(self, env):
# Release the results of all sub-expressions of
# this node.
for node in self.subexpr_nodes():
if node:
node.release_temp(env)
# ---------------- Code Generation -----------------
def make_owned_reference(self, code):
# If result is a pyobject, make sure we own
# a reference to it.
if self.type.is_pyobject and not self.result_in_temp():
code.put_incref(self.result(), self.ctype())
def generate_evaluation_code(self, code):
code.mark_pos(self.pos)
# Generate code to evaluate this node and
# its sub-expressions, and dispose of any
# temporary results of its sub-expressions.
self.generate_subexpr_evaluation_code(code)
self.generate_result_code(code)
if self.is_temp:
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
def generate_subexpr_evaluation_code(self, code):
for node in self.subexpr_nodes():
node.generate_evaluation_code(code)
def generate_result_code(self, code):
self.not_implemented("generate_result_code")
def generate_disposal_code(self, code):
# If necessary, generate code to dispose of
# temporary Python reference.
if self.is_temp:
if self.type.is_pyobject:
code.put_decref_clear(self.result(), self.ctype())
else:
self.generate_subexpr_disposal_code(code)
def generate_subexpr_disposal_code(self, code):
# Generate code to dispose of temporary results
# of all sub-expressions.
for node in self.subexpr_nodes():
node.generate_disposal_code(code)
def generate_post_assignment_code(self, code):
# Same as generate_disposal_code except that
# assignment will have absorbed a reference to
# the result if it is a Python object.
if self.is_temp:
if self.type.is_pyobject:
code.putln("%s = 0;" % self.result())
else:
self.generate_subexpr_disposal_code(code)
def generate_assignment_code(self, rhs, code):
# Stub method for nodes which are not legal as
# the LHS of an assignment. An error will have
# been reported earlier.
pass
def generate_deletion_code(self, code):
# Stub method for nodes that are not legal as
# the argument of a del statement. An error
# will have been reported earlier.
pass
def free_temps(self, code):
if not self.is_temp:
self.free_subexpr_temps(code)
# otherwise, already freed in generate_evaluation_code
def free_subexpr_temps(self, code):
for sub in self.subexpr_nodes():
sub.free_temps(code)
# ---------------- Annotation ---------------------
def annotate(self, code):
for node in self.subexpr_nodes():
node.annotate(code)
# ----------------- Coercion ----------------------
def coerce_to(self, dst_type, env):
# Coerce the result so that it can be assigned to
# something of type dst_type. If processing is necessary,
# wraps this node in a coercion node and returns that.
# Otherwise, returns this node unchanged.
#
# This method is called during the analyse_expressions
# phase of the src_node's processing.
src = self
src_type = self.type
src_is_py_type = src_type.is_pyobject
dst_is_py_type = dst_type.is_pyobject
if dst_type.is_pyobject:
if not src.type.is_pyobject:
src = CoerceToPyTypeNode(src, env)
if not src.type.subtype_of(dst_type):
if not isinstance(src, NoneNode):
src = PyTypeTestNode(src, dst_type, env)
elif src.type.is_pyobject:
src = CoerceFromPyTypeNode(dst_type, src, env)
else: # neither src nor dst are py types
# Added the string comparison, since for c types that
# is enough, but Cython gets confused when the types are
# in different files.
if not (str(src.type) == str(dst_type) or dst_type.assignable_from(src_type)):
error(self.pos, "Cannot assign type '%s' to '%s'" %
(src.type, dst_type))
return src
def coerce_to_pyobject(self, env):
return self.coerce_to(PyrexTypes.py_object_type, env)
def coerce_to_boolean(self, env):
# Coerce result to something acceptable as
# a boolean value.
type = self.type
if type.is_pyobject or type.is_ptr or type.is_float:
return CoerceToBooleanNode(self, env)
else:
if not type.is_int and not type.is_error:
error(self.pos,
"Type '%s' not acceptable as a boolean" % type)
return self
def coerce_to_integer(self, env):
# If not already some C integer type, coerce to longint.
if self.type.is_int:
return self
else:
return self.coerce_to(PyrexTypes.c_long_type, env)
def coerce_to_temp(self, env):
# Ensure that the result is in a temporary.
if self.result_in_temp():
return self
else:
return CoerceToTempNode(self, env)
def coerce_to_simple(self, env):
# Ensure that the result is simple (see is_simple).
if self.is_simple():
return self
else:
return self.coerce_to_temp(env)
def is_simple(self):
# A node is simple if its result is something that can
# be referred to without performing any operations, e.g.
# a constant, local var, C global var, struct member
# reference, or temporary.
return self.result_in_temp()
def as_cython_attribute(self):
return None
class RemoveAllocateTemps(type):
def __init__(cls, name, bases, dct):
super(RemoveAllocateTemps, cls).__init__(name, bases, dct)
def noop(self, env): pass
setattr(cls, 'allocate_temps', noop)
setattr(cls, 'allocate_temp', noop)
setattr(cls, 'release_temp', noop)
class NewTempExprNode(ExprNode):
backwards_compatible_result = None
temp_code = None
old_temp = None # error checker for multiple frees etc.
# Do not enable this unless you are trying to make all ExprNodes
# NewTempExprNodes (child nodes reached via recursion may not have
# transferred).
# __metaclass__ = RemoveAllocateTemps
def result(self):
if self.is_temp:
return self.temp_code
else:
return self.calculate_result_code()
def allocate_target_temps(self, env, rhs):
self.allocate_subexpr_temps(env)
self.is_target = True
if rhs:
rhs.release_temp(env)
self.release_subexpr_temps(env)
def allocate_temps(self, env, result = None):
self.allocate_subexpr_temps(env)
self.backwards_compatible_result = result
if self.is_temp:
self.release_subexpr_temps(env)
def allocate_temp(self, env, result = None):
assert result is None
def release_temp(self, env):
if self.is_temp:
pass
else:
self.release_subexpr_temps(env)
def allocate_temp_result(self, code):
if self.temp_code:
raise RuntimeError("Temp allocated multiple times")
type = self.type
if not type.is_void:
if type.is_pyobject:
type = PyrexTypes.py_object_type
if self.backwards_compatible_result:
self.temp_code = self.backwards_compatible_result
else:
self.temp_code = code.funcstate.allocate_temp(
type, manage_ref=True)
else:
self.temp_code = None
def release_temp_result(self, code):
if not self.temp_code:
if self.old_temp:
raise RuntimeError("temp %s released multiple times in %s" % (
self.old_temp, self.__class__.__name__))
else:
raise RuntimeError("no temp, but release requested in %s" % (
self.__class__.__name__))
code.funcstate.release_temp(self.temp_code)
self.old_temp = self.temp_code
self.temp_code = None
def generate_evaluation_code(self, code):
code.mark_pos(self.pos)
# Generate code to evaluate this node and
# its sub-expressions, and dispose of any
# temporary results of its sub-expressions.
self.generate_subexpr_evaluation_code(code)
if self.is_temp:
self.allocate_temp_result(code)
self.generate_result_code(code)
if self.is_temp:
# If we are temp we do not need to wait until this node is disposed
# before disposing children.
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
def generate_disposal_code(self, code):
if self.is_temp:
if self.type.is_pyobject:
code.put_decref_clear(self.result(), self.ctype())
else:
# Already done if self.is_temp
self.generate_subexpr_disposal_code(code)
def generate_post_assignment_code(self, code):
if self.is_temp:
if self.type.is_pyobject:
code.putln("%s = 0;" % self.result())
else:
self.generate_subexpr_disposal_code(code)
def free_temps(self, code):
if self.is_temp:
if not self.type.is_void:
self.release_temp_result(code)
else:
self.free_subexpr_temps(code)
# ExprNode = NewTempExprNode
class AtomicExprNode(ExprNode):
# Abstract base class for expression nodes which have
# no sub-expressions.
subexprs = []
class AtomicNewTempExprNode(NewTempExprNode):
# I do not dare to convert NameNode yet. This is now
# ancestor of all former AtomicExprNode except
# NameNode. Should be renamed to AtomicExprNode
# when done.
# Abstract base class for expression nodes which have
# no sub-expressions.
subexprs = []
# Override to optimize -- we know we have no children
def generate_subexpr_evaluation_code(self, code):
pass
def generate_subexpr_disposal_code(self, code):
pass
class PyConstNode(AtomicNewTempExprNode):
# Abstract base class for constant Python values.
is_literal = 1
def is_simple(self):
return 1
def analyse_types(self, env):
self.type = py_object_type
def calculate_result_code(self):
return self.value
def generate_result_code(self, code):
pass
class NoneNode(PyConstNode):
# The constant value None
value = "Py_None"
constant_result = None
def compile_time_value(self, denv):
return None
class EllipsisNode(PyConstNode):
# '...' in a subscript list.
value = "Py_Ellipsis"
constant_result = Ellipsis
def compile_time_value(self, denv):
return Ellipsis
class ConstNode(AtomicNewTempExprNode):
# Abstract base type for literal constant nodes.
#
# value string C code fragment
is_literal = 1
def is_simple(self):
return 1
def analyse_types(self, env):
pass # Types are held in class variables
def check_const(self):
pass
def calculate_result_code(self):
return str(self.value)
def generate_result_code(self, code):
pass
class BoolNode(ConstNode):
type = PyrexTypes.c_bint_type
# The constant value True or False
def calculate_constant_result(self):
self.constant_result = self.value
def compile_time_value(self, denv):
return self.value
def calculate_result_code(self):
return str(int(self.value))
class NullNode(ConstNode):
type = PyrexTypes.c_null_ptr_type
value = "NULL"
constant_result = 0
class CharNode(ConstNode):
type = PyrexTypes.c_char_type
def calculate_constant_result(self):
self.constant_result = ord(self.value)
def compile_time_value(self, denv):
return ord(self.value)
def calculate_result_code(self):
return "'%s'" % StringEncoding.escape_character(self.value)
class IntNode(ConstNode):
# unsigned "" or "U"
# longness "" or "L" or "LL"
unsigned = ""
longness = ""
type = PyrexTypes.c_long_type
def coerce_to(self, dst_type, env):
if dst_type.is_numeric:
self.type = PyrexTypes.c_long_type
return self
# Arrange for a Python version of the number to be pre-allocated
# when coercing to a Python type.
if dst_type.is_pyobject:
self.entry = env.get_py_num(self.value, self.longness)
self.type = PyrexTypes.py_object_type
# We still need to perform normal coerce_to processing on the
# result, because we might be coercing to an extension type,
# in which case a type test node will be needed.
return ConstNode.coerce_to(self, dst_type, env)
def coerce_to_boolean(self, env):
self.type = PyrexTypes.c_bint_type
return self
def calculate_result_code(self):
if self.type.is_pyobject:
return self.entry.cname
else:
return str(self.value) + self.unsigned + self.longness
def calculate_constant_result(self):
self.constant_result = int(self.value, 0)
def compile_time_value(self, denv):
return int(self.value, 0)
class FloatNode(ConstNode):
type = PyrexTypes.c_double_type
def calculate_constant_result(self):
self.constant_result = float(self.value)
def compile_time_value(self, denv):
return float(self.value)
def calculate_result_code(self):
strval = repr(float(self.value))
if strval == 'nan':
return "(Py_HUGE_VAL * 0)"
elif strval == 'inf':
return "Py_HUGE_VAL"
elif strval == '-inf':
return "(-Py_HUGE_VAL)"
else:
return strval
class StringNode(ConstNode):
# entry Symtab.Entry
type = PyrexTypes.c_char_ptr_type
def compile_time_value(self, denv):
return self.value
def analyse_types(self, env):
self.entry = env.add_string_const(self.value)
def analyse_as_type(self, env):
type = PyrexTypes.parse_basic_type(self.value)
if type is not None:
return type
from TreeFragment import TreeFragment
pos = (self.pos[0], self.pos[1], self.pos[2]-7)
declaration = TreeFragment(u"sizeof(%s)" % self.value, name=pos[0].filename, initial_pos=pos)
sizeof_node = declaration.root.stats[0].expr
sizeof_node.analyse_types(env)
if isinstance(sizeof_node, SizeofTypeNode):
return sizeof_node.arg_type
def coerce_to(self, dst_type, env):
if dst_type == PyrexTypes.c_char_ptr_type:
self.type = PyrexTypes.c_char_ptr_type
return self
if dst_type.is_int:
if not self.type.is_pyobject and len(self.entry.init) == 1:
return CharNode(self.pos, value=self.value)
else:
error(self.pos, "Only coerce single-character ascii strings can be used as ints.")
return self
# Arrange for a Python version of the string to be pre-allocated
# when coercing to a Python type.
if dst_type.is_pyobject and not self.type.is_pyobject:
node = self.as_py_string_node(env)
else:
node = self
# We still need to perform normal coerce_to processing on the
# result, because we might be coercing to an extension type,
# in which case a type test node will be needed.
return ConstNode.coerce_to(node, dst_type, env)
def as_py_string_node(self, env):
# Return a new StringNode with the same entry as this node
# but whose type is a Python type instead of a C type.
entry = self.entry
env.add_py_string(entry)
return StringNode(self.pos, value = self.value, entry = entry, type = py_object_type)
def calculate_result_code(self):
if self.type.is_pyobject:
return self.entry.pystring_cname
else:
return self.entry.cname
class UnicodeNode(PyConstNode):
# entry Symtab.Entry
type = unicode_type
def analyse_types(self, env):
self.entry = env.add_string_const(self.value)
env.add_py_string(self.entry)
def calculate_result_code(self):
return self.entry.pystring_cname
def _coerce_to(self, dst_type, env):
if not dst_type.is_pyobject:
node = StringNode(self.pos, entry = entry, type = py_object_type)
return ConstNode.coerce_to(node, dst_type, env)
else:
return self
# We still need to perform normal coerce_to processing on the
# result, because we might be coercing to an extension type,
# in which case a type test node will be needed.
def compile_time_value(self, env):
return self.value
class IdentifierStringNode(ConstNode):
# A Python string that behaves like an identifier, e.g. for
# keyword arguments in a call, or for imported names
type = PyrexTypes.py_object_type
def analyse_types(self, env):
self.cname = env.intern_identifier(self.value)
def calculate_result_code(self):
return self.cname
class LongNode(AtomicNewTempExprNode):
# Python long integer literal
#
# value string
def calculate_constant_result(self):
self.constant_result = long(self.value)
def compile_time_value(self, denv):
return long(self.value)
gil_message = "Constructing Python long int"
def analyse_types(self, env):
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
gil_message = "Constructing Python long int"
def generate_result_code(self, code):
code.putln(
'%s = PyLong_FromString((char *)"%s", 0, 0); %s' % (
self.result(),
self.value,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class ImagNode(AtomicNewTempExprNode):
# Imaginary number literal
#
# value float imaginary part
def calculate_constant_result(self):
self.constant_result = complex(0.0, self.value)
def compile_time_value(self, denv):
return complex(0.0, self.value)
def analyse_types(self, env):
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
gil_message = "Constructing complex number"
def generate_result_code(self, code):
code.putln(
"%s = PyComplex_FromDoubles(0.0, %r); %s" % (
self.result(),
float(self.value),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class NameNode(AtomicExprNode):
# Reference to a local or global variable name.
#
# name string Python name of the variable
#
# entry Entry Symbol table entry
# interned_cname string
is_name = True
is_cython_module = False
cython_attribute = None
lhs_of_first_assignment = False
entry = None
def create_analysed_rvalue(pos, env, entry):
node = NameNode(pos)
node.analyse_types(env, entry=entry)
return node
def as_cython_attribute(self):
return self.cython_attribute
create_analysed_rvalue = staticmethod(create_analysed_rvalue)
def compile_time_value(self, denv):
try:
return denv.lookup(self.name)
except KeyError:
error(self.pos, "Compile-time name '%s' not defined" % self.name)
def coerce_to(self, dst_type, env):
# If coercing to a generic pyobject and this is a builtin
# C function with a Python equivalent, manufacture a NameNode
# referring to the Python builtin.
#print "NameNode.coerce_to:", self.name, dst_type ###
if dst_type is py_object_type:
entry = self.entry
if entry and entry.is_cfunction:
var_entry = entry.as_variable
if var_entry:
if var_entry.is_builtin and Options.cache_builtins:
var_entry = env.declare_builtin(var_entry.name, self.pos)
node = NameNode(self.pos, name = self.name)
node.entry = var_entry
node.analyse_rvalue_entry(env)
return node
return AtomicExprNode.coerce_to(self, dst_type, env)
def analyse_as_module(self, env):
# Try to interpret this as a reference to a cimported module.
# Returns the module scope, or None.
entry = self.entry
if not entry:
entry = env.lookup(self.name)
if entry and entry.as_module:
return entry.as_module
return None
def analyse_as_type(self, env):
if self.cython_attribute:
type = PyrexTypes.parse_basic_type(self.cython_attribute)
else:
type = PyrexTypes.parse_basic_type(self.name)
if type:
return type
entry = self.entry
if not entry:
entry = env.lookup(self.name)
if entry and entry.is_type:
return entry.type
else:
return None
def analyse_as_extension_type(self, env):
# Try to interpret this as a reference to an extension type.
# Returns the extension type, or None.
entry = self.entry
if not entry:
entry = env.lookup(self.name)
if entry and entry.is_type and entry.type.is_extension_type:
return entry.type
else:
return None
def analyse_target_declaration(self, env):
if not self.entry:
self.entry = env.lookup_here(self.name)
if not self.entry:
self.entry = env.declare_var(self.name, py_object_type, self.pos)
env.control_flow.set_state(self.pos, (self.name, 'initalized'), True)
env.control_flow.set_state(self.pos, (self.name, 'source'), 'assignment')
if self.entry.is_declared_generic:
self.result_ctype = py_object_type
def analyse_types(self, env):
if self.entry is None:
self.entry = env.lookup(self.name)
if not self.entry:
self.entry = env.declare_builtin(self.name, self.pos)
if not self.entry:
self.type = PyrexTypes.error_type
return
self.analyse_rvalue_entry(env)
def analyse_target_types(self, env):
self.analyse_entry(env)
if not self.is_lvalue():
error(self.pos, "Assignment to non-lvalue '%s'"
% self.name)
self.type = PyrexTypes.error_type
self.entry.used = 1
if self.entry.type.is_buffer:
import Buffer
Buffer.used_buffer_aux_vars(self.entry)
def analyse_rvalue_entry(self, env):
#print "NameNode.analyse_rvalue_entry:", self.name ###
#print "Entry:", self.entry.__dict__ ###
self.analyse_entry(env)
entry = self.entry
if entry.is_declared_generic:
self.result_ctype = py_object_type
if entry.is_pyglobal or entry.is_builtin:
if Options.cache_builtins and entry.is_builtin:
self.is_temp = 0
else:
self.is_temp = 1
env.use_utility_code(get_name_interned_utility_code)
self.gil_check(env)
gil_message = "Accessing Python global or builtin"
def analyse_entry(self, env):
#print "NameNode.analyse_entry:", self.name ###
self.check_identifier_kind()
entry = self.entry
type = entry.type
self.type = type
if entry.is_pyglobal or entry.is_builtin:
assert type.is_pyobject, "Python global or builtin not a Python object"
self.interned_cname = self.entry.interned_cname = \
env.intern_identifier(self.entry.name)
def check_identifier_kind(self):
#print "NameNode.check_identifier_kind:", self.entry.name ###
#print self.entry.__dict__ ###
entry = self.entry
#entry.used = 1
if not (entry.is_const or entry.is_variable
or entry.is_builtin or entry.is_cfunction):
if self.entry.as_variable:
self.entry = self.entry.as_variable
else:
error(self.pos,
"'%s' is not a constant, variable or function identifier" % self.name)
def is_simple(self):
# If it's not a C variable, it'll be in a temp.
return 1
def calculate_target_results(self, env):
pass
def check_const(self):
entry = self.entry
if entry is not None and not (entry.is_const or entry.is_cfunction or entry.is_builtin):
self.not_const()
def check_const_addr(self):
entry = self.entry
if not (entry.is_cglobal or entry.is_cfunction or entry.is_builtin):
self.addr_not_const()
def is_lvalue(self):
return self.entry.is_variable and \
not self.entry.type.is_array and \
not self.entry.is_readonly
def is_ephemeral(self):
# Name nodes are never ephemeral, even if the
# result is in a temporary.
return 0
def allocate_temp(self, env, result = None):
AtomicExprNode.allocate_temp(self, env, result)
entry = self.entry
if entry:
entry.used = 1
if entry.type.is_buffer:
import Buffer
Buffer.used_buffer_aux_vars(entry)
if entry.utility_code:
env.use_utility_code(entry.utility_code)
def calculate_result_code(self):
entry = self.entry
if not entry:
return "" # There was an error earlier
return entry.cname
def generate_result_code(self, code):
assert hasattr(self, 'entry')
entry = self.entry
if entry is None:
return # There was an error earlier
if entry.is_builtin and Options.cache_builtins:
return # Lookup already cached
elif entry.is_pyglobal or entry.is_builtin:
if entry.is_builtin:
namespace = Naming.builtins_cname
else: # entry.is_pyglobal
namespace = entry.scope.namespace_cname
code.putln(
'%s = __Pyx_GetName(%s, %s); %s' % (
self.result(),
namespace,
self.interned_cname,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
elif entry.is_local and False:
# control flow not good enough yet
assigned = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos)
if assigned is False:
error(self.pos, "local variable '%s' referenced before assignment" % entry.name)
elif not Options.init_local_none and assigned is None:
code.putln('if (%s == 0) { PyErr_SetString(PyExc_UnboundLocalError, "%s"); %s }' %
(entry.cname, entry.name, code.error_goto(self.pos)))
entry.scope.control_flow.set_state(self.pos, (entry.name, 'initalized'), True)
def generate_assignment_code(self, rhs, code):
#print "NameNode.generate_assignment_code:", self.name ###
entry = self.entry
if entry is None:
return # There was an error earlier
if (self.entry.type.is_ptr and isinstance(rhs, ListNode)
and not self.lhs_of_first_assignment):
error(self.pos, "Literal list must be assigned to pointer at time of declaration")
# is_pyglobal seems to be True for module level-globals only.
# We use this to access class->tp_dict if necessary.
if entry.is_pyglobal:
namespace = self.entry.scope.namespace_cname
if entry.is_member:
# if the entry is a member we have to cheat: SetAttr does not work
# on types, so we create a descriptor which is then added to tp_dict
code.put_error_if_neg(self.pos,
'PyDict_SetItem(%s->tp_dict, %s, %s)' % (
namespace,
self.interned_cname,
rhs.py_result()))
# in Py2.6+, we need to invalidate the method cache
code.putln("PyType_Modified(%s);" %
entry.scope.parent_type.typeptr_cname)
else:
code.put_error_if_neg(self.pos,
'PyObject_SetAttr(%s, %s, %s)' % (
namespace,
self.interned_cname,
rhs.py_result()))
if debug_disposal_code:
print("NameNode.generate_assignment_code:")
print("...generating disposal code for %s" % rhs)
rhs.generate_disposal_code(code)
rhs.free_temps(code)
else:
if self.type.is_buffer:
# Generate code for doing the buffer release/acquisition.
# This might raise an exception in which case the assignment (done
# below) will not happen.
#
# The reason this is not in a typetest-like node is because the
# variables that the acquired buffer info is stored to is allocated
# per entry and coupled with it.
self.generate_acquire_buffer(rhs, code)
if self.type.is_pyobject:
rhs.make_owned_reference(code)
#print "NameNode.generate_assignment_code: to", self.name ###
#print "...from", rhs ###
#print "...LHS type", self.type, "ctype", self.ctype() ###
#print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
if not self.lhs_of_first_assignment:
if entry.is_local and not Options.init_local_none:
initalized = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos)
if initalized is True:
code.put_decref(self.result(), self.ctype())
elif initalized is None:
code.put_xdecref(self.result(), self.ctype())
else:
code.put_decref(self.result(), self.ctype())
code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype())))
if debug_disposal_code:
print("NameNode.generate_assignment_code:")
print("...generating post-assignment code for %s" % rhs)
rhs.generate_post_assignment_code(code)
rhs.free_temps(code)
def generate_acquire_buffer(self, rhs, code):
# rhstmp is only used in case the rhs is a complicated expression leading to
# the object, to avoid repeating the same C expression for every reference
# to the rhs. It does NOT hold a reference.
pretty_rhs = isinstance(rhs, NameNode) or rhs.is_temp
if pretty_rhs:
rhstmp = rhs.result_as(self.ctype())
else:
rhstmp = code.funcstate.allocate_temp(self.entry.type, manage_ref=False)
code.putln('%s = %s;' % (rhstmp, rhs.result_as(self.ctype())))
buffer_aux = self.entry.buffer_aux
bufstruct = buffer_aux.buffer_info_var.cname
import Buffer
Buffer.put_assign_to_buffer(self.result(), rhstmp, buffer_aux, self.entry.type,
is_initialized=not self.lhs_of_first_assignment,
pos=self.pos, code=code)
if not pretty_rhs:
code.putln("%s = 0;" % rhstmp)
code.funcstate.release_temp(rhstmp)
def generate_deletion_code(self, code):
if self.entry is None:
return # There was an error earlier
if not self.entry.is_pyglobal:
error(self.pos, "Deletion of local or C global name not supported")
return
code.put_error_if_neg(self.pos,
'__Pyx_DelAttrString(%s, "%s")' % (
Naming.module_cname,
self.entry.name))
def annotate(self, code):
if hasattr(self, 'is_called') and self.is_called:
pos = (self.pos[0], self.pos[1], self.pos[2] - len(self.name) - 1)
if self.type.is_pyobject:
code.annotate(pos, AnnotationItem('py_call', 'python function', size=len(self.name)))
else:
code.annotate(pos, AnnotationItem('c_call', 'c function', size=len(self.name)))
class BackquoteNode(ExprNode):
# `expr`
#
# arg ExprNode
subexprs = ['arg']
def analyse_types(self, env):
self.arg.analyse_types(env)
self.arg = self.arg.coerce_to_pyobject(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
gil_message = "Backquote expression"
def calculate_constant_result(self):
self.constant_result = repr(self.arg.constant_result)
def generate_result_code(self, code):
code.putln(
"%s = PyObject_Repr(%s); %s" % (
self.result(),
self.arg.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class ImportNode(ExprNode):
# Used as part of import statement implementation.
# Implements result =
# __import__(module_name, globals(), None, name_list)
#
# module_name IdentifierStringNode dotted name of module
# name_list ListNode or None list of names to be imported
subexprs = ['module_name', 'name_list']
def analyse_types(self, env):
self.module_name.analyse_types(env)
self.module_name = self.module_name.coerce_to_pyobject(env)
if self.name_list:
self.name_list.analyse_types(env)
self.name_list.coerce_to_pyobject(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
env.use_utility_code(import_utility_code)
gil_message = "Python import"
def generate_result_code(self, code):
if self.name_list:
name_list_code = self.name_list.py_result()
else:
name_list_code = "0"
code.putln(
"%s = __Pyx_Import(%s, %s); %s" % (
self.result(),
self.module_name.py_result(),
name_list_code,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class IteratorNode(NewTempExprNode):
# Used as part of for statement implementation.
#
# allocate_counter_temp/release_counter_temp needs to be called
# by parent (ForInStatNode)
#
# Implements result = iter(sequence)
#
# sequence ExprNode
subexprs = ['sequence']
def analyse_types(self, env):
self.sequence.analyse_types(env)
self.sequence = self.sequence.coerce_to_pyobject(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
gil_message = "Iterating over Python object"
def allocate_counter_temp(self, code):
self.counter_cname = code.funcstate.allocate_temp(
PyrexTypes.c_py_ssize_t_type, manage_ref=False)
def release_counter_temp(self, code):
code.funcstate.release_temp(self.counter_cname)
def generate_result_code(self, code):
is_builtin_sequence = self.sequence.type is list_type or \
self.sequence.type is tuple_type
if is_builtin_sequence:
code.putln(
"if (likely(%s != Py_None)) {" % self.sequence.py_result())
else:
code.putln(
"if (PyList_CheckExact(%s) || PyTuple_CheckExact(%s)) {" % (
self.sequence.py_result(),
self.sequence.py_result()))
code.putln(
"%s = 0; %s = %s; __Pyx_INCREF(%s);" % (
self.counter_cname,
self.result(),
self.sequence.py_result(),
self.result()))
code.putln("} else {")
if is_builtin_sequence:
code.putln(
'PyErr_SetString(PyExc_TypeError, "\'NoneType\' object is not iterable"); %s' %
code.error_goto(self.pos))
else:
code.putln("%s = -1; %s = PyObject_GetIter(%s); %s" % (
self.counter_cname,
self.result(),
self.sequence.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
code.putln("}")
class NextNode(AtomicNewTempExprNode):
# Used as part of for statement implementation.
# Implements result = iterator.next()
# Created during analyse_types phase.
# The iterator is not owned by this node.
#
# iterator ExprNode
def __init__(self, iterator, env):
self.pos = iterator.pos
self.iterator = iterator
self.type = py_object_type
self.is_temp = 1
def generate_result_code(self, code):
if self.iterator.sequence.type is list_type:
type_checks = [(list_type, "List")]
elif self.iterator.sequence.type is tuple_type:
type_checks = [(tuple_type, "Tuple")]
else:
type_checks = [(list_type, "List"), (tuple_type, "Tuple")]
for py_type, prefix in type_checks:
if len(type_checks) > 1:
code.putln(
"if (likely(Py%s_CheckExact(%s))) {" % (
prefix, self.iterator.py_result()))
code.putln(
"if (%s >= Py%s_GET_SIZE(%s)) break;" % (
self.iterator.counter_cname,
prefix,
self.iterator.py_result()))
code.putln(
"%s = Py%s_GET_ITEM(%s, %s); __Pyx_INCREF(%s); %s++;" % (
self.result(),
prefix,
self.iterator.py_result(),
self.iterator.counter_cname,
self.result(),
self.iterator.counter_cname))
if len(type_checks) > 1:
code.put("} else ")
if len(type_checks) == 1:
return
code.putln("{")
code.putln(
"%s = PyIter_Next(%s);" % (
self.result(),
self.iterator.py_result()))
code.putln(
"if (!%s) {" %
self.result())
code.putln(code.error_goto_if_PyErr(self.pos))
code.putln("break;")
code.putln("}")
code.put_gotref(self.py_result())
code.putln("}")
class ExcValueNode(AtomicNewTempExprNode):
# Node created during analyse_types phase
# of an ExceptClauseNode to fetch the current
# exception value.
def __init__(self, pos, env, var):
ExprNode.__init__(self, pos)
self.type = py_object_type
self.var = var
def calculate_result_code(self):
return self.var
def generate_result_code(self, code):
pass
def analyse_types(self, env):
pass
class TempNode(ExprNode):
# Node created during analyse_types phase
# of some nodes to hold a temporary value.
subexprs = []
def __init__(self, pos, type, env):
ExprNode.__init__(self, pos)
self.type = type
if type.is_pyobject:
self.result_ctype = py_object_type
self.is_temp = 1
def analyse_types(self, env):
return self.type
def generate_result_code(self, code):
pass
class PyTempNode(TempNode):
# TempNode holding a Python value.
def __init__(self, pos, env):
TempNode.__init__(self, pos, PyrexTypes.py_object_type, env)
#-------------------------------------------------------------------
#
# Trailer nodes
#
#-------------------------------------------------------------------
class IndexNode(ExprNode):
# Sequence indexing.
#
# base ExprNode
# index ExprNode
# indices [ExprNode]
# is_buffer_access boolean Whether this is a buffer access.
#
# indices is used on buffer access, index on non-buffer access.
# The former contains a clean list of index parameters, the
# latter whatever Python object is needed for index access.
subexprs = ['base', 'index', 'indices']
indices = None
def __init__(self, pos, index, *args, **kw):
ExprNode.__init__(self, pos, index=index, *args, **kw)
self._index = index
def calculate_constant_result(self):
self.constant_result = \
self.base.constant_result[self.index.constant_result]
def compile_time_value(self, denv):
base = self.base.compile_time_value(denv)
index = self.index.compile_time_value(denv)
try:
return base[index]
except Exception, e:
self.compile_time_value_error(e)
def is_ephemeral(self):
return self.base.is_ephemeral()
def analyse_target_declaration(self, env):
pass
def analyse_as_type(self, env):
base_type = self.base.analyse_as_type(env)
if base_type and not base_type.is_pyobject:
return PyrexTypes.CArrayType(base_type, int(self.index.compile_time_value(env)))
return None
def analyse_types(self, env):
self.analyse_base_and_index_types(env, getting = 1)
def analyse_target_types(self, env):
self.analyse_base_and_index_types(env, setting = 1)
def analyse_base_and_index_types(self, env, getting = 0, setting = 0):
# Note: This might be cleaned up by having IndexNode
# parsed in a saner way and only construct the tuple if
# needed.
# Note that this function must leave IndexNode in a cloneable state.
# For buffers, self.index is packed out on the initial analysis, and
# when cloning self.indices is copied.
self.is_buffer_access = False
self.base.analyse_types(env)
# Handle the case where base is a literal char* (and we expect a string, not an int)
if isinstance(self.base, StringNode):
self.base = self.base.coerce_to_pyobject(env)
skip_child_analysis = False
buffer_access = False
if self.base.type.is_buffer:
assert hasattr(self.base, "entry") # Must be a NameNode-like node
if self.indices:
indices = self.indices
else:
# On cloning, indices is cloned. Otherwise, unpack index into indices
assert not isinstance(self.index, CloneNode)
if isinstance(self.index, TupleNode):
indices = self.index.args
else:
indices = [self.index]
if len(indices) == self.base.type.ndim:
buffer_access = True
skip_child_analysis = True
for x in indices:
x.analyse_types(env)
if not x.type.is_int:
buffer_access = False
if buffer_access:
self.indices = indices
self.index = None
self.type = self.base.type.dtype
self.is_buffer_access = True
self.buffer_type = self.base.entry.type
if getting and self.type.is_pyobject:
self.is_temp = True
if setting:
if not self.base.entry.type.writable:
error(self.pos, "Writing to readonly buffer")
else:
self.base.entry.buffer_aux.writable_needed = True
else:
if isinstance(self.index, TupleNode):
self.index.analyse_types(env, skip_children=skip_child_analysis)
elif not skip_child_analysis:
self.index.analyse_types(env)
self.original_index_type = self.index.type
if self.base.type.is_pyobject:
if self.index.type.is_int and not self.index.type.is_longlong:
self.index = self.index.coerce_to(PyrexTypes.c_py_ssize_t_type, env).coerce_to_simple(env)
else:
self.index = self.index.coerce_to_pyobject(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
else:
if self.base.type.is_ptr or self.base.type.is_array:
self.type = self.base.type.base_type
else:
error(self.pos,
"Attempting to index non-array type '%s'" %
self.base.type)
self.type = PyrexTypes.error_type
if self.index.type.is_pyobject:
self.index = self.index.coerce_to(
PyrexTypes.c_py_ssize_t_type, env)
if not self.index.type.is_int:
error(self.pos,
"Invalid index type '%s'" %
self.index.type)
gil_message = "Indexing Python object"
def check_const_addr(self):
self.base.check_const_addr()
self.index.check_const()
def is_lvalue(self):
return 1
def calculate_result_code(self):
if self.is_buffer_access:
return "(*%s)" % self.buffer_ptr_code
else:
return "(%s[%s])" % (
self.base.result(), self.index.result())
def index_unsigned_parameter(self):
if self.index.type.is_int:
if self.original_index_type.signed:
return ", 0"
else:
return ", sizeof(Py_ssize_t) <= sizeof(%s)" % self.original_index_type.declaration_code("")
else:
return ""
def generate_subexpr_evaluation_code(self, code):
self.base.generate_evaluation_code(code)
if not self.indices:
self.index.generate_evaluation_code(code)
else:
for i in self.indices:
i.generate_evaluation_code(code)
def generate_subexpr_disposal_code(self, code):
self.base.generate_disposal_code(code)
if not self.indices:
self.index.generate_disposal_code(code)
else:
for i in self.indices:
i.generate_disposal_code(code)
def free_subexpr_temps(self, code):
self.base.free_temps(code)
if not self.indices:
self.index.free_temps(code)
else:
for i in self.indices:
i.free_temps(code)
def generate_result_code(self, code):
if self.is_buffer_access:
if code.globalstate.directives['nonecheck']:
self.put_nonecheck(code)
self.buffer_ptr_code = self.buffer_lookup_code(code)
if self.type.is_pyobject:
# is_temp is True, so must pull out value and incref it.
code.putln("%s = *%s;" % (self.result(), self.buffer_ptr_code))
code.putln("__Pyx_INCREF((PyObject*)%s);" % self.result())
elif self.type.is_pyobject:
if self.index.type.is_int:
function = "__Pyx_GetItemInt"
index_code = self.index.result()
code.globalstate.use_utility_code(getitem_int_utility_code)
else:
function = "PyObject_GetItem"
index_code = self.index.py_result()
sign_code = ""
code.putln(
"%s = %s(%s, %s%s); if (!%s) %s" % (
self.result(),
function,
self.base.py_result(),
index_code,
self.index_unsigned_parameter(),
self.result(),
code.error_goto(self.pos)))
code.put_gotref(self.py_result())
def generate_setitem_code(self, value_code, code):
if self.index.type.is_int:
function = "__Pyx_SetItemInt"
index_code = self.index.result()
code.globalstate.use_utility_code(setitem_int_utility_code)
else:
index_code = self.index.py_result()
if self.base.type is dict_type:
function = "PyDict_SetItem"
elif self.base.type is list_type:
function = "PyList_SetItem"
# don't use PyTuple_SetItem(), as we'd normally get a
# TypeError when changing a tuple, while PyTuple_SetItem()
# would allow updates
#
#elif self.base.type is tuple_type:
# function = "PyTuple_SetItem"
else:
function = "PyObject_SetItem"
code.putln(
"if (%s(%s, %s, %s%s) < 0) %s" % (
function,
self.base.py_result(),
index_code,
value_code,
self.index_unsigned_parameter(),
code.error_goto(self.pos)))
def generate_buffer_setitem_code(self, rhs, code, op=""):
# Used from generate_assignment_code and InPlaceAssignmentNode
if code.globalstate.directives['nonecheck']:
self.put_nonecheck(code)
ptrexpr = self.buffer_lookup_code(code)
if self.buffer_type.dtype.is_pyobject:
# Must manage refcounts. Decref what is already there
# and incref what we put in.
ptr = code.funcstate.allocate_temp(self.buffer_type.buffer_ptr_type, manage_ref=False)
rhs_code = rhs.result()
code.putln("%s = %s;" % (ptr, ptrexpr))
code.putln("__Pyx_DECREF(*%s); __Pyx_INCREF(%s);" % (
ptr, rhs_code
))
code.putln("*%s %s= %s;" % (ptr, op, rhs_code))
code.funcstate.release_temp(ptr)
else:
# Simple case
code.putln("*%s %s= %s;" % (ptrexpr, op, rhs.result()))
def generate_assignment_code(self, rhs, code):
self.generate_subexpr_evaluation_code(code)
if self.is_buffer_access:
self.generate_buffer_setitem_code(rhs, code)
elif self.type.is_pyobject:
self.generate_setitem_code(rhs.py_result(), code)
else:
code.putln(
"%s = %s;" % (
self.result(), rhs.result()))
self.generate_subexpr_disposal_code(code)
self.free_subexpr_temps(code)
rhs.generate_disposal_code(code)
rhs.free_temps(code)
def generate_deletion_code(self, code):
self.generate_subexpr_evaluation_code(code)
#if self.type.is_pyobject:
if self.index.type.is_int:
function = "__Pyx_DelItemInt"
index_code = self.index.result()
code.globalstate.use_utility_code(delitem_int_utility_code)
else:
index_code = self.index.py_result()
if self.base.type is dict_type:
function = "PyDict_DelItem"
else:
function = "PyObject_DelItem"
code.putln(
"if (%s(%s, %s%s) < 0) %s" % (
function,
self.base.py_result(),
index_code,
self.index_unsigned_parameter(),
code.error_goto(self.pos)))
self.generate_subexpr_disposal_code(code)
def buffer_lookup_code(self, code):
# Assign indices to temps
index_temps = [code.funcstate.allocate_temp(i.type, manage_ref=False) for i in self.indices]
for temp, index in zip(index_temps, self.indices):
code.putln("%s = %s;" % (temp, index.result()))
# Generate buffer access code using these temps
import Buffer
# The above could happen because child_attrs is wrong somewhere so that
# options are not propagated.
return Buffer.put_buffer_lookup_code(entry=self.base.entry,
index_signeds=[i.type.signed for i in self.indices],
index_cnames=index_temps,
options=code.globalstate.directives,
pos=self.pos, code=code)
def put_nonecheck(self, code):
code.globalstate.use_utility_code(raise_noneindex_error_utility_code)
code.putln("if (%s) {" % code.unlikely("%s == Py_None") % self.base.result_as(PyrexTypes.py_object_type))
code.putln("__Pyx_RaiseNoneIndexingError();")
code.putln(code.error_goto(self.pos))
code.putln("}")
class SliceIndexNode(ExprNode):
# 2-element slice indexing
#
# base ExprNode
# start ExprNode or None
# stop ExprNode or None
subexprs = ['base', 'start', 'stop']
def calculate_constant_result(self):
self.constant_result = self.base.constant_result[
self.start.constant_result : self.stop.constant_result]
def compile_time_value(self, denv):
base = self.base.compile_time_value(denv)
if self.start is None:
start = 0
else:
start = self.start.compile_time_value(denv)
if self.stop is None:
stop = None
else:
stop = self.stop.compile_time_value(denv)
try:
return base[start:stop]
except Exception, e:
self.compile_time_value_error(e)
def analyse_target_declaration(self, env):
pass
def analyse_types(self, env):
self.base.analyse_types(env)
if self.start:
self.start.analyse_types(env)
if self.stop:
self.stop.analyse_types(env)
if self.base.type.is_string:
self.type = py_object_type
elif self.base.type.is_array or self.base.type.is_ptr:
# we need a ptr type here instead of an array type, as
# array types can result in invalid type casts in the C
# code
self.type = PyrexTypes.CPtrType(self.base.type.base_type)
else:
self.base = self.base.coerce_to_pyobject(env)
self.type = py_object_type
c_int = PyrexTypes.c_py_ssize_t_type
if self.start:
self.start = self.start.coerce_to(c_int, env)
if self.stop:
self.stop = self.stop.coerce_to(c_int, env)
self.gil_check(env)
self.is_temp = 1
gil_message = "Slicing Python object"
def generate_result_code(self, code):
if not self.type.is_pyobject:
error(self.pos,
"Slicing is not currently supported for '%s'." % self.type)
return
if self.base.type.is_string:
if self.stop is None:
code.putln(
"%s = __Pyx_PyBytes_FromString(%s + %s); %s" % (
self.result(),
self.base.result(),
self.start_code(),
code.error_goto_if_null(self.result(), self.pos)))
else:
code.putln(
"%s = __Pyx_PyBytes_FromStringAndSize(%s + %s, %s - %s); %s" % (
self.result(),
self.base.result(),
self.start_code(),
self.stop_code(),
self.start_code(),
code.error_goto_if_null(self.result(), self.pos)))
else:
code.putln(
"%s = PySequence_GetSlice(%s, %s, %s); %s" % (
self.result(),
self.base.py_result(),
self.start_code(),
self.stop_code(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
def generate_assignment_code(self, rhs, code):
self.generate_subexpr_evaluation_code(code)
if self.type.is_pyobject:
code.put_error_if_neg(self.pos,
"PySequence_SetSlice(%s, %s, %s, %s)" % (
self.base.py_result(),
self.start_code(),
self.stop_code(),
rhs.result()))
else:
start_offset = ''
if self.start:
start_offset = self.start_code()
if start_offset == '0':
start_offset = ''
else:
start_offset += '+'
if rhs.type.is_array:
array_length = rhs.type.size
self.generate_slice_guard_code(code, array_length)
else:
error("Slice assignments from pointers are not yet supported.")
# FIXME: fix the array size according to start/stop
array_length = self.base.type.size
for i in range(array_length):
code.putln("%s[%s%s] = %s[%d];" % (
self.base.result(), start_offset, i,
rhs.result(), i))
self.generate_subexpr_disposal_code(code)
rhs.generate_disposal_code(code)
rhs.free_temps(code)
def generate_deletion_code(self, code):
if not self.base.type.is_pyobject:
error(self.pos,
"Deleting slices is only supported for Python types, not '%s'." % self.type)
return
self.generate_subexpr_evaluation_code(code)
code.put_error_if_neg(self.pos,
"PySequence_DelSlice(%s, %s, %s)" % (
self.base.py_result(),
self.start_code(),
self.stop_code()))
self.generate_subexpr_disposal_code(code)
def generate_slice_guard_code(self, code, target_size):
if not self.base.type.is_array:
return
slice_size = self.base.type.size
start = stop = None
if self.stop:
stop = self.stop.result()
try:
stop = int(stop)
if stop < 0:
slice_size = self.base.type.size + stop
else:
slice_size = stop
stop = None
except ValueError:
pass
if self.start:
start = self.start.result()
try:
start = int(start)
if start < 0:
start = self.base.type.size + start
slice_size -= start
start = None
except ValueError:
pass
check = None
if slice_size < 0:
if target_size > 0:
error(self.pos, "Assignment to empty slice.")
elif start is None and stop is None:
# we know the exact slice length
if target_size != slice_size:
error(self.pos, "Assignment to slice of wrong length, expected %d, got %d" % (
slice_size, target_size))
elif start is not None:
if stop is None:
stop = slice_size
check = "(%s)-(%s)" % (stop, start)
else: # stop is not None:
check = stop
if check:
code.putln("if (unlikely((%s) != %d)) {" % (check, target_size))
code.putln('PyErr_Format(PyExc_ValueError, "Assignment to slice of wrong length, expected %%d, got %%d", %d, (%s));' % (
target_size, check))
code.putln(code.error_goto(self.pos))
code.putln("}")
def start_code(self):
if self.start:
return self.start.result()
else:
return "0"
def stop_code(self):
if self.stop:
return self.stop.result()
elif self.base.type.is_array:
return self.base.type.size
else:
return "PY_SSIZE_T_MAX"
def calculate_result_code(self):
# self.result() is not used, but this method must exist
return ""
class SliceNode(ExprNode):
# start:stop:step in subscript list
#
# start ExprNode
# stop ExprNode
# step ExprNode
def calculate_constant_result(self):
self.constant_result = self.base.constant_result[
self.start.constant_result : \
self.stop.constant_result : \
self.step.constant_result]
def compile_time_value(self, denv):
start = self.start.compile_time_value(denv)
if self.stop is None:
stop = None
else:
stop = self.stop.compile_time_value(denv)
if self.step is None:
step = None
else:
step = self.step.compile_time_value(denv)
try:
return slice(start, stop, step)
except Exception, e:
self.compile_time_value_error(e)
subexprs = ['start', 'stop', 'step']
def analyse_types(self, env):
self.start.analyse_types(env)
self.stop.analyse_types(env)
self.step.analyse_types(env)
self.start = self.start.coerce_to_pyobject(env)
self.stop = self.stop.coerce_to_pyobject(env)
self.step = self.step.coerce_to_pyobject(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
gil_message = "Constructing Python slice object"
def generate_result_code(self, code):
code.putln(
"%s = PySlice_New(%s, %s, %s); %s" % (
self.result(),
self.start.py_result(),
self.stop.py_result(),
self.step.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class CallNode(NewTempExprNode):
def gil_check(self, env):
# Make sure we're not in a nogil environment
if env.nogil:
error(self.pos, "Calling gil-requiring function without gil")
def analyse_as_type_constructor(self, env):
type = self.function.analyse_as_type(env)
if type and type.is_struct_or_union:
args, kwds = self.explicit_args_kwds()
items = []
for arg, member in zip(args, type.scope.var_entries):
items.append(DictItemNode(pos=arg.pos, key=IdentifierStringNode(pos=arg.pos, value=member.name), value=arg))
if kwds:
items += kwds.key_value_pairs
self.key_value_pairs = items
self.__class__ = DictNode
self.analyse_types(env)
self.coerce_to(type, env)
return True
class SimpleCallNode(CallNode):
# Function call without keyword, * or ** args.
#
# function ExprNode
# args [ExprNode]
# arg_tuple ExprNode or None used internally
# self ExprNode or None used internally
# coerced_self ExprNode or None used internally
# wrapper_call bool used internally
# has_optional_args bool used internally
subexprs = ['self', 'coerced_self', 'function', 'args', 'arg_tuple']
self = None
coerced_self = None
arg_tuple = None
wrapper_call = False
has_optional_args = False
def compile_time_value(self, denv):
function = self.function.compile_time_value(denv)
args = [arg.compile_time_value(denv) for arg in self.args]
try:
return function(*args)
except Exception, e:
self.compile_time_value_error(e)
def analyse_as_type(self, env):
attr = self.function.as_cython_attribute()
if attr == 'pointer':
if len(self.args) != 1:
error(self.args.pos, "only one type allowed.")
else:
type = self.args[0].analyse_as_type(env)
if not type:
error(self.args[0].pos, "Unknown type")
else:
return PyrexTypes.CPtrType(type)
def explicit_args_kwds(self):
return self.args, None
def analyse_types(self, env):
if self.analyse_as_type_constructor(env):
return
function = self.function
function.is_called = 1
self.function.analyse_types(env)
if function.is_attribute and function.is_py_attr and \
function.attribute == "append" and len(self.args) == 1:
# L.append(x) is almost always applied to a list
self.py_func = self.function
self.function = NameNode(pos=self.function.pos, name="__Pyx_PyObject_Append")
self.function.analyse_types(env)
self.self = self.py_func.obj
function.obj = CloneNode(self.self)
env.use_utility_code(append_utility_code)
if function.is_attribute and function.entry and function.entry.is_cmethod:
# Take ownership of the object from which the attribute
# was obtained, because we need to pass it as 'self'.
self.self = function.obj
function.obj = CloneNode(self.self)
func_type = self.function_type()
if func_type.is_pyobject:
self.arg_tuple = TupleNode(self.pos, args = self.args)
self.arg_tuple.analyse_types(env)
self.args = None
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
else:
for arg in self.args:
arg.analyse_types(env)
if self.self and func_type.args:
# Coerce 'self' to the type expected by the method.
expected_type = func_type.args[0].type
self.coerced_self = CloneNode(self.self).coerce_to(
expected_type, env)
# Insert coerced 'self' argument into argument list.
self.args.insert(0, self.coerced_self)
self.analyse_c_function_call(env)
def function_type(self):
# Return the type of the function being called, coercing a function
# pointer to a function if necessary.
func_type = self.function.type
if func_type.is_ptr:
func_type = func_type.base_type
return func_type
def analyse_c_function_call(self, env):
func_type = self.function_type()
# Check function type
if not func_type.is_cfunction:
if not func_type.is_error:
error(self.pos, "Calling non-function type '%s'" %
func_type)
self.type = PyrexTypes.error_type
self.result_code = ""
return
# Check no. of args
max_nargs = len(func_type.args)
expected_nargs = max_nargs - func_type.optional_arg_count
actual_nargs = len(self.args)
if actual_nargs < expected_nargs \
or (not func_type.has_varargs and actual_nargs > max_nargs):
expected_str = str(expected_nargs)
if func_type.has_varargs:
expected_str = "at least " + expected_str
elif func_type.optional_arg_count:
if actual_nargs < max_nargs:
expected_str = "at least " + expected_str
else:
expected_str = "at most " + str(max_nargs)
error(self.pos,
"Call with wrong number of arguments (expected %s, got %s)"
% (expected_str, actual_nargs))
self.args = None
self.type = PyrexTypes.error_type
self.result_code = ""
return
if func_type.optional_arg_count and expected_nargs != actual_nargs:
self.has_optional_args = 1
self.is_temp = 1
self.opt_arg_struct = env.allocate_temp(func_type.op_arg_struct.base_type)
env.release_temp(self.opt_arg_struct)
# Coerce arguments
for i in range(min(max_nargs, actual_nargs)):
formal_type = func_type.args[i].type
self.args[i] = self.args[i].coerce_to(formal_type, env)
for i in range(max_nargs, actual_nargs):
if self.args[i].type.is_pyobject:
error(self.args[i].pos,
"Python object cannot be passed as a varargs parameter")
# Calc result type and code fragment
self.type = func_type.return_type
if self.type.is_pyobject:
self.result_ctype = py_object_type
self.is_temp = 1
elif func_type.exception_value is not None \
or func_type.exception_check:
self.is_temp = 1
# C++ exception handler
if func_type.exception_check == '+':
if func_type.exception_value is None:
env.use_utility_code(cpp_exception_utility_code)
# Check gil
if not func_type.nogil:
self.gil_check(env)
def calculate_result_code(self):
return self.c_call_code()
def c_call_code(self):
func_type = self.function_type()
if self.args is None or not func_type.is_cfunction:
return ""
formal_args = func_type.args
arg_list_code = []
args = zip(formal_args, self.args)
max_nargs = len(func_type.args)
expected_nargs = max_nargs - func_type.optional_arg_count
actual_nargs = len(self.args)
for formal_arg, actual_arg in args[:expected_nargs]:
arg_code = actual_arg.result_as(formal_arg.type)
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 expected_nargs == actual_nargs:
optional_args = 'NULL'
else:
optional_args = "&%s" % self.opt_arg_struct
arg_list_code.append(optional_args)
for actual_arg in self.args[len(formal_args):]:
arg_list_code.append(actual_arg.result())
result = "%s(%s)" % (self.function.result(),
', '.join(arg_list_code))
# if self.wrapper_call or \
# self.function.entry.is_unbound_cmethod and self.function.entry.type.is_overridable:
# result = "(%s = 1, %s)" % (Naming.skip_dispatch_cname, result)
return result
def generate_result_code(self, code):
func_type = self.function_type()
if func_type.is_pyobject:
arg_code = self.arg_tuple.py_result()
code.putln(
"%s = PyObject_Call(%s, %s, NULL); %s" % (
self.result(),
self.function.py_result(),
arg_code,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
elif func_type.is_cfunction:
if self.has_optional_args:
actual_nargs = len(self.args)
expected_nargs = len(func_type.args) - func_type.optional_arg_count
code.putln("%s.%s = %s;" % (
self.opt_arg_struct,
Naming.pyrex_prefix + "n",
len(self.args) - expected_nargs))
args = zip(func_type.args, self.args)
for formal_arg, actual_arg in args[expected_nargs:actual_nargs]:
code.putln("%s.%s = %s;" % (
self.opt_arg_struct,
formal_arg.name,
actual_arg.result_as(formal_arg.type)))
exc_checks = []
if self.type.is_pyobject:
exc_checks.append("!%s" % self.result())
else:
exc_val = func_type.exception_value
exc_check = func_type.exception_check
if exc_val is not None:
exc_checks.append("%s == %s" % (self.result(), exc_val))
if exc_check:
exc_checks.append("PyErr_Occurred()")
if self.is_temp or exc_checks:
rhs = self.c_call_code()
if self.result():
lhs = "%s = " % self.result()
if self.is_temp and self.type.is_pyobject:
#return_type = self.type # func_type.return_type
#print "SimpleCallNode.generate_result_code: casting", rhs, \
# "from", return_type, "to pyobject" ###
rhs = typecast(py_object_type, self.type, rhs)
else:
lhs = ""
if func_type.exception_check == '+':
if func_type.exception_value is None:
raise_py_exception = "__Pyx_CppExn2PyErr()"
elif func_type.exception_value.type.is_pyobject:
raise_py_exception = 'PyErr_SetString(%s, "")' % func_type.exception_value.entry.cname
else:
raise_py_exception = '%s(); if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError , "Error converting c++ exception.")' % func_type.exception_value.entry.cname
code.putln(
"try {%s%s;} catch(...) {%s; %s}" % (
lhs,
rhs,
raise_py_exception,
code.error_goto(self.pos)))
else:
if exc_checks:
goto_error = code.error_goto_if(" && ".join(exc_checks), self.pos)
else:
goto_error = ""
code.putln("%s%s; %s" % (lhs, rhs, goto_error))
if self.type.is_pyobject and self.result():
code.put_gotref(self.py_result())
class GeneralCallNode(CallNode):
# General Python function call, including keyword,
# * and ** arguments.
#
# function ExprNode
# positional_args ExprNode Tuple of positional arguments
# keyword_args ExprNode or None Dict of keyword arguments
# starstar_arg ExprNode or None Dict of extra keyword args
subexprs = ['function', 'positional_args', 'keyword_args', 'starstar_arg']
def compile_time_value(self, denv):
function = self.function.compile_time_value(denv)
positional_args = self.positional_args.compile_time_value(denv)
keyword_args = self.keyword_args.compile_time_value(denv)
starstar_arg = self.starstar_arg.compile_time_value(denv)
try:
keyword_args.update(starstar_arg)
return function(*positional_args, **keyword_args)
except Exception, e:
self.compile_time_value_error(e)
def explicit_args_kwds(self):
if self.starstar_arg or not isinstance(self.positional_args, TupleNode):
raise PostParseError(self.pos,
'Compile-time keyword arguments must be explicit.')
return self.positional_args.args, self.keyword_args
def analyse_types(self, env):
if self.analyse_as_type_constructor(env):
return
self.function.analyse_types(env)
self.positional_args.analyse_types(env)
if self.keyword_args:
self.keyword_args.analyse_types(env)
if self.starstar_arg:
self.starstar_arg.analyse_types(env)
self.function = self.function.coerce_to_pyobject(env)
self.positional_args = \
self.positional_args.coerce_to_pyobject(env)
if self.starstar_arg:
self.starstar_arg = \
self.starstar_arg.coerce_to_pyobject(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
def generate_result_code(self, code):
if self.keyword_args and self.starstar_arg:
code.put_error_if_neg(self.pos,
"PyDict_Update(%s, %s)" % (
self.keyword_args.py_result(),
self.starstar_arg.py_result()))
keyword_code = self.keyword_args.py_result()
elif self.keyword_args:
keyword_code = self.keyword_args.py_result()
elif self.starstar_arg:
keyword_code = self.starstar_arg.py_result()
else:
keyword_code = None
if not keyword_code:
call_code = "PyObject_Call(%s, %s, NULL)" % (
self.function.py_result(),
self.positional_args.py_result())
else:
call_code = "PyEval_CallObjectWithKeywords(%s, %s, %s)" % (
self.function.py_result(),
self.positional_args.py_result(),
keyword_code)
code.putln(
"%s = %s; %s" % (
self.result(),
call_code,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class AsTupleNode(ExprNode):
# Convert argument to tuple. Used for normalising
# the * argument of a function call.
#
# arg ExprNode
subexprs = ['arg']
def calculate_constant_result(self):
self.constant_result = tuple(self.base.constant_result)
def compile_time_value(self, denv):
arg = self.arg.compile_time_value(denv)
try:
return tuple(arg)
except Exception, e:
self.compile_time_value_error(e)
def analyse_types(self, env):
self.arg.analyse_types(env)
self.arg = self.arg.coerce_to_pyobject(env)
self.type = tuple_type
self.gil_check(env)
self.is_temp = 1
gil_message = "Constructing Python tuple"
def generate_result_code(self, code):
code.putln(
"%s = PySequence_Tuple(%s); %s" % (
self.result(),
self.arg.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class AttributeNode(NewTempExprNode):
# obj.attribute
#
# obj ExprNode
# 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:
#
# is_py_attr boolean Is a Python getattr operation
# member string C name of struct member
# is_called boolean Function call is being done on result
# entry Entry Symbol table entry of attribute
# interned_attr_cname string C name of interned attribute name
is_attribute = 1
subexprs = ['obj']
type = PyrexTypes.error_type
entry = None
is_called = 0
needs_none_check = True
def as_cython_attribute(self):
if isinstance(self.obj, NameNode) and self.obj.is_cython_module:
return self.attribute
def coerce_to(self, dst_type, env):
# If coercing to a generic pyobject and this is a cpdef function
# we can create the corresponding attribute
if dst_type is py_object_type:
entry = self.entry
if entry and entry.is_cfunction and entry.as_variable:
# must be a cpdef function
self.is_temp = 1
self.entry = entry.as_variable
self.analyse_as_python_attribute(env)
return self
return ExprNode.coerce_to(self, dst_type, env)
def calculate_constant_result(self):
attr = self.attribute
if attr.startswith("__") and attr.endswith("__"):
return
self.constant_result = getattr(self.obj.constant_result, attr)
def compile_time_value(self, denv):
attr = self.attribute
if attr.startswith("__") and attr.endswith("__"):
self.error("Invalid attribute name '%s' in compile-time expression"
% attr)
return None
obj = self.obj.compile_time_value(denv)
try:
return getattr(obj, attr)
except Exception, e:
self.compile_time_value_error(e)
def analyse_target_declaration(self, env):
pass
def analyse_target_types(self, env):
self.analyse_types(env, target = 1)
def analyse_types(self, env, target = 0):
if self.analyse_as_cimported_attribute(env, target):
return
if not target and self.analyse_as_unbound_cmethod(env):
return
self.analyse_as_ordinary_attribute(env, target)
def analyse_as_cimported_attribute(self, env, target):
# Try to interpret this as a reference to an imported
# C const, type, var or function. If successful, mutates
# this node into a NameNode and returns 1, otherwise
# returns 0.
module_scope = self.obj.analyse_as_module(env)
if module_scope:
entry = module_scope.lookup_here(self.attribute)
if entry and (
entry.is_cglobal or entry.is_cfunction
or entry.is_type or entry.is_const):
self.mutate_into_name_node(env, entry, target)
return 1
return 0
def analyse_as_unbound_cmethod(self, env):
# Try to interpret this as a reference to an unbound
# C method of an extension type. If successful, mutates
# this node into a NameNode and returns 1, otherwise
# returns 0.
type = self.obj.analyse_as_extension_type(env)
if type:
entry = type.scope.lookup_here(self.attribute)
if entry and entry.is_cmethod:
# Create a temporary entry describing the C method
# as an ordinary function.
ubcm_entry = Symtab.Entry(entry.name,
"%s->%s" % (type.vtabptr_cname, entry.cname),
entry.type)
ubcm_entry.is_cfunction = 1
ubcm_entry.func_cname = entry.func_cname
ubcm_entry.is_unbound_cmethod = 1
self.mutate_into_name_node(env, ubcm_entry, None)
return 1
return 0
def analyse_as_type(self, env):
module_scope = self.obj.analyse_as_module(env)
if module_scope:
return module_scope.lookup_type(self.attribute)
return None
def analyse_as_extension_type(self, env):
# Try to interpret this as a reference to an extension type
# in a cimported module. Returns the extension type, or None.
module_scope = self.obj.analyse_as_module(env)
if module_scope:
entry = module_scope.lookup_here(self.attribute)
if entry and entry.is_type and entry.type.is_extension_type:
return entry.type
return None
def analyse_as_module(self, env):
# Try to interpret this as a reference to a cimported module
# in another cimported module. Returns the module scope, or None.
module_scope = self.obj.analyse_as_module(env)
if module_scope:
entry = module_scope.lookup_here(self.attribute)
if entry and entry.as_module:
return entry.as_module
return None
def mutate_into_name_node(self, env, entry, target):
# Mutate this node into a NameNode and complete the
# analyse_types phase.
self.__class__ = NameNode
self.name = self.attribute
self.entry = entry
del self.obj
del self.attribute
if target:
NameNode.analyse_target_types(self, env)
else:
NameNode.analyse_rvalue_entry(self, env)
def analyse_as_ordinary_attribute(self, env, target):
self.obj.analyse_types(env)
self.analyse_attribute(env)
if self.entry and self.entry.is_cmethod and not self.is_called:
# error(self.pos, "C method can only be called")
pass
## Reference to C array turns into pointer to first element.
#while self.type.is_array:
# self.type = self.type.element_ptr_type()
if self.is_py_attr:
if not target:
self.is_temp = 1
self.result_ctype = py_object_type
def analyse_attribute(self, env):
# Look up attribute and set self.type and self.member.
self.is_py_attr = 0
self.member = self.attribute
if self.obj.type.is_string:
self.obj = self.obj.coerce_to_pyobject(env)
obj_type = self.obj.type
if obj_type.is_ptr or obj_type.is_array:
obj_type = obj_type.base_type
self.op = "->"
elif obj_type.is_extension_type:
self.op = "->"
else:
self.op = "."
if obj_type.has_attributes:
entry = None
if obj_type.attributes_known():
entry = obj_type.scope.lookup_here(self.attribute)
if entry and entry.is_member:
entry = None
else:
error(self.pos,
"Cannot select attribute of incomplete type '%s'"
% obj_type)
self.type = PyrexTypes.error_type
return
self.entry = entry
if entry:
if obj_type.is_extension_type and entry.name == "__weakref__":
error(self.pos, "Illegal use of special attribute __weakref__")
# methods need the normal attribute lookup
# because they do not have struct entries
if entry.is_variable or entry.is_cmethod:
self.type = entry.type
self.member = entry.cname
return
else:
# If it's not a variable or C method, it must be a Python
# method of an extension type, so we treat it like a Python
# attribute.
pass
# If we get here, the base object is not a struct/union/extension
# type, or it is an extension type and the attribute is either not
# declared or is declared as a Python method. Treat it as a Python
# attribute reference.
self.analyse_as_python_attribute(env)
def analyse_as_python_attribute(self, env):
obj_type = self.obj.type
self.member = self.attribute
if obj_type.is_pyobject:
self.type = py_object_type
self.is_py_attr = 1
self.interned_attr_cname = env.intern_identifier(self.attribute)
self.gil_check(env)
else:
if not obj_type.is_error:
error(self.pos,
"Object of type '%s' has no attribute '%s'" %
(obj_type, self.attribute))
gil_message = "Accessing Python attribute"
def is_simple(self):
if self.obj:
return self.result_in_temp() or self.obj.is_simple()
else:
return NameNode.is_simple(self)
def is_lvalue(self):
if self.obj:
return 1
else:
return NameNode.is_lvalue(self)
def is_ephemeral(self):
if self.obj:
return self.obj.is_ephemeral()
else:
return NameNode.is_ephemeral(self)
def calculate_result_code(self):
#print "AttributeNode.calculate_result_code:", self.member ###
#print "...obj node =", self.obj, "code", self.obj.result() ###
#print "...obj type", self.obj.type, "ctype", self.obj.ctype() ###
obj = self.obj
obj_code = obj.result_as(obj.type)
#print "...obj_code =", obj_code ###
if self.entry and self.entry.is_cmethod:
if obj.type.is_extension_type:
return "((struct %s *)%s%s%s)->%s" % (
obj.type.vtabstruct_cname, obj_code, self.op,
obj.type.vtabslot_cname, self.member)
else:
return self.member
else:
return "%s%s%s" % (obj_code, self.op, self.member)
def generate_result_code(self, code):
if self.is_py_attr:
code.putln(
'%s = PyObject_GetAttr(%s, %s); %s' % (
self.result(),
self.obj.py_result(),
self.interned_attr_cname,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
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):
self.obj.generate_evaluation_code(code)
if self.is_py_attr:
code.put_error_if_neg(self.pos,
'PyObject_SetAttr(%s, %s, %s)' % (
self.obj.py_result(),
self.interned_attr_cname,
rhs.py_result()))
rhs.generate_disposal_code(code)
rhs.free_temps(code)
else:
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:
rhs.make_owned_reference(code)
code.put_decref(select_code, self.ctype())
code.putln(
"%s = %s;" % (
select_code,
rhs.result_as(self.ctype())))
#rhs.result()))
rhs.generate_post_assignment_code(code)
rhs.free_temps(code)
self.obj.generate_disposal_code(code)
self.obj.free_temps(code)
def generate_deletion_code(self, code):
self.obj.generate_evaluation_code(code)
if self.is_py_attr:
code.put_error_if_neg(self.pos,
'PyObject_DelAttr(%s, %s)' % (
self.obj.py_result(),
self.interned_attr_cname))
else:
error(self.pos, "Cannot delete C attribute of extension type")
self.obj.generate_disposal_code(code)
self.obj.free_temps(code)
def annotate(self, code):
if self.is_py_attr:
code.annotate(self.pos, AnnotationItem('py_attr', 'python attribute', size=len(self.attribute)))
else:
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
#
#-------------------------------------------------------------------
class SequenceNode(NewTempExprNode):
# Base class for list and tuple constructor nodes.
# Contains common code for performing sequence unpacking.
#
# args [ExprNode]
# iterator ExprNode
# unpacked_items [ExprNode] or None
# coerced_unpacked_items [ExprNode] or None
subexprs = ['args']
is_sequence_constructor = 1
unpacked_items = None
def compile_time_value_list(self, denv):
return [arg.compile_time_value(denv) for arg in self.args]
def analyse_target_declaration(self, env):
for arg in self.args:
arg.analyse_target_declaration(env)
def analyse_types(self, env, skip_children=False):
for i in range(len(self.args)):
arg = self.args[i]
if not skip_children: arg.analyse_types(env)
self.args[i] = arg.coerce_to_pyobject(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
def analyse_target_types(self, env):
self.iterator = PyTempNode(self.pos, env)
self.unpacked_items = []
self.coerced_unpacked_items = []
for arg in self.args:
arg.analyse_target_types(env)
unpacked_item = PyTempNode(self.pos, env)
coerced_unpacked_item = unpacked_item.coerce_to(arg.type, env)
self.unpacked_items.append(unpacked_item)
self.coerced_unpacked_items.append(coerced_unpacked_item)
self.type = py_object_type
env.use_utility_code(unpacking_utility_code)
def allocate_target_temps(self, env, rhs):
self.iterator.allocate_temps(env)
for arg, node in zip(self.args, self.coerced_unpacked_items):
node.allocate_temps(env)
arg.allocate_target_temps(env, None)
#arg.release_target_temp(env)
#node.release_temp(env)
if rhs:
rhs.release_temp(env)
self.iterator.release_temp(env)
for node in self.coerced_unpacked_items:
node.release_temp(env)
# def release_target_temp(self, env):
# #for arg in self.args:
# # arg.release_target_temp(env)
# #for node in self.coerced_unpacked_items:
# # node.release_temp(env)
# self.iterator.release_temp(env)
def generate_result_code(self, code):
self.generate_operation_code(code)
def generate_assignment_code(self, rhs, code):
# Need to work around the fact that generate_evaluation_code
# allocates the temps in a rather hacky way -- the assignment
# is evaluated twice, within each if-block.
code.putln(
"if (PyTuple_CheckExact(%s) && PyTuple_GET_SIZE(%s) == %s) {" % (
rhs.py_result(),
rhs.py_result(),
len(self.args)))
code.putln("PyObject* tuple = %s;" % rhs.py_result())
for i in range(len(self.args)):
item = self.unpacked_items[i]
code.put(
"%s = PyTuple_GET_ITEM(tuple, %s); " % (
item.result(),
i))
code.put_incref(item.result(), item.ctype())
value_node = self.coerced_unpacked_items[i]
value_node.generate_evaluation_code(code)
rhs.generate_disposal_code(code)
for i in range(len(self.args)):
self.args[i].generate_assignment_code(
self.coerced_unpacked_items[i], code)
code.putln("} else {")
code.putln(
"%s = PyObject_GetIter(%s); %s" % (
self.iterator.result(),
rhs.py_result(),
code.error_goto_if_null(self.iterator.result(), self.pos)))
code.put_gotref(self.iterator.py_result())
rhs.generate_disposal_code(code)
for i in range(len(self.args)):
item = self.unpacked_items[i]
unpack_code = "__Pyx_UnpackItem(%s, %d)" % (
self.iterator.py_result(), i)
code.putln(
"%s = %s; %s" % (
item.result(),
typecast(item.ctype(), py_object_type, unpack_code),
code.error_goto_if_null(item.result(), self.pos)))
code.put_gotref(item.py_result())
value_node = self.coerced_unpacked_items[i]
value_node.generate_evaluation_code(code)
code.put_error_if_neg(self.pos,
"__Pyx_EndUnpack(%s)" % (
self.iterator.py_result()))
if debug_disposal_code:
print("UnpackNode.generate_assignment_code:")
print("...generating disposal code for %s" % self.iterator)
self.iterator.generate_disposal_code(code)
self.iterator.free_temps(code)
for i in range(len(self.args)):
self.args[i].generate_assignment_code(
self.coerced_unpacked_items[i], code)
code.putln("}")
rhs.free_temps(code)
def annotate(self, code):
for arg in self.args:
arg.annotate(code)
if self.unpacked_items:
for arg in self.unpacked_items:
arg.annotate(code)
for arg in self.coerced_unpacked_items:
arg.annotate(code)
class TupleNode(SequenceNode):
# Tuple constructor.
gil_message = "Constructing Python tuple"
def analyse_types(self, env, skip_children=False):
if len(self.args) == 0:
self.is_temp = 0
self.is_literal = 1
else:
SequenceNode.analyse_types(self, env, skip_children)
self.type = tuple_type
def calculate_result_code(self):
if len(self.args) > 0:
error(self.pos, "Positive length tuples must be constructed.")
else:
return Naming.empty_tuple
def calculate_constant_result(self):
self.constant_result = tuple([
arg.constant_result for arg in self.args])
def compile_time_value(self, denv):
values = self.compile_time_value_list(denv)
try:
return tuple(values)
except Exception, e:
self.compile_time_value_error(e)
def generate_operation_code(self, code):
if len(self.args) == 0:
# result_code is Naming.empty_tuple
return
code.putln(
"%s = PyTuple_New(%s); %s" % (
self.result(),
len(self.args),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
for i in range(len(self.args)):
arg = self.args[i]
if not arg.result_in_temp():
code.put_incref(arg.result(), arg.ctype())
code.putln(
"PyTuple_SET_ITEM(%s, %s, %s);" % (
self.result(),
i,
arg.py_result()))
code.put_giveref(arg.py_result())
def generate_subexpr_disposal_code(self, code):
# We call generate_post_assignment_code here instead
# of generate_disposal_code, because values were stored
# in the tuple using a reference-stealing operation.
for arg in self.args:
arg.generate_post_assignment_code(code)
# Should NOT call free_temps -- this is invoked by the default
# generate_evaluation_code which will do that.
class ListNode(SequenceNode):
# List constructor.
# obj_conversion_errors [PyrexError] used internally
# orignial_args [ExprNode] used internally
gil_message = "Constructing Python list"
def analyse_expressions(self, env):
SequenceNode.analyse_expressions(self, env)
self.coerce_to_pyobject(env)
def analyse_types(self, env):
hold_errors()
self.original_args = list(self.args)
SequenceNode.analyse_types(self, env)
self.type = list_type
self.obj_conversion_errors = held_errors()
release_errors(ignore=True)
def coerce_to(self, dst_type, env):
if dst_type.is_pyobject:
for err in self.obj_conversion_errors:
report_error(err)
self.obj_conversion_errors = []
if not self.type.subtype_of(dst_type):
error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
elif dst_type.is_ptr:
base_type = dst_type.base_type
self.type = PyrexTypes.CArrayType(base_type, len(self.args))
for i in range(len(self.original_args)):
arg = self.args[i]
if isinstance(arg, CoerceToPyTypeNode):
arg = arg.arg
self.args[i] = arg.coerce_to(base_type, env)
elif dst_type.is_struct:
if len(self.args) > len(dst_type.scope.var_entries):
error(self.pos, "Too may members for '%s'" % dst_type)
else:
if len(self.args) < len(dst_type.scope.var_entries):
warning(self.pos, "Too few members for '%s'" % dst_type, 1)
for i, (arg, member) in enumerate(zip(self.original_args, dst_type.scope.var_entries)):
if isinstance(arg, CoerceToPyTypeNode):
arg = arg.arg
self.args[i] = arg.coerce_to(member.type, env)
self.type = dst_type
else:
self.type = error_type
error(self.pos, "Cannot coerce list to type '%s'" % dst_type)
return self
def release_temp(self, env):
if self.type.is_array:
# To be valid C++, we must allocate the memory on the stack
# manually and be sure not to reuse it for something else.
pass
else:
SequenceNode.release_temp(self, env)
def calculate_constant_result(self):
self.constant_result = [
arg.constant_result for arg in self.args]
def compile_time_value(self, denv):
return self.compile_time_value_list(denv)
def generate_operation_code(self, code):
if self.type.is_pyobject:
for err in self.obj_conversion_errors:
report_error(err)
code.putln("%s = PyList_New(%s); %s" %
(self.result(),
len(self.args),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
for i in range(len(self.args)):
arg = self.args[i]
#if not arg.is_temp:
if not arg.result_in_temp():
code.put_incref(arg.result(), arg.ctype())
code.putln("PyList_SET_ITEM(%s, %s, %s);" %
(self.result(),
i,
arg.py_result()))
code.put_giveref(arg.py_result())
elif self.type.is_array:
for i, arg in enumerate(self.args):
code.putln("%s[%s] = %s;" % (
self.result(),
i,
arg.result()))
elif self.type.is_struct:
for arg, member in zip(self.args, self.type.scope.var_entries):
code.putln("%s.%s = %s;" % (
self.result(),
member.cname,
arg.result()))
else:
raise InternalError("List type never specified")
def generate_subexpr_disposal_code(self, code):
# We call generate_post_assignment_code here instead
# of generate_disposal_code, because values were stored
# in the list using a reference-stealing operation.
for arg in self.args:
arg.generate_post_assignment_code(code)
# Should NOT call free_temps -- this is invoked by the default
# generate_evaluation_code which will do that.
class ComprehensionNode(NewTempExprNode):
subexprs = ["target"]
child_attrs = ["loop", "append"]
def analyse_types(self, env):
self.target.analyse_expressions(env)
self.type = self.target.type
self.append.target = self # this is a CloneNode used in the PyList_Append in the inner loop
self.loop.analyse_declarations(env)
def allocate_temps(self, env, result = None):
if debug_temp_alloc:
print("%s Allocating temps" % self)
self.allocate_temp(env, result)
# call loop.analyse_expressions() now to make sure temps get
# allocated at the right time
self.loop.analyse_expressions(env)
def calculate_result_code(self):
return self.target.result()
def generate_result_code(self, code):
self.generate_operation_code(code)
def generate_operation_code(self, code):
self.loop.generate_execution_code(code)
def annotate(self, code):
self.loop.annotate(code)
class ComprehensionAppendNode(NewTempExprNode):
# Need to be careful to avoid infinite recursion:
# target must not be in child_attrs/subexprs
subexprs = ['expr']
def analyse_types(self, env):
self.expr.analyse_types(env)
if not self.expr.type.is_pyobject:
self.expr = self.expr.coerce_to_pyobject(env)
self.type = PyrexTypes.c_int_type
self.is_temp = 1
def generate_result_code(self, code):
if self.target.type is list_type:
function = "PyList_Append"
elif self.target.type is set_type:
function = "PySet_Add"
else:
raise InternalError(
"Invalid type for comprehension node: %s" % self.target.type)
code.putln("%s = %s(%s, (PyObject*)%s); %s" %
(self.result(),
function,
self.target.result(),
self.expr.result(),
code.error_goto_if(self.result(), self.pos)))
class DictComprehensionAppendNode(ComprehensionAppendNode):
subexprs = ['key_expr', 'value_expr']
def analyse_types(self, env):
self.key_expr.analyse_types(env)
if not self.key_expr.type.is_pyobject:
self.key_expr = self.key_expr.coerce_to_pyobject(env)
self.value_expr.analyse_types(env)
if not self.value_expr.type.is_pyobject:
self.value_expr = self.value_expr.coerce_to_pyobject(env)
self.type = PyrexTypes.c_int_type
self.is_temp = 1
def generate_result_code(self, code):
code.putln("%s = PyDict_SetItem(%s, (PyObject*)%s, (PyObject*)%s); %s" %
(self.result(),
self.target.result(),
self.key_expr.result(),
self.value_expr.result(),
code.error_goto_if(self.result(), self.pos)))
class SetNode(NewTempExprNode):
# Set constructor.
subexprs = ['args']
gil_message = "Constructing Python set"
def analyse_types(self, env):
for i in range(len(self.args)):
arg = self.args[i]
arg.analyse_types(env)
self.args[i] = arg.coerce_to_pyobject(env)
self.type = set_type
self.gil_check(env)
self.is_temp = 1
def calculate_constant_result(self):
self.constant_result = set([
arg.constant_result for arg in self.args])
def compile_time_value(self, denv):
values = [arg.compile_time_value(denv) for arg in self.args]
try:
return set(values)
except Exception, e:
self.compile_time_value_error(e)
def generate_evaluation_code(self, code):
code.globalstate.use_utility_code(Builtin.py23_set_utility_code)
self.allocate_temp_result(code)
code.putln(
"%s = PySet_New(0); %s" % (
self.result(),
code.error_goto_if_null(self.result(), self.pos)))
for arg in self.args:
arg.generate_evaluation_code(code)
code.putln(
code.error_goto_if_neg(
"PySet_Add(%s, %s)" % (self.result(), arg.py_result()),
self.pos))
arg.generate_disposal_code(code)
arg.free_temps(code)
class DictNode(ExprNode):
# Dictionary constructor.
#
# key_value_pairs [DictItemNode]
#
# obj_conversion_errors [PyrexError] used internally
subexprs = ['key_value_pairs']
def calculate_constant_result(self):
self.constant_result = dict([
item.constant_result for item in self.key_value_pairs])
def compile_time_value(self, denv):
pairs = [(item.key.compile_time_value(denv), item.value.compile_time_value(denv))
for item in self.key_value_pairs]
try:
return dict(pairs)
except Exception, e:
self.compile_time_value_error(e)
def analyse_types(self, env):
hold_errors()
self.type = dict_type
for item in self.key_value_pairs:
item.analyse_types(env)
self.gil_check(env)
self.obj_conversion_errors = held_errors()
release_errors(ignore=True)
self.is_temp = 1
def coerce_to(self, dst_type, env):
if dst_type.is_pyobject:
self.release_errors()
if not self.type.subtype_of(dst_type):
error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
elif dst_type.is_struct_or_union:
self.type = dst_type
if not dst_type.is_struct and len(self.key_value_pairs) != 1:
error(self.pos, "Exactly one field must be specified to convert to union '%s'" % dst_type)
elif dst_type.is_struct and len(self.key_value_pairs) < len(dst_type.scope.var_entries):
warning(self.pos, "Not all members given for struct '%s'" % dst_type, 1)
for item in self.key_value_pairs:
if isinstance(item.key, CoerceToPyTypeNode):
item.key = item.key.arg
if not isinstance(item.key, (StringNode, IdentifierStringNode)):
error(item.key.pos, "Invalid struct field identifier")
item.key = IdentifierStringNode(item.key.pos, value="")
else:
member = dst_type.scope.lookup_here(item.key.value)
if not member:
error(item.key.pos, "struct '%s' has no field '%s'" % (dst_type, item.key.value))
else:
value = item.value
if isinstance(value, CoerceToPyTypeNode):
value = value.arg
item.value = value.coerce_to(member.type, env)
else:
self.type = error_type
error(self.pos, "Cannot interpret dict as type '%s'" % dst_type)
return self
def release_errors(self):
for err in self.obj_conversion_errors:
report_error(err)
self.obj_conversion_errors = []
gil_message = "Constructing Python dict"
def allocate_temps(self, env, result = None):
# Custom method used here because key-value
# pairs are evaluated and used one at a time.
self.allocate_temp(env, result)
for item in self.key_value_pairs:
item.key.allocate_temps(env)
item.value.allocate_temps(env)
item.key.release_temp(env)
item.value.release_temp(env)
def generate_evaluation_code(self, code):
# Custom method used here because key-value
# pairs are evaluated and used one at a time.
if self.type.is_pyobject:
self.release_errors()
code.putln(
"%s = PyDict_New(); %s" % (
self.result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
for item in self.key_value_pairs:
item.generate_evaluation_code(code)
if self.type.is_pyobject:
code.put_error_if_neg(self.pos,
"PyDict_SetItem(%s, %s, %s)" % (
self.result(),
item.key.py_result(),
item.value.py_result()))
else:
code.putln("%s.%s = %s;" % (
self.result(),
item.key.value,
item.value.result()))
item.generate_disposal_code(code)
item.free_temps(code)
def annotate(self, code):
for item in self.key_value_pairs:
item.annotate(code)
class DictItemNode(ExprNode):
# Represents a single item in a DictNode
#
# key ExprNode
# value ExprNode
subexprs = ['key', 'value']
def calculate_constant_result(self):
self.constant_result = (
self.key.constant_result, self.value.constant_result)
def analyse_types(self, env):
self.key.analyse_types(env)
self.value.analyse_types(env)
self.key = self.key.coerce_to_pyobject(env)
self.value = self.value.coerce_to_pyobject(env)
def generate_evaluation_code(self, code):
self.key.generate_evaluation_code(code)
self.value.generate_evaluation_code(code)
def generate_disposal_code(self, code):
self.key.generate_disposal_code(code)
self.value.generate_disposal_code(code)
def free_temps(self, code):
self.key.free_temps(code)
self.value.free_temps(code)
def __iter__(self):
return iter([self.key, self.value])
class ClassNode(ExprNode):
# Helper class used in the implementation of Python
# class definitions. Constructs a class object given
# a name, tuple of bases and class dictionary.
#
# name EncodedString Name of the class
# cname string Class name as a Python string
# bases ExprNode Base class tuple
# dict ExprNode Class dict (not owned by this node)
# doc ExprNode or None Doc string
# module_name string Name of defining module
subexprs = ['bases', 'doc']
def analyse_types(self, env):
self.cname = env.intern_identifier(self.name)
self.bases.analyse_types(env)
if self.doc:
self.doc.analyse_types(env)
self.doc = self.doc.coerce_to_pyobject(env)
self.module_name = env.global_scope().qualified_name
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
env.use_utility_code(create_class_utility_code);
gil_message = "Constructing Python class"
def generate_result_code(self, code):
if self.doc:
code.put_error_if_neg(self.pos,
'PyDict_SetItemString(%s, "__doc__", %s)' % (
self.dict.py_result(),
self.doc.py_result()))
code.putln(
'%s = __Pyx_CreateClass(%s, %s, %s, "%s"); %s' % (
self.result(),
self.bases.py_result(),
self.dict.py_result(),
self.cname,
self.module_name,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class UnboundMethodNode(ExprNode):
# Helper class used in the implementation of Python
# class definitions. Constructs an unbound method
# object from a class and a function.
#
# class_cname string C var holding the class object
# function ExprNode Function object
subexprs = ['function']
def analyse_types(self, env):
self.function.analyse_types(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
gil_message = "Constructing an unbound method"
def generate_result_code(self, code):
code.putln(
"%s = PyMethod_New(%s, 0, %s); %s" % (
self.result(),
self.function.py_result(),
self.class_cname,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class PyCFunctionNode(AtomicNewTempExprNode):
# Helper class used in the implementation of Python
# class definitions. Constructs a PyCFunction object
# from a PyMethodDef struct.
#
# pymethdef_cname string PyMethodDef structure
def analyse_types(self, env):
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
gil_message = "Constructing Python function"
def generate_result_code(self, code):
code.putln(
"%s = PyCFunction_New(&%s, 0); %s" % (
self.result(),
self.pymethdef_cname,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
#-------------------------------------------------------------------
#
# Unary operator nodes
#
#-------------------------------------------------------------------
compile_time_unary_operators = {
'not': operator.not_,
'~': operator.inv,
'-': operator.neg,
'+': operator.pos,
}
class UnopNode(ExprNode):
# operator string
# operand ExprNode
#
# Processing during analyse_expressions phase:
#
# analyse_c_operation
# Called when the operand is not a pyobject.
# - Check operand type and coerce if needed.
# - Determine result type and result code fragment.
# - Allocate temporary for result if needed.
subexprs = ['operand']
def calculate_constant_result(self):
func = compile_time_unary_operators[self.operator]
self.constant_result = func(self.operand.constant_result)
def compile_time_value(self, denv):
func = compile_time_unary_operators.get(self.operator)
if not func:
error(self.pos,
"Unary '%s' not supported in compile-time expression"
% self.operator)
operand = self.operand.compile_time_value(denv)
try:
return func(operand)
except Exception, e:
self.compile_time_value_error(e)
def analyse_types(self, env):
self.operand.analyse_types(env)
if self.is_py_operation():
self.coerce_operand_to_pyobject(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
else:
self.analyse_c_operation(env)
def check_const(self):
self.operand.check_const()
def is_py_operation(self):
return self.operand.type.is_pyobject
def coerce_operand_to_pyobject(self, env):
self.operand = self.operand.coerce_to_pyobject(env)
def generate_result_code(self, code):
if self.operand.type.is_pyobject:
self.generate_py_operation_code(code)
else:
if self.is_temp:
self.generate_c_operation_code(code)
def generate_py_operation_code(self, code):
function = self.py_operation_function()
code.putln(
"%s = %s(%s); %s" % (
self.result(),
function,
self.operand.py_result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
def type_error(self):
if not self.operand.type.is_error:
error(self.pos, "Invalid operand type for '%s' (%s)" %
(self.operator, self.operand.type))
self.type = PyrexTypes.error_type
class NotNode(ExprNode):
# 'not' operator
#
# operand ExprNode
def calculate_constant_result(self):
self.constant_result = not self.operand.constant_result
def compile_time_value(self, denv):
operand = self.operand.compile_time_value(denv)
try:
return not operand
except Exception, e:
self.compile_time_value_error(e)
subexprs = ['operand']
def analyse_types(self, env):
self.operand.analyse_types(env)
self.operand = self.operand.coerce_to_boolean(env)
self.type = PyrexTypes.c_bint_type
def calculate_result_code(self):
return "(!%s)" % self.operand.result()
def generate_result_code(self, code):
pass
class UnaryPlusNode(UnopNode):
# unary '+' operator
operator = '+'
def analyse_c_operation(self, env):
self.type = self.operand.type
def py_operation_function(self):
return "PyNumber_Positive"
def calculate_result_code(self):
return self.operand.result()
class UnaryMinusNode(UnopNode):
# unary '-' operator
operator = '-'
def analyse_c_operation(self, env):
if self.operand.type.is_numeric:
self.type = self.operand.type
else:
self.type_error()
def py_operation_function(self):
return "PyNumber_Negative"
def calculate_result_code(self):
return "(-%s)" % self.operand.result()
class TildeNode(UnopNode):
# unary '~' operator
def analyse_c_operation(self, env):
if self.operand.type.is_int:
self.type = self.operand.type
else:
self.type_error()
def py_operation_function(self):
return "PyNumber_Invert"
def calculate_result_code(self):
return "(~%s)" % self.operand.result()
class AmpersandNode(ExprNode):
# The C address-of operator.
#
# operand ExprNode
subexprs = ['operand']
def analyse_types(self, env):
self.operand.analyse_types(env)
argtype = self.operand.type
if not (argtype.is_cfunction or self.operand.is_lvalue()):
self.error("Taking address of non-lvalue")
return
if argtype.is_pyobject:
self.error("Cannot take address of Python variable")
return
self.type = PyrexTypes.c_ptr_type(argtype)
def check_const(self):
self.operand.check_const_addr()
def error(self, mess):
error(self.pos, mess)
self.type = PyrexTypes.error_type
self.result_code = ""
def calculate_result_code(self):
return "(&%s)" % self.operand.result()
def generate_result_code(self, code):
pass
unop_node_classes = {
"+": UnaryPlusNode,
"-": UnaryMinusNode,
"~": TildeNode,
}
def unop_node(pos, operator, operand):
# Construct unnop node of appropriate class for
# given operator.
if isinstance(operand, IntNode) and operator == '-':
return IntNode(pos = operand.pos, value = str(-int(operand.value, 0)))
elif isinstance(operand, UnopNode) and operand.operator == operator:
warning(pos, "Python has no increment/decrement operator: %s%sx = %s(%sx) = x" % ((operator,)*4), 5)
return unop_node_classes[operator](pos,
operator = operator,
operand = operand)
class TypecastNode(NewTempExprNode):
# C type cast
#
# operand ExprNode
# base_type CBaseTypeNode
# declarator CDeclaratorNode
#
# If used from a transform, one can if wanted specify the attribute
# "type" directly and leave base_type and declarator to None
subexprs = ['operand']
base_type = declarator = type = None
def analyse_types(self, env):
if self.type is None:
base_type = self.base_type.analyse(env)
_, self.type = self.declarator.analyse(base_type, env)
if self.type.is_cfunction:
error(self.pos,
"Cannot cast to a function type")
self.type = PyrexTypes.error_type
self.operand.analyse_types(env)
to_py = self.type.is_pyobject
from_py = self.operand.type.is_pyobject
if from_py and not to_py and self.operand.is_ephemeral() and not self.type.is_numeric:
error(self.pos, "Casting temporary Python object to non-numeric non-Python type")
if to_py and not from_py:
if (self.operand.type.to_py_function and
self.operand.type.create_convert_utility_code(env)):
self.result_ctype = py_object_type
self.operand = self.operand.coerce_to_pyobject(env)
else:
warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.operand.type, self.type))
self.operand = self.operand.coerce_to_simple(env)
elif from_py and not to_py:
if self.type.from_py_function:
self.operand = self.operand.coerce_to(self.type, env)
else:
warning(self.pos, "No conversion from %s to %s, python object pointer used." % (self.type, self.operand.type))
elif from_py and to_py:
if self.typecheck and self.type.is_extension_type:
self.operand = PyTypeTestNode(self.operand, self.type, env)
def check_const(self):
self.operand.check_const()
def calculate_constant_result(self):
self.constant_result = self.operand.constant_result
def calculate_result_code(self):
opnd = self.operand
return self.type.cast_code(opnd.result())
def result_as(self, type):
if self.type.is_pyobject and not self.is_temp:
# Optimise away some unnecessary casting
return self.operand.result_as(type)
else:
return ExprNode.result_as(self, type)
def generate_result_code(self, code):
if self.is_temp:
code.putln(
"%s = (PyObject *)%s;" % (
self.result(),
self.operand.result()))
code.put_incref(self.result(), self.ctype())
class SizeofNode(ExprNode):
# Abstract base class for sizeof(x) expression nodes.
type = PyrexTypes.c_int_type
def check_const(self):
pass
def generate_result_code(self, code):
pass
class SizeofTypeNode(SizeofNode):
# C sizeof function applied to a type
#
# base_type CBaseTypeNode
# declarator CDeclaratorNode
subexprs = []
arg_type = None
def analyse_types(self, env):
# we may have incorrectly interpreted a dotted name as a type rather than an attribute
# this could be better handled by more uniformly treating types as runtime-available objects
if 0 and self.base_type.module_path:
path = self.base_type.module_path
obj = env.lookup(path[0])
if obj.as_module is None:
operand = NameNode(pos=self.pos, name=path[0])
for attr in path[1:]:
operand = AttributeNode(pos=self.pos, obj=operand, attribute=attr)
operand = AttributeNode(pos=self.pos, obj=operand, attribute=self.base_type.name)
self.operand = operand
self.__class__ = SizeofVarNode
self.analyse_types(env)
return
if self.arg_type is None:
base_type = self.base_type.analyse(env)
_, arg_type = self.declarator.analyse(base_type, env)
self.arg_type = arg_type
self.check_type()
def check_type(self):
arg_type = self.arg_type
if arg_type.is_pyobject and not arg_type.is_extension_type:
error(self.pos, "Cannot take sizeof Python object")
elif arg_type.is_void:
error(self.pos, "Cannot take sizeof void")
elif not arg_type.is_complete():
error(self.pos, "Cannot take sizeof incomplete type '%s'" % arg_type)
def calculate_result_code(self):
if self.arg_type.is_extension_type:
# the size of the pointer is boring
# we want the size of the actual struct
arg_code = self.arg_type.declaration_code("", deref=1)
else:
arg_code = self.arg_type.declaration_code("")
return "(sizeof(%s))" % arg_code
class SizeofVarNode(SizeofNode):
# C sizeof function applied to a variable
#
# operand ExprNode
subexprs = ['operand']
def analyse_types(self, env):
# We may actually be looking at a type rather than a variable...
# If we are, traditional analysis would fail...
operand_as_type = self.operand.analyse_as_type(env)
if operand_as_type:
self.arg_type = operand_as_type
self.__class__ = SizeofTypeNode
self.check_type()
else:
self.operand.analyse_types(env)
def calculate_result_code(self):
return "(sizeof(%s))" % self.operand.result()
def generate_result_code(self, code):
pass
#-------------------------------------------------------------------
#
# Binary operator nodes
#
#-------------------------------------------------------------------
def _not_in(x, seq):
return x not in seq
compile_time_binary_operators = {
'<': operator.lt,
'<=': operator.le,
'==': operator.eq,
'!=': operator.ne,
'>=': operator.ge,
'>': operator.gt,
'is': operator.is_,
'is_not': operator.is_not,
'+': operator.add,
'&': operator.and_,
'/': operator.div,
'//': operator.floordiv,
'<<': operator.lshift,
'%': operator.mod,
'*': operator.mul,
'|': operator.or_,
'**': operator.pow,
'>>': operator.rshift,
'-': operator.sub,
#'/': operator.truediv,
'^': operator.xor,
'in': operator.contains,
'not_in': _not_in,
}
def get_compile_time_binop(node):
func = compile_time_binary_operators.get(node.operator)
if not func:
error(node.pos,
"Binary '%s' not supported in compile-time expression"
% node.operator)
return func
class BinopNode(NewTempExprNode):
# operator string
# operand1 ExprNode
# operand2 ExprNode
#
# Processing during analyse_expressions phase:
#
# analyse_c_operation
# Called when neither operand is a pyobject.
# - Check operand types and coerce if needed.
# - Determine result type and result code fragment.
# - Allocate temporary for result if needed.
subexprs = ['operand1', 'operand2']
def calculate_constant_result(self):
func = compile_time_binary_operators[self.operator]
self.constant_result = func(
self.operand1.constant_result,
self.operand2.constant_result)
def compile_time_value(self, denv):
func = get_compile_time_binop(self)
operand1 = self.operand1.compile_time_value(denv)
operand2 = self.operand2.compile_time_value(denv)
try:
return func(operand1, operand2)
except Exception, e:
self.compile_time_value_error(e)
def analyse_types(self, env):
self.operand1.analyse_types(env)
self.operand2.analyse_types(env)
if self.is_py_operation():
self.coerce_operands_to_pyobjects(env)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
if Options.incref_local_binop and self.operand1.type.is_pyobject:
self.operand1 = self.operand1.coerce_to_temp(env)
else:
self.analyse_c_operation(env)
def is_py_operation(self):
return (self.operand1.type.is_pyobject
or self.operand2.type.is_pyobject)
def coerce_operands_to_pyobjects(self, env):
self.operand1 = self.operand1.coerce_to_pyobject(env)
self.operand2 = self.operand2.coerce_to_pyobject(env)
def check_const(self):
self.operand1.check_const()
self.operand2.check_const()
def generate_result_code(self, code):
#print "BinopNode.generate_result_code:", self.operand1, self.operand2 ###
if self.operand1.type.is_pyobject:
function = self.py_operation_function()
if function == "PyNumber_Power":
extra_args = ", Py_None"
else:
extra_args = ""
code.putln(
"%s = %s(%s, %s%s); %s" % (
self.result(),
function,
self.operand1.py_result(),
self.operand2.py_result(),
extra_args,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
else:
if self.is_temp:
self.generate_c_operation_code(code)
def type_error(self):
if not (self.operand1.type.is_error
or self.operand2.type.is_error):
error(self.pos, "Invalid operand types for '%s' (%s; %s)" %
(self.operator, self.operand1.type,
self.operand2.type))
self.type = PyrexTypes.error_type
class NumBinopNode(BinopNode):
# Binary operation taking numeric arguments.
def analyse_c_operation(self, env):
type1 = self.operand1.type
type2 = self.operand2.type
self.type = self.compute_c_result_type(type1, type2)
if not self.type:
self.type_error()
def compute_c_result_type(self, type1, type2):
if self.c_types_okay(type1, type2):
return PyrexTypes.widest_numeric_type(type1, type2)
else:
return None
def c_types_okay(self, type1, type2):
#print "NumBinopNode.c_types_okay:", type1, type2 ###
return (type1.is_numeric or type1.is_enum) \
and (type2.is_numeric or type2.is_enum)
def calculate_result_code(self):
return "(%s %s %s)" % (
self.operand1.result(),
self.operator,
self.operand2.result())
def py_operation_function(self):
return self.py_functions[self.operator]
py_functions = {
"|": "PyNumber_Or",
"^": "PyNumber_Xor",
"&": "PyNumber_And",
"<<": "PyNumber_Lshift",
">>": "PyNumber_Rshift",
"+": "PyNumber_Add",
"-": "PyNumber_Subtract",
"*": "PyNumber_Multiply",
"/": "__Pyx_PyNumber_Divide",
"//": "PyNumber_FloorDivide",
"%": "PyNumber_Remainder",
"**": "PyNumber_Power"
}
class IntBinopNode(NumBinopNode):
# Binary operation taking integer arguments.
def c_types_okay(self, type1, type2):
#print "IntBinopNode.c_types_okay:", type1, type2 ###
return (type1.is_int or type1.is_enum) \
and (type2.is_int or type2.is_enum)
class AddNode(NumBinopNode):
# '+' operator.
def is_py_operation(self):
if self.operand1.type.is_string \
and self.operand2.type.is_string:
return 1
else:
return NumBinopNode.is_py_operation(self)
def compute_c_result_type(self, type1, type2):
#print "AddNode.compute_c_result_type:", type1, self.operator, type2 ###
if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
return type1
elif (type2.is_ptr or type2.is_array) and (type1.is_int or type1.is_enum):
return type2
else:
return NumBinopNode.compute_c_result_type(
self, type1, type2)
class SubNode(NumBinopNode):
# '-' operator.
def compute_c_result_type(self, type1, type2):
if (type1.is_ptr or type1.is_array) and (type2.is_int or type2.is_enum):
return type1
elif (type1.is_ptr or type1.is_array) and (type2.is_ptr or type2.is_array):
return PyrexTypes.c_int_type
else:
return NumBinopNode.compute_c_result_type(
self, type1, type2)
class MulNode(NumBinopNode):
# '*' operator.
def is_py_operation(self):
type1 = self.operand1.type
type2 = self.operand2.type
if (type1.is_string and type2.is_int) \
or (type2.is_string and type1.is_int):
return 1
else:
return NumBinopNode.is_py_operation(self)
class FloorDivNode(NumBinopNode):
# '//' operator.
def calculate_result_code(self):
return "(%s %s %s)" % (
self.operand1.result(),
"/", # c division is by default floor-div
self.operand2.result())
class ModNode(NumBinopNode):
# '%' operator.
def is_py_operation(self):
return (self.operand1.type.is_string
or self.operand2.type.is_string
or NumBinopNode.is_py_operation(self))
def calculate_result_code(self):
if self.operand1.type.is_float or self.operand2.type.is_float:
return "fmod(%s, %s)" % (
self.operand1.result(),
self.operand2.result())
else:
return "(%s %% %s)" % (
self.operand1.result(),
self.operand2.result())
class PowNode(NumBinopNode):
# '**' operator.
def compute_c_result_type(self, type1, type2):
if self.c_types_okay(type1, type2):
return PyrexTypes.c_double_type
else:
return None
def c_types_okay(self, type1, type2):
return (type1.is_float or type2.is_float) and \
NumBinopNode.c_types_okay(self, type1, type2)
def type_error(self):
if not (self.operand1.type.is_error or self.operand2.type.is_error):
if self.operand1.type.is_int and self.operand2.type.is_int:
error(self.pos, "C has no integer powering, use python ints or floats instead '%s' (%s; %s)" %
(self.operator, self.operand1.type, self.operand2.type))
else:
NumBinopNode.type_error(self)
self.type = PyrexTypes.error_type
def calculate_result_code(self):
return "pow(%s, %s)" % (
self.operand1.result(), self.operand2.result())
# Note: This class is temporary "shut down" into an ineffective mode temp
# allocation mode.
#
# More sophisticated temp reuse was going on before,
# one could have a look at adding this again after /all/ classes
# are converted to the new temp scheme. (The temp juggling cannot work
# otherwise).
class BoolBinopNode(NewTempExprNode):
# Short-circuiting boolean operation.
#
# operator string
# operand1 ExprNode
# operand2 ExprNode
subexprs = ['operand1', 'operand2']
def calculate_constant_result(self):
if self.operator == 'and':
self.constant_result = \
self.operand1.constant_result and \
self.operand2.constant_result
else:
self.constant_result = \
self.operand1.constant_result or \
self.operand2.constant_result
def compile_time_value(self, denv):
if self.operator == 'and':
return self.operand1.compile_time_value(denv) \
and self.operand2.compile_time_value(denv)
else:
return self.operand1.compile_time_value(denv) \
or self.operand2.compile_time_value(denv)
def coerce_to_boolean(self, env):
self.operand1 = self.operand1.coerce_to_boolean(env)
self.operand2 = self.operand2.coerce_to_boolean(env)
self.type = PyrexTypes.c_bint_type
return self
def analyse_types(self, env):
self.operand1.analyse_types(env)
self.operand2.analyse_types(env)
if self.operand1.type.is_pyobject or \
self.operand2.type.is_pyobject:
self.operand1 = self.operand1.coerce_to_pyobject(env)
self.operand2 = self.operand2.coerce_to_pyobject(env)
self.type = py_object_type
self.gil_check(env)
else:
self.operand1 = self.operand1.coerce_to_boolean(env)
self.operand2 = self.operand2.coerce_to_boolean(env)
self.type = PyrexTypes.c_bint_type
# Below disabled for
# For what we're about to do, it's vital that
# both operands be temp nodes.
# self.operand1 = self.operand1.coerce_to_temp(env) #CTT
# self.operand2 = self.operand2.coerce_to_temp(env)
self.is_temp = 1
gil_message = "Truth-testing Python object"
## def allocate_temps(self, env, result_code = None):
## # We don't need both operands at the same time, and
## # one of the operands will also be our result. So we
## # use an allocation strategy here which results in
## # this node and both its operands sharing the same
## # result variable. This allows us to avoid some
## # assignments and increfs/decrefs that would otherwise
## # be necessary.
## self.allocate_temp(env, result_code)
## self.operand1.allocate_temps(env, self.result())
## self.operand2.allocate_temps(env, self.result())
## # We haven't called release_temp on either operand,
## # because although they are temp nodes, they don't own
## # their result variable. And because they are temp
## # nodes, any temps in their subnodes will have been
## # released before their allocate_temps returned.
## # Therefore, they contain no temp vars that need to
## # be released.
def check_const(self):
self.operand1.check_const()
self.operand2.check_const()
def calculate_result_code(self):
return "(%s %s %s)" % (
self.operand1.result(),
self.py_to_c_op[self.operator],
self.operand2.result())
py_to_c_op = {'and': "&&", 'or': "||"}
def generate_evaluation_code(self, code):
code.mark_pos(self.pos)
self.operand1.generate_evaluation_code(code)
test_result, uses_temp = self.generate_operand1_test(code)
if self.operator == 'and':
sense = ""
else:
sense = "!"
code.putln(
"if (%s%s) {" % (
sense,
test_result))
if uses_temp:
code.funcstate.release_temp(test_result)
self.operand1.generate_disposal_code(code)
self.operand2.generate_evaluation_code(code)
self.allocate_temp_result(code)
self.operand2.make_owned_reference(code)
code.putln("%s = %s;" % (self.result(), self.operand2.result()))
self.operand2.generate_post_assignment_code(code)
self.operand2.free_temps(code)
code.putln("} else {")
self.operand1.make_owned_reference(code)
code.putln("%s = %s;" % (self.result(), self.operand1.result()))
self.operand1.generate_post_assignment_code(code)
self.operand1.free_temps(code)
code.putln("}")
def generate_operand1_test(self, code):
# Generate code to test the truth of the first operand.
if self.type.is_pyobject:
test_result = code.funcstate.allocate_temp(PyrexTypes.c_bint_type,
manage_ref=False)
code.putln(
"%s = __Pyx_PyObject_IsTrue(%s); %s" % (
test_result,
self.operand1.py_result(),
code.error_goto_if_neg(test_result, self.pos)))
else:
test_result = self.operand1.result()
return (test_result, self.type.is_pyobject)
class CondExprNode(ExprNode):
# Short-circuiting conditional expression.
#
# test ExprNode
# true_val ExprNode
# false_val ExprNode
true_val = None
false_val = None
subexprs = ['test', 'true_val', 'false_val']
def calculate_constant_result(self):
if self.test.constant_result:
self.constant_result = self.true_val.constant_result
else:
self.constant_result = self.false_val.constant_result
def analyse_types(self, env):
self.test.analyse_types(env)
self.test = self.test.coerce_to_boolean(env)
self.true_val.analyse_types(env)
self.false_val.analyse_types(env)
self.type = self.compute_result_type(self.true_val.type, self.false_val.type)
if self.true_val.type.is_pyobject or self.false_val.type.is_pyobject:
self.true_val = self.true_val.coerce_to(self.type, env)
self.false_val = self.false_val.coerce_to(self.type, env)
# must be tmp variables so they can share a result
self.true_val = self.true_val.coerce_to_temp(env)
self.false_val = self.false_val.coerce_to_temp(env)
self.is_temp = 1
if self.type == PyrexTypes.error_type:
self.type_error()
def allocate_temps(self, env, result_code = None):
# We only ever evaluate one side, and this is
# after evaluating the truth value, so we may
# use an allocation strategy here which results in
# this node and both its operands sharing the same
# result variable. This allows us to avoid some
# assignments and increfs/decrefs that would otherwise
# be necessary.
self.allocate_temp(env, result_code)
self.test.allocate_temps(env, result_code)
self.true_val.allocate_temps(env, self.result())
self.false_val.allocate_temps(env, self.result())
# We haven't called release_temp on either value,
# because although they are temp nodes, they don't own
# their result variable. And because they are temp
# nodes, any temps in their subnodes will have been
# released before their allocate_temps returned.
# Therefore, they contain no temp vars that need to
# be released.
def compute_result_type(self, type1, type2):
if type1 == type2:
return type1
elif type1.is_numeric and type2.is_numeric:
return PyrexTypes.widest_numeric_type(type1, type2)
elif type1.is_extension_type and type1.subtype_of_resolved_type(type2):
return type2
elif type2.is_extension_type and type2.subtype_of_resolved_type(type1):
return type1
elif type1.is_pyobject or type2.is_pyobject:
return py_object_type
elif type1.assignable_from(type2):
return type1
elif type2.assignable_from(type1):
return type2
else:
return PyrexTypes.error_type
def type_error(self):
if not (self.true_val.type.is_error or self.false_val.type.is_error):
error(self.pos, "Incompatable types in conditional expression (%s; %s)" %
(self.true_val.type, self.false_val.type))
self.type = PyrexTypes.error_type
def check_const(self):
self.test.check_const()
self.true_val.check_const()
self.false_val.check_const()
def generate_evaluation_code(self, code):
self.test.generate_evaluation_code(code)
code.putln("if (%s) {" % self.test.result() )
self.true_val.generate_evaluation_code(code)
code.putln("} else {")
self.false_val.generate_evaluation_code(code)
code.putln("}")
self.test.generate_disposal_code(code)
self.test.free_temps(code)
richcmp_constants = {
"<" : "Py_LT",
"<=": "Py_LE",
"==": "Py_EQ",
"!=": "Py_NE",
"<>": "Py_NE",
">" : "Py_GT",
">=": "Py_GE",
}
class CmpNode(object):
# Mixin class containing code common to PrimaryCmpNodes
# and CascadedCmpNodes.
def calculate_cascaded_constant_result(self, operand1_result):
func = compile_time_binary_operators[self.operator]
operand2_result = self.operand2.constant_result
result = func(operand1_result, operand2_result)
if result and self.cascade:
result = result and \
self.cascade.cascaded_compile_time_value(operand2_result)
self.constant_result = result
def cascaded_compile_time_value(self, operand1, denv):
func = get_compile_time_binop(self)
operand2 = self.operand2.compile_time_value(denv)
try:
result = func(operand1, operand2)
except Exception, e:
self.compile_time_value_error(e)
result = None
if result:
cascade = self.cascade
if cascade:
# FIXME: I bet this must call cascaded_compile_time_value()
result = result and cascade.compile_time_value(operand2, denv)
return result
def is_python_comparison(self):
return (self.has_python_operands()
or (self.cascade and self.cascade.is_python_comparison())
or self.operator in ('in', 'not_in'))
def is_python_result(self):
return ((self.has_python_operands() and self.operator not in ('is', 'is_not', 'in', 'not_in'))
or (self.cascade and self.cascade.is_python_result()))
def check_types(self, env, operand1, op, operand2):
if not self.types_okay(operand1, op, operand2):
error(self.pos, "Invalid types for '%s' (%s, %s)" %
(self.operator, operand1.type, operand2.type))
def types_okay(self, operand1, op, operand2):
type1 = operand1.type
type2 = operand2.type
if type1.is_error or type2.is_error:
return 1
if type1.is_pyobject: # type2 will be, too
return 1
elif type1.is_ptr or type1.is_array:
return type1.is_null_ptr or type2.is_null_ptr \
or ((type2.is_ptr or type2.is_array)
and type1.base_type.same_as(type2.base_type))
elif ((type1.is_numeric and type2.is_numeric
or type1.is_enum and (type1 is type2 or type2.is_int)
or type1.is_int and type2.is_enum)
and op not in ('is', 'is_not')):
return 1
else:
return type1.is_cfunction and type1.is_cfunction and type1 == type2
def generate_operation_code(self, code, result_code,
operand1, op , operand2):
if self.type is PyrexTypes.py_object_type:
coerce_result = "__Pyx_PyBool_FromLong"
else:
coerce_result = ""
if 'not' in op: negation = "!"
else: negation = ""
if op == 'in' or op == 'not_in':
code.putln(
"%s = %s(%sPySequence_Contains(%s, %s)); %s" % (
result_code,
coerce_result,
negation,
operand2.py_result(),
operand1.py_result(),
code.error_goto_if_neg(result_code, self.pos)))
elif (operand1.type.is_pyobject
and op not in ('is', 'is_not')):
code.putln("%s = PyObject_RichCompare(%s, %s, %s); %s" % (
result_code,
operand1.py_result(),
operand2.py_result(),
richcmp_constants[op],
code.error_goto_if_null(result_code, self.pos)))
code.put_gotref(result_code)
else:
type1 = operand1.type
type2 = operand2.type
if (type1.is_extension_type or type2.is_extension_type) \
and not type1.same_as(type2):
common_type = py_object_type
elif type1.is_numeric:
common_type = PyrexTypes.widest_numeric_type(type1, type2)
else:
common_type = type1
code1 = operand1.result_as(common_type)
code2 = operand2.result_as(common_type)
code.putln("%s = %s(%s %s %s);" % (
result_code,
coerce_result,
code1,
self.c_operator(op),
code2))
def c_operator(self, op):
if op == 'is':
return "=="
elif op == 'is_not':
return "!="
else:
return op
class PrimaryCmpNode(NewTempExprNode, CmpNode):
# Non-cascaded comparison or first comparison of
# a cascaded sequence.
#
# operator string
# operand1 ExprNode
# operand2 ExprNode
# cascade CascadedCmpNode
# We don't use the subexprs mechanism, because
# things here are too complicated for it to handle.
# Instead, we override all the framework methods
# which use it.
child_attrs = ['operand1', 'operand2', 'cascade']
cascade = None
def calculate_constant_result(self):
self.constant_result = self.calculate_cascaded_constant_result(
self.operand1.constant_result)
def compile_time_value(self, denv):
operand1 = self.operand1.compile_time_value(denv)
return self.cascaded_compile_time_value(operand1, denv)
def analyse_types(self, env):
self.operand1.analyse_types(env)
self.operand2.analyse_types(env)
if self.cascade:
self.cascade.analyse_types(env, self.operand2)
self.is_pycmp = self.is_python_comparison()
if self.is_pycmp:
self.coerce_operands_to_pyobjects(env)
if self.has_int_operands():
self.coerce_chars_to_ints(env)
if self.cascade:
self.operand2 = self.operand2.coerce_to_simple(env)
self.cascade.coerce_cascaded_operands_to_temp(env)
self.check_operand_types(env)
if self.is_python_result():
self.type = PyrexTypes.py_object_type
else:
self.type = PyrexTypes.c_bint_type
cdr = self.cascade
while cdr:
cdr.type = self.type
cdr = cdr.cascade
if self.is_pycmp or self.cascade:
self.is_temp = 1
def check_operand_types(self, env):
self.check_types(env,
self.operand1, self.operator, self.operand2)
if self.cascade:
self.cascade.check_operand_types(env, self.operand2)
def has_python_operands(self):
return (self.operand1.type.is_pyobject
or self.operand2.type.is_pyobject)
def coerce_operands_to_pyobjects(self, env):
self.operand1 = self.operand1.coerce_to_pyobject(env)
self.operand2 = self.operand2.coerce_to_pyobject(env)
if self.cascade:
self.cascade.coerce_operands_to_pyobjects(env)
def has_int_operands(self):
return (self.operand1.type.is_int or self.operand2.type.is_int) \
or (self.cascade and self.cascade.has_int_operands())
def coerce_chars_to_ints(self, env):
# coerce literal single-char strings to c chars
if self.operand1.type.is_string and isinstance(self.operand1, StringNode):
self.operand1 = self.operand1.coerce_to(PyrexTypes.c_uchar_type, env)
if self.operand2.type.is_string and isinstance(self.operand2, StringNode):
self.operand2 = self.operand2.coerce_to(PyrexTypes.c_uchar_type, env)
if self.cascade:
self.cascade.coerce_chars_to_ints(env)
def allocate_subexpr_temps(self, env):
self.operand1.allocate_temps(env)
self.operand2.allocate_temps(env)
if self.cascade:
self.cascade.allocate_subexpr_temps(env)
def release_subexpr_temps(self, env):
self.operand1.release_temp(env)
self.operand2.release_temp(env)
if self.cascade:
self.cascade.release_subexpr_temps(env)
def check_const(self):
self.operand1.check_const()
self.operand2.check_const()
if self.cascade:
self.not_const()
def calculate_result_code(self):
return "(%s %s %s)" % (
self.operand1.result(),
self.c_operator(self.operator),
self.operand2.result())
def generate_evaluation_code(self, code):
self.operand1.generate_evaluation_code(code)
self.operand2.generate_evaluation_code(code)
if self.is_temp:
self.allocate_temp_result(code)
self.generate_operation_code(code, self.result(),
self.operand1, self.operator, self.operand2)
if self.cascade:
self.cascade.generate_evaluation_code(code,
self.result(), self.operand2)
self.operand1.generate_disposal_code(code)
self.operand1.free_temps(code)
self.operand2.generate_disposal_code(code)
self.operand2.free_temps(code)
def generate_subexpr_disposal_code(self, code):
# If this is called, it is a non-cascaded cmp,
# so only need to dispose of the two main operands.
self.operand1.generate_disposal_code(code)
self.operand2.generate_disposal_code(code)
def free_subexpr_temps(self, code):
# If this is called, it is a non-cascaded cmp,
# so only need to dispose of the two main operands.
self.operand1.free_temps(code)
self.operand2.free_temps(code)
def annotate(self, code):
self.operand1.annotate(code)
self.operand2.annotate(code)
if self.cascade:
self.cascade.annotate(code)
class CascadedCmpNode(Node, CmpNode):
# A CascadedCmpNode is not a complete expression node. It
# hangs off the side of another comparison node, shares
# its left operand with that node, and shares its result
# with the PrimaryCmpNode at the head of the chain.
#
# operator string
# operand2 ExprNode
# cascade CascadedCmpNode
child_attrs = ['operand2', 'cascade']
cascade = None
constant_result = constant_value_not_set # FIXME: where to calculate this?
def analyse_types(self, env, operand1):
self.operand2.analyse_types(env)
if self.cascade:
self.cascade.analyse_types(env, self.operand2)
def check_operand_types(self, env, operand1):
self.check_types(env,
operand1, self.operator, self.operand2)
if self.cascade:
self.cascade.check_operand_types(env, self.operand2)
def has_python_operands(self):
return self.operand2.type.is_pyobject
def coerce_operands_to_pyobjects(self, env):
self.operand2 = self.operand2.coerce_to_pyobject(env)
if self.cascade:
self.cascade.coerce_operands_to_pyobjects(env)
def has_int_operands(self):
return self.operand2.type.is_int
def coerce_chars_to_ints(self, env):
if self.operand2.type.is_string and isinstance(self.operand2, StringNode):
self.operand2 = self.operand2.coerce_to(PyrexTypes.c_uchar_type, env)
def coerce_cascaded_operands_to_temp(self, env):
if self.cascade:
#self.operand2 = self.operand2.coerce_to_temp(env) #CTT
self.operand2 = self.operand2.coerce_to_simple(env)
self.cascade.coerce_cascaded_operands_to_temp(env)
def allocate_subexpr_temps(self, env):
self.operand2.allocate_temps(env)
if self.cascade:
self.cascade.allocate_subexpr_temps(env)
def release_subexpr_temps(self, env):
self.operand2.release_temp(env)
if self.cascade:
self.cascade.release_subexpr_temps(env)
def generate_evaluation_code(self, code, result, operand1):
if self.type.is_pyobject:
code.putln("if (__Pyx_PyObject_IsTrue(%s)) {" % result)
else:
code.putln("if (%s) {" % result)
self.operand2.generate_evaluation_code(code)
self.generate_operation_code(code, result,
operand1, self.operator, self.operand2)
if self.cascade:
self.cascade.generate_evaluation_code(
code, result, self.operand2)
# Cascaded cmp result is always temp
self.operand2.generate_disposal_code(code)
self.operand2.free_temps(code)
code.putln("}")
def annotate(self, code):
self.operand2.annotate(code)
if self.cascade:
self.cascade.annotate(code)
binop_node_classes = {
"or": BoolBinopNode,
"and": BoolBinopNode,
"|": IntBinopNode,
"^": IntBinopNode,
"&": IntBinopNode,
"<<": IntBinopNode,
">>": IntBinopNode,
"+": AddNode,
"-": SubNode,
"*": MulNode,
"/": NumBinopNode,
"//": FloorDivNode,
"%": ModNode,
"**": PowNode
}
def binop_node(pos, operator, operand1, operand2):
# Construct binop node of appropriate class for
# given operator.
return binop_node_classes[operator](pos,
operator = operator,
operand1 = operand1,
operand2 = operand2)
#-------------------------------------------------------------------
#
# Coercion nodes
#
# Coercion nodes are special in that they are created during
# the analyse_types phase of parse tree processing.
# Their __init__ methods consequently incorporate some aspects
# of that phase.
#
#-------------------------------------------------------------------
class CoercionNode(NewTempExprNode):
# Abstract base class for coercion nodes.
#
# arg ExprNode node being coerced
subexprs = ['arg']
def __init__(self, arg):
self.pos = arg.pos
self.arg = arg
if debug_coercion:
print("%s Coercing %s" % (self, self.arg))
def calculate_constant_result(self):
self.constant_result = self.arg.constant_result
def annotate(self, code):
self.arg.annotate(code)
if self.arg.type != self.type:
file, line, col = self.pos
code.annotate((file, line, col-1), AnnotationItem(style='coerce', tag='coerce', text='[%s] to [%s]' % (self.arg.type, self.type)))
class CastNode(CoercionNode):
# Wrap a node in a C type cast.
def __init__(self, arg, new_type):
CoercionNode.__init__(self, arg)
self.type = new_type
def calculate_result_code(self):
return self.arg.result_as(self.type)
def generate_result_code(self, code):
self.arg.generate_result_code(code)
class PyTypeTestNode(CoercionNode):
# This node is used to check that a generic Python
# object is an instance of a particular extension type.
# This node borrows the result of its argument node.
def __init__(self, arg, dst_type, env):
# The arg is know to be a Python object, and
# the dst_type is known to be an extension type.
assert dst_type.is_extension_type or dst_type.is_builtin_type, "PyTypeTest on non extension type"
CoercionNode.__init__(self, arg)
self.type = dst_type
self.gil_check(env)
self.result_ctype = arg.ctype()
gil_message = "Python type test"
def analyse_types(self, env):
pass
def result_in_temp(self):
return self.arg.result_in_temp()
def is_ephemeral(self):
return self.arg.is_ephemeral()
def calculate_result_code(self):
return self.arg.result()
def generate_result_code(self, code):
if self.type.typeobj_is_available():
if not self.type.is_builtin_type:
code.globalstate.use_utility_code(type_test_utility_code)
code.putln(
"if (!(%s)) %s" % (
self.type.type_test_code(self.arg.py_result()),
code.error_goto(self.pos)))
else:
error(self.pos, "Cannot test type of extern C class "
"without type object name specification")
def generate_post_assignment_code(self, code):
self.arg.generate_post_assignment_code(code)
def free_temps(self, code):
self.arg.free_temps(code)
class CoerceToPyTypeNode(CoercionNode):
# This node is used to convert a C data type
# to a Python object.
def __init__(self, arg, env):
CoercionNode.__init__(self, arg)
self.type = py_object_type
self.gil_check(env)
self.is_temp = 1
if not arg.type.to_py_function or not arg.type.create_convert_utility_code(env):
error(arg.pos,
"Cannot convert '%s' to Python object" % arg.type)
gil_message = "Converting to Python object"
def coerce_to_boolean(self, env):
return self.arg.coerce_to_boolean(env).coerce_to_temp(env)
def coerce_to_integer(self, env):
# If not already some C integer type, coerce to longint.
if self.arg.type.is_int:
return self.arg
else:
return self.arg.coerce_to(PyrexTypes.c_long_type, env)
def analyse_types(self, env):
# The arg is always already analysed
pass
def generate_result_code(self, code):
function = self.arg.type.to_py_function
code.putln('%s = %s(%s); %s' % (
self.result(),
function,
self.arg.result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class CoerceFromPyTypeNode(CoercionNode):
# This node is used to convert a Python object
# to a C data type.
def __init__(self, result_type, arg, env):
CoercionNode.__init__(self, arg)
self.type = result_type
self.is_temp = 1
if not result_type.from_py_function:
error(arg.pos,
"Cannot convert Python object to '%s'" % result_type)
if self.type.is_string and self.arg.is_ephemeral():
error(arg.pos,
"Obtaining char * from temporary Python value")
def analyse_types(self, env):
# The arg is always already analysed
pass
def generate_result_code(self, code):
function = self.type.from_py_function
operand = self.arg.py_result()
rhs = "%s(%s)" % (function, operand)
if self.type.is_enum:
rhs = typecast(self.type, c_long_type, rhs)
code.putln('%s = %s; %s' % (
self.result(),
rhs,
code.error_goto_if(self.type.error_condition(self.result()), self.pos)))
if self.type.is_pyobject:
code.put_gotref(self.py_result())
class CoerceToBooleanNode(CoercionNode):
# This node is used when a result needs to be used
# in a boolean context.
def __init__(self, arg, env):
CoercionNode.__init__(self, arg)
self.type = PyrexTypes.c_bint_type
if arg.type.is_pyobject:
if env.nogil:
self.gil_error()
self.is_temp = 1
gil_message = "Truth-testing Python object"
def check_const(self):
if self.is_temp:
self.not_const()
self.arg.check_const()
def calculate_result_code(self):
return "(%s != 0)" % self.arg.result()
def generate_result_code(self, code):
if self.arg.type.is_pyobject:
code.putln(
"%s = __Pyx_PyObject_IsTrue(%s); %s" % (
self.result(),
self.arg.py_result(),
code.error_goto_if_neg(self.result(), self.pos)))
class CoerceToTempNode(CoercionNode):
# This node is used to force the result of another node
# to be stored in a temporary. It is only used if the
# argument node's result is not already in a temporary.
def __init__(self, arg, env):
CoercionNode.__init__(self, arg)
self.type = self.arg.type
self.is_temp = 1
if self.type.is_pyobject:
self.gil_check(env)
self.result_ctype = py_object_type
gil_message = "Creating temporary Python reference"
def analyse_types(self, env):
# The arg is always already analysed
pass
def coerce_to_boolean(self, env):
self.arg = self.arg.coerce_to_boolean(env)
self.type = self.arg.type
self.result_ctype = self.type
return self
def generate_result_code(self, code):
#self.arg.generate_evaluation_code(code) # Already done
# by generic generate_subexpr_evaluation_code!
code.putln("%s = %s;" % (
self.result(), self.arg.result_as(self.ctype())))
if self.type.is_pyobject:
code.put_incref(self.result(), self.ctype())
class CloneNode(CoercionNode):
# This node is employed when the result of another node needs
# to be used multiple times. The argument node's result must
# be in a temporary. This node "borrows" the result from the
# argument node, and does not generate any evaluation or
# disposal code for it. The original owner of the argument
# node is responsible for doing those things.
subexprs = [] # Arg is not considered a subexpr
def __init__(self, arg):
CoercionNode.__init__(self, arg)
if hasattr(arg, 'type'):
self.type = arg.type
self.result_ctype = arg.result_ctype
if hasattr(arg, 'entry'):
self.entry = arg.entry
def result(self):
return self.arg.result()
def analyse_types(self, env):
self.type = self.arg.type
self.result_ctype = self.arg.result_ctype
self.is_temp = 1
if hasattr(self.arg, 'entry'):
self.entry = self.arg.entry
def generate_evaluation_code(self, code):
pass
def generate_result_code(self, code):
pass
def generate_disposal_code(self, code):
pass
def allocate_temps(self, env):
pass
def release_temp(self, env):
pass
def free_temps(self, code):
pass
#------------------------------------------------------------------------------------
#
# Runtime support code
#
#------------------------------------------------------------------------------------
get_name_interned_utility_code = UtilityCode(
proto = """
static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/
""",
impl = """
static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name) {
PyObject *result;
result = PyObject_GetAttr(dict, name);
if (!result)
PyErr_SetObject(PyExc_NameError, name);
return result;
}
""")
#------------------------------------------------------------------------------------
import_utility_code = UtilityCode(
proto = """
static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/
""",
impl = """
static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list) {
PyObject *__import__ = 0;
PyObject *empty_list = 0;
PyObject *module = 0;
PyObject *global_dict = 0;
PyObject *empty_dict = 0;
PyObject *list;
__import__ = __Pyx_GetAttrString(%(BUILTINS)s, "__import__");
if (!__import__)
goto bad;
if (from_list)
list = from_list;
else {
empty_list = PyList_New(0);
if (!empty_list)
goto bad;
list = empty_list;
}
global_dict = PyModule_GetDict(%(GLOBALS)s);
if (!global_dict)
goto bad;
empty_dict = PyDict_New();
if (!empty_dict)
goto bad;
module = PyObject_CallFunctionObjArgs(__import__,
name, global_dict, empty_dict, list, NULL);
bad:
Py_XDECREF(empty_list);
Py_XDECREF(__import__);
Py_XDECREF(empty_dict);
return module;
}
""" % {
"BUILTINS": Naming.builtins_cname,
"GLOBALS": Naming.module_cname,
})
#------------------------------------------------------------------------------------
get_exception_utility_code = UtilityCode(
proto = """
static PyObject *__Pyx_GetExcValue(void); /*proto*/
""",
impl = """
static PyObject *__Pyx_GetExcValue(void) {
PyObject *type = 0, *value = 0, *tb = 0;
PyObject *tmp_type, *tmp_value, *tmp_tb;
PyObject *result = 0;
PyThreadState *tstate = PyThreadState_Get();
PyErr_Fetch(&type, &value, &tb);
PyErr_NormalizeException(&type, &value, &tb);
if (PyErr_Occurred())
goto bad;
if (!value) {
value = Py_None;
Py_INCREF(value);
}
tmp_type = tstate->exc_type;
tmp_value = tstate->exc_value;
tmp_tb = tstate->exc_traceback;
tstate->exc_type = type;
tstate->exc_value = value;
tstate->exc_traceback = tb;
/* Make sure tstate is in a consistent state when we XDECREF
these objects (XDECREF may run arbitrary code). */
Py_XDECREF(tmp_type);
Py_XDECREF(tmp_value);
Py_XDECREF(tmp_tb);
result = value;
Py_XINCREF(result);
type = 0;
value = 0;
tb = 0;
bad:
Py_XDECREF(type);
Py_XDECREF(value);
Py_XDECREF(tb);
return result;
}
""")
#------------------------------------------------------------------------------------
unpacking_utility_code = UtilityCode(
proto = """
static PyObject *__Pyx_UnpackItem(PyObject *, Py_ssize_t index); /*proto*/
static int __Pyx_EndUnpack(PyObject *); /*proto*/
""",
impl = """
static PyObject *__Pyx_UnpackItem(PyObject *iter, Py_ssize_t index) {
PyObject *item;
if (!(item = PyIter_Next(iter))) {
if (!PyErr_Occurred()) {
PyErr_Format(PyExc_ValueError,
#if PY_VERSION_HEX < 0x02050000
"need more than %d values to unpack", (int)index);
#else
"need more than %zd values to unpack", index);
#endif
}
}
return item;
}
static int __Pyx_EndUnpack(PyObject *iter) {
PyObject *item;
if ((item = PyIter_Next(iter))) {
Py_DECREF(item);
PyErr_SetString(PyExc_ValueError, "too many values to unpack");
return -1;
}
else if (!PyErr_Occurred())
return 0;
else
return -1;
}
""")
#------------------------------------------------------------------------------------
type_test_utility_code = UtilityCode(
proto = """
static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/
""",
impl = """
static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) {
if (!type) {
PyErr_Format(PyExc_SystemError, "Missing type object");
return 0;
}
if (obj == Py_None || PyObject_TypeCheck(obj, type))
return 1;
PyErr_Format(PyExc_TypeError, "Cannot convert %s to %s",
Py_TYPE(obj)->tp_name, type->tp_name);
return 0;
}
""")
#------------------------------------------------------------------------------------
create_class_utility_code = UtilityCode(
proto = """
static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, const char *modname); /*proto*/
""",
impl = """
static PyObject *__Pyx_CreateClass(
PyObject *bases, PyObject *dict, PyObject *name, const char *modname)
{
PyObject *py_modname;
PyObject *result = 0;
#if PY_MAJOR_VERSION < 3
py_modname = PyString_FromString(modname);
#else
py_modname = PyUnicode_FromString(modname);
#endif
if (!py_modname)
goto bad;
if (PyDict_SetItemString(dict, "__module__", py_modname) < 0)
goto bad;
#if PY_MAJOR_VERSION < 3
result = PyClass_New(bases, dict, name);
#else
result = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, name, bases, dict, NULL);
#endif
bad:
Py_XDECREF(py_modname);
return result;
}
""")
#------------------------------------------------------------------------------------
cpp_exception_utility_code = UtilityCode(
proto = """
#ifndef __Pyx_CppExn2PyErr
static void __Pyx_CppExn2PyErr() {
try {
if (PyErr_Occurred())
; // let the latest Python exn pass through and ignore the current one
else
throw;
} catch (const std::out_of_range& exn) {
// catch out_of_range explicitly so the proper Python exn may be raised
PyErr_SetString(PyExc_IndexError, exn.what());
} catch (const std::exception& exn) {
PyErr_SetString(PyExc_RuntimeError, exn.what());
}
catch (...)
{
PyErr_SetString(PyExc_RuntimeError, "Unknown exception");
}
}
#endif
""",
impl = ""
)
#------------------------------------------------------------------------------------
append_utility_code = UtilityCode(
proto = """
static INLINE PyObject* __Pyx_PyObject_Append(PyObject* L, PyObject* x) {
if (likely(PyList_CheckExact(L))) {
if (PyList_Append(L, x) < 0) return NULL;
Py_INCREF(Py_None);
return Py_None; /* this is just to have an accurate signature */
}
else {
PyObject *r, *m;
m = __Pyx_GetAttrString(L, "append");
if (!m) return NULL;
r = PyObject_CallFunctionObjArgs(m, x, NULL);
Py_DECREF(m);
return r;
}
}
""",
impl = ""
)
#------------------------------------------------------------------------------------
# If the is_unsigned flag is set, we need to do some extra work to make
# sure the index doesn't become negative.
getitem_int_utility_code = UtilityCode(
proto = """
static INLINE PyObject *__Pyx_GetItemInt(PyObject *o, Py_ssize_t i, int is_unsigned) {
PyObject *r;
if (PyList_CheckExact(o) && 0 <= i && i < PyList_GET_SIZE(o)) {
r = PyList_GET_ITEM(o, i);
Py_INCREF(r);
}
else if (PyTuple_CheckExact(o) && 0 <= i && i < PyTuple_GET_SIZE(o)) {
r = PyTuple_GET_ITEM(o, i);
Py_INCREF(r);
}
else if (Py_TYPE(o)->tp_as_sequence && Py_TYPE(o)->tp_as_sequence->sq_item && (likely(i >= 0) || !is_unsigned))
r = PySequence_GetItem(o, i);
else {
PyObject *j = (likely(i >= 0) || !is_unsigned) ? PyInt_FromLong(i) : PyLong_FromUnsignedLongLong((sizeof(unsigned long long) > sizeof(Py_ssize_t) ? (1ULL << (sizeof(Py_ssize_t)*8)) : 0) + i);
if (!j)
return 0;
r = PyObject_GetItem(o, j);
Py_DECREF(j);
}
return r;
}
""",
impl = """
""")
#------------------------------------------------------------------------------------
setitem_int_utility_code = UtilityCode(
proto = """
static INLINE int __Pyx_SetItemInt(PyObject *o, Py_ssize_t i, PyObject *v, int is_unsigned) {
int r;
if (PyList_CheckExact(o) && 0 <= i && i < PyList_GET_SIZE(o)) {
Py_DECREF(PyList_GET_ITEM(o, i));
Py_INCREF(v);
PyList_SET_ITEM(o, i, v);
return 1;
}
else if (Py_TYPE(o)->tp_as_sequence && Py_TYPE(o)->tp_as_sequence->sq_ass_item && (likely(i >= 0) || !is_unsigned))
r = PySequence_SetItem(o, i, v);
else {
PyObject *j = (likely(i >= 0) || !is_unsigned) ? PyInt_FromLong(i) : PyLong_FromUnsignedLongLong((sizeof(unsigned long long) > sizeof(Py_ssize_t) ? (1ULL << (sizeof(Py_ssize_t)*8)) : 0) + i);
if (!j)
return -1;
r = PyObject_SetItem(o, j, v);
Py_DECREF(j);
}
return r;
}
""",
impl = """
""")
#------------------------------------------------------------------------------------
delitem_int_utility_code = UtilityCode(
proto = """
static INLINE int __Pyx_DelItemInt(PyObject *o, Py_ssize_t i, int is_unsigned) {
int r;
if (Py_TYPE(o)->tp_as_sequence && Py_TYPE(o)->tp_as_sequence->sq_ass_item && (likely(i >= 0) || !is_unsigned))
r = PySequence_DelItem(o, i);
else {
PyObject *j = (likely(i >= 0) || !is_unsigned) ? PyInt_FromLong(i) : PyLong_FromUnsignedLongLong((sizeof(unsigned long long) > sizeof(Py_ssize_t) ? (1ULL << (sizeof(Py_ssize_t)*8)) : 0) + i);
if (!j)
return -1;
r = PyObject_DelItem(o, j);
Py_DECREF(j);
}
return r;
}
""",
impl = """
""")
#------------------------------------------------------------------------------------
raise_noneattr_error_utility_code = UtilityCode(
proto = """
static INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname);
""",
impl = """
static INLINE void __Pyx_RaiseNoneAttributeError(const char* attrname) {
PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", attrname);
}
""")
raise_noneindex_error_utility_code = UtilityCode(
proto = """
static INLINE void __Pyx_RaiseNoneIndexingError(void);
""",
impl = """
static INLINE void __Pyx_RaiseNoneIndexingError(void) {
PyErr_SetString(PyExc_TypeError, "'NoneType' object is unsubscriptable");
}
""")
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/Future.py 0000664 0000000 0000000 00000000547 11136717635 0024772 0 ustar 00root root 0000000 0000000 def _get_feature(name):
import __future__
try:
return getattr(__future__, name)
except AttributeError:
# unique fake object for earlier Python versions
return object()
unicode_literals = _get_feature("unicode_literals")
with_statement = _get_feature("with_statement")
division = _get_feature("division")
del _get_feature
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/Interpreter.py 0000664 0000000 0000000 00000003225 11136717635 0026017 0 ustar 00root root 0000000 0000000 """
This module deals with interpreting the parse tree as Python
would have done, in the compiler.
For now this only covers parse tree to value conversion of
compile-time values.
"""
from Nodes import *
from ExprNodes import *
from Visitor import BasicVisitor
from Errors import CompileError
class EmptyScope:
def lookup(self, name):
return None
empty_scope = EmptyScope()
def interpret_compiletime_options(optlist, optdict, type_env=None):
"""
Tries to interpret a list of compile time option nodes.
The result will be a tuple (optlist, optdict) but where
all expression nodes have been interpreted. The result is
in the form of tuples (value, pos).
optlist is a list of nodes, while optdict is a DictNode (the
result optdict is a dict)
If type_env is set, all type nodes will be analysed and the resulting
type set. Otherwise only interpretateable ExprNodes
are allowed, other nodes raises errors.
A CompileError will be raised if there are problems.
"""
def interpret(node):
if isinstance(node, CBaseTypeNode):
if type_env:
return (node.analyse(type_env), node.pos)
else:
raise CompileError(node.pos, "Type not allowed here.")
else:
return (node.compile_time_value(empty_scope), node.pos)
if optlist:
optlist = [interpret(x) for x in optlist]
if optdict:
assert isinstance(optdict, DictNode)
new_optdict = {}
for item in optdict.key_value_pairs:
new_optdict[item.key.value] = interpret(item.value)
optdict = new_optdict
return (optlist, new_optdict)
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/Lexicon.py 0000664 0000000 0000000 00000011446 11136717635 0025121 0 ustar 00root root 0000000 0000000 #
# Pyrex Scanner - Lexical Definitions
#
# Changing anything in this file will cause Lexicon.pickle
# to be rebuilt next time pyrexc is run.
#
raw_prefixes = "rR"
string_prefixes = "cCuUbB"
IDENT = 'IDENT'
def make_lexicon():
from Cython.Plex import \
Str, Any, AnyBut, AnyChar, Rep, Rep1, Opt, Bol, Eol, Eof, \
TEXT, IGNORE, State, Lexicon
from Scanning import Method
letter = Any("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_")
digit = Any("0123456789")
octdigit = Any("01234567")
hexdigit = Any("0123456789ABCDEFabcdef")
indentation = Bol + Rep(Any(" \t"))
decimal = Rep1(digit)
dot = Str(".")
exponent = Any("Ee") + Opt(Any("+-")) + decimal
decimal_fract = (decimal + dot + Opt(decimal)) | (dot + decimal)
name = letter + Rep(letter | digit)
intconst = decimal | (Str("0x") + Rep1(hexdigit))
intsuffix = (Opt(Any("Uu")) + Opt(Any("Ll")) + Opt(Any("Ll"))) | (Opt(Any("Ll")) + Opt(Any("Ll")) + Opt(Any("Uu")))
intliteral = intconst + intsuffix
fltconst = (decimal_fract + Opt(exponent)) | (decimal + exponent)
imagconst = (intconst | fltconst) + Any("jJ")
sq_string = (
Str("'") +
Rep(AnyBut("\\\n'") | (Str("\\") + AnyChar)) +
Str("'")
)
dq_string = (
Str('"') +
Rep(AnyBut('\\\n"') | (Str("\\") + AnyChar)) +
Str('"')
)
non_sq = AnyBut("'") | (Str('\\') + AnyChar)
tsq_string = (
Str("'''")
+ Rep(non_sq | (Str("'") + non_sq) | (Str("''") + non_sq))
+ Str("'''")
)
non_dq = AnyBut('"') | (Str('\\') + AnyChar)
tdq_string = (
Str('"""')
+ Rep(non_dq | (Str('"') + non_dq) | (Str('""') + non_dq))
+ Str('"""')
)
beginstring = Opt(Any(string_prefixes)) + Opt(Any(raw_prefixes)) + (Str("'") | Str('"') | Str("'''") | Str('"""'))
two_oct = octdigit + octdigit
three_oct = octdigit + octdigit + octdigit
two_hex = hexdigit + hexdigit
four_hex = two_hex + two_hex
escapeseq = Str("\\") + (two_oct | three_oct |
Str('u') + four_hex | Str('x') + two_hex |
Str('U') + four_hex + four_hex | AnyChar)
deco = Str("@")
bra = Any("([{")
ket = Any(")]}")
punct = Any(":,;+-*/|&<>=.%`~^?")
diphthong = Str("==", "<>", "!=", "<=", ">=", "<<", ">>", "**", "//",
"+=", "-=", "*=", "/=", "%=", "|=", "^=", "&=",
"<<=", ">>=", "**=", "//=")
spaces = Rep1(Any(" \t\f"))
escaped_newline = Str("\\\n")
lineterm = Eol + Opt(Str("\n"))
comment = Str("#") + Rep(AnyBut("\n"))
return Lexicon([
(name, IDENT),
(intliteral, 'INT'),
(fltconst, 'FLOAT'),
(imagconst, 'IMAG'),
(deco, 'DECORATOR'),
(punct | diphthong, TEXT),
(bra, Method('open_bracket_action')),
(ket, Method('close_bracket_action')),
(lineterm, Method('newline_action')),
#(stringlit, 'STRING'),
(beginstring, Method('begin_string_action')),
(comment, IGNORE),
(spaces, IGNORE),
(escaped_newline, IGNORE),
State('INDENT', [
(comment + lineterm, Method('commentline')),
(Opt(spaces) + Opt(comment) + lineterm, IGNORE),
(indentation, Method('indentation_action')),
(Eof, Method('eof_action'))
]),
State('SQ_STRING', [
(escapeseq, 'ESCAPE'),
(Rep1(AnyBut("'\"\n\\")), 'CHARS'),
(Str('"'), 'CHARS'),
(Str("\n"), Method('unclosed_string_action')),
(Str("'"), Method('end_string_action')),
(Eof, 'EOF')
]),
State('DQ_STRING', [
(escapeseq, 'ESCAPE'),
(Rep1(AnyBut('"\n\\')), 'CHARS'),
(Str("'"), 'CHARS'),
(Str("\n"), Method('unclosed_string_action')),
(Str('"'), Method('end_string_action')),
(Eof, 'EOF')
]),
State('TSQ_STRING', [
(escapeseq, 'ESCAPE'),
(Rep1(AnyBut("'\"\n\\")), 'CHARS'),
(Any("'\""), 'CHARS'),
(Str("\n"), 'NEWLINE'),
(Str("'''"), Method('end_string_action')),
(Eof, 'EOF')
]),
State('TDQ_STRING', [
(escapeseq, 'ESCAPE'),
(Rep1(AnyBut('"\'\n\\')), 'CHARS'),
(Any("'\""), 'CHARS'),
(Str("\n"), 'NEWLINE'),
(Str('"""'), Method('end_string_action')),
(Eof, 'EOF')
]),
(Eof, Method('eof_action'))
],
# FIXME: Plex 1.9 needs different args here from Plex 1.1.4
#debug_flags = scanner_debug_flags,
#debug_file = scanner_dump_file
)
cython-e2d35bac692f6311fa3f2e280b9eea6f7d967514/Cython/Compiler/Main.py 0000664 0000000 0000000 00000072755 11136717635 0024416 0 ustar 00root root 0000000 0000000 #
# Cython Top Level
#
import os, sys, re, codecs
if sys.version_info[:2] < (2, 3):
sys.stderr.write("Sorry, Cython requires Python 2.3 or later\n")
sys.exit(1)
try:
set
except NameError:
# Python 2.3
from sets import Set as set
from time import time
import Code
import Errors
import Parsing
import Version
from Scanning import PyrexScanner, FileSourceDescriptor
from Errors import PyrexError, CompileError, InternalError, error
from Symtab import BuiltinScope, ModuleScope
from Cython import Utils
from Cython.Utils import open_new_file, replace_suffix
import CythonScope
module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$")
verbose = 0
def dumptree(t):
# For quick debugging in pipelines
print t.dump()
return t
class CompilationData:
# Bundles the information that is passed from transform to transform.
# (For now, this is only)
# While Context contains every pxd ever loaded, path information etc.,
# this only contains the data related to a single compilation pass
#
# pyx ModuleNode Main code tree of this compilation.
# pxds {string : ModuleNode} Trees for the pxds used in the pyx.
# codewriter CCodeWriter Where to output final code.
# options CompilationOptions
# result CompilationResult
pass
class Context:
# This class encapsulates the context needed for compiling
# one or more Cython implementation files along with their
# associated and imported declaration files. It includes
# the root of the module import namespace and the list
# of directories to search for include files.
#
# modules {string : ModuleScope}
# include_directories [string]
# future_directives [object]
def __init__(self, include_directories, pragma_overrides):
#self.modules = {"__builtin__" : BuiltinScope()}
import Builtin, CythonScope
self.modules = {"__builtin__" : Builtin.builtin_scope}
self.modules["cython"] = CythonScope.create_cython_scope(self)
self.include_directories = include_directories
self.future_directives = set()
self.pragma_overrides = pragma_overrides
self.pxds = {} # full name -> node tree
standard_include_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', 'Includes'))
self.include_directories = include_directories + [standard_include_path]
def create_pipeline(self, pxd, py=False):
from Visitor import PrintTree
from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform
from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
from ParseTreeTransforms import InterpretCompilerDirectives, TransformBuiltinMethods
from ParseTreeTransforms import ComprehensionTransform, AlignFunctionDefinitions
from AutoDocTransforms import EmbedSignature
from Optimize import FlattenInListTransform, SwitchTransform, IterationTransform
from Optimize import FlattenBuiltinTypeCreation, ConstantFolding, FinalOptimizePhase
from Buffer import IntroduceBufferAuxiliaryVars
from ModuleNode import check_c_declarations
# Temporary hack that can be used to ensure that all result_code's
# are generated at code generation time.
import Visitor
class ClearResultCodes(Visitor.CythonTransform):
def visit_ExprNode(self, node):
self.visitchildren(node)
node.result_code = ""
return node
if pxd:
_check_c_declarations = None
_specific_post_parse = PxdPostParse(self)
else:
_check_c_declarations = check_c_declarations
_specific_post_parse = None
if py and not pxd:
_align_function_definitions = AlignFunctionDefinitions(self)
else:
_align_function_definitions = None
return [
NormalizeTree(self),
PostParse(self),
_specific_post_parse,
InterpretCompilerDirectives(self, self.pragma_overrides),
_align_function_definitions,
FlattenInListTransform(),
WithTransform(self),
DecoratorTransform(self),
AnalyseDeclarationsTransform(self),
EmbedSignature(self),
TransformBuiltinMethods(self),
IntroduceBufferAuxiliaryVars(self),
_check_c_declarations,
AnalyseExpressionsTransform(self),
FlattenBuiltinTypeCreation(),
ConstantFolding(),
# ComprehensionTransform(),
IterationTransform(),
SwitchTransform(),
FinalOptimizePhase(self),
# ClearResultCodes(self),
# SpecialFunctions(self),
# CreateClosureClasses(context),
]
def create_pyx_pipeline(self, options, result, py=False):
def generate_pyx_code(module_node):
module_node.process_implementation(options, result)
result.compilation_source = module_node.compilation_source
return result
def inject_pxd_code(module_node):
from textwrap import dedent
stats = module_node.body.stats
for name, (statlistnode, scope) in self.pxds.iteritems():
# Copy over function nodes to the module
# (this seems strange -- I believe the right concept is to split
# ModuleNode into a ModuleNode and a CodeGenerator, and tell that
# CodeGenerator to generate code both from the pyx and pxd ModuleNodes.
stats.append(statlistnode)
# Until utility code is moved to code generation phase everywhere,
# we need to copy it over to the main scope
module_node.scope.utility_code_list.extend(scope.utility_code_list)
return module_node
return ([
create_parse(self),
] + self.create_pipeline(pxd=False, py=py) + [
inject_pxd_code,
generate_pyx_code,
])
def create_pxd_pipeline(self, scope, module_name):
def parse_pxd(source_desc):
tree = self.parse(source_desc, scope, pxd=True,
full_module_name=module_name)
tree.scope = scope
tree.is_pxd = True
return tree
from CodeGeneration import ExtractPxdCode
# The pxd pipeline ends up with a CCodeWriter containing the
# code of the pxd, as well as a pxd scope.
return [parse_pxd] + self.create_pipeline(pxd=True) + [
ExtractPxdCode(self),
]
def create_py_pipeline(self, options, result):
return self.create_pyx_pipeline(options, result, py=True)
def process_pxd(self, source_desc, scope, module_name):
pipeline = self.create_pxd_pipeline(scope, module_name)
result = self.run_pipeline(pipeline, source_desc)
return result
def nonfatal_error(self, exc):
return Errors.report_error(exc)
def run_pipeline(self, pipeline, source):
err = None
data = source
try:
for phase in pipeline:
if phase is not None:
data = phase(data)
except CompileError, err:
# err is set
Errors.report_error(err)
except InternalError, err:
# Only raise if there was not an earlier error
if Errors.num_errors == 0:
raise
return (err, data)
def find_module(self, module_name,
relative_to = None, pos = None, need_pxd = 1):
# Finds and returns the module scope corresponding to
# the given relative or absolute module name. If this
# is the first time the module has been requested, finds
# the corresponding .pxd file and process it.
# If relative_to is not None, it must be a module scope,
# and the module will first be searched for relative to
# that module, provided its name is not a dotted name.
debug_find_module = 0
if debug_find_module:
print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (
module_name, relative_to, pos, need_pxd))
scope = None
pxd_pathname = None
if not module_name_pattern.match(module_name):
if pos is None:
pos = (module_name, 0, 0)
raise CompileError(pos,
"'%s' is not a valid module name" % module_name)
if "." not in module_name and relative_to:
if debug_find_module:
print("...trying relative import")
scope = relative_to.lookup_submodule(module_name)
if not scope:
qualified_name = relative_to.qualify_name(module_name)
pxd_pathname = self.find_pxd_file(qualified_name, pos)
if pxd_pathname:
scope = relative_to.find_submodule(module_name)
if not scope:
if debug_find_module:
print("...trying absolute import")
scope = self
for name in module_name.split("."):
scope = scope.find_submodule(name)
if debug_find_module:
print("...scope =", scope)
if not scope.pxd_file_loaded:
if debug_find_module:
print("...pxd not loaded")
scope.pxd_file_loaded = 1
if not pxd_pathname:
if debug_find_module:
print("...looking for pxd file")
pxd_pathname = self.find_pxd_file(module_name, pos)
if debug_find_module:
print("......found ", pxd_pathname)
if not pxd_pathname and need_pxd:
error(pos, "'%s.pxd' not found" % module_name)
if pxd_pathname:
try:
if debug_find_module:
print("Context.find_module: Parsing %s" % pxd_pathname)
source_desc = FileSourceDescriptor(pxd_pathname)
err, result = self.process_pxd(source_desc, scope, module_name)
if err:
raise err
(pxd_codenodes, pxd_scope) = result
self.pxds[module_name] = (pxd_codenodes, pxd_scope)
except CompileError:
pass
return scope
def find_pxd_file(self, qualified_name, pos):
# Search include path for the .pxd file corresponding to the
# given fully-qualified module name.
# Will find either a dotted filename or a file in a
# package directory. If a source file position is given,
# the directory containing the source file is searched first
# for a dotted filename, and its containing package root
# directory is searched first for a non-dotted filename.
return self.search_include_directories(qualified_name, ".pxd", pos)
def find_pyx_file(self, qualified_name, pos):
# Search include path for the .pyx file corresponding to the
# given fully-qualified module name, as for find_pxd_file().
return self.search_include_directories(qualified_name, ".pyx", pos)
def find_include_file(self, filename, pos):
# Search list of include directories for filename.
# Reports an error and returns None if not found.
path = self.search_include_directories(filename, "", pos,
include=True)
if not path:
error(pos, "'%s' not found" % filename)
return path
def search_include_directories(self, qualified_name, suffix, pos,
include=False):
# Search the list of include directories for the given
# file name. If a source file position is given, first
# searches the directory containing that file. Returns
# None if not found, but does not report an error.
# The 'include' option will disable package dereferencing.
dirs = self.include_directories
if pos:
file_desc = pos[0]
if not isinstance(file_desc, FileSourceDescriptor):
raise RuntimeError("Only file sources for code supported")
if include:
dirs = [os.path.dirname(file_desc.filename)] + dirs
else:
dirs = [self.find_root_package_dir(file_desc.filename)] + dirs
dotted_filename = qualified_name + suffix
if not include:
names = qualified_name.split('.')
package_names = names[:-1]
module_name = names[-1]
module_filename = module_name + suffix
package_filename = "__init__" + suffix
for dir in dirs:
path = os.path.join(dir, dotted_filename)
if os.path.exists(path):
return path
if not include:
package_dir = self.check_package_dir(dir, package_names)
if package_dir is not None:
path = os.path.join(package_dir, module_filename)
if os.path.exists(path):
return path
path = os.path.join(dir, package_dir, module_name,
package_filename)
if os.path.exists(path):
return path
return None
def find_root_package_dir(self, file_path):
dir = os.path.dirname(file_path)
while self.is_package_dir(dir):
parent = os.path.dirname(dir)
if parent == dir:
break
dir = parent
return dir
def is_package_dir(self, dir):
package_init = os.path.join(dir, "__init__.py")
return os.path.exists(package_init) or \
os.path.exists(package_init + "x") # same with .pyx
def check_package_dir(self, dir, package_names):
package_dir = os.path.join(dir, *package_names)
if not os.path.exists(package_dir):
return None
for dirname in package_names:
dir = os.path.join(dir, dirname)
if not self.is_package_dir(dir):
return None
return package_dir
def c_file_out_of_date(self, source_path):
c_path = Utils.replace_suffix(source_path, ".c")
if not os.path.exists(c_path):
return 1
c_time = Utils.modification_time(c_path)
if Utils.file_newer_than(source_path, c_time):
return 1
pos = [source_path]
pxd_path = Utils.replace_suffix(source_path, ".pxd")
if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time):
return 1
for kind, name in self.read_dependency_file(source_path):
if kind == "cimport":
dep_path = self.find_pxd_file(name, pos)
elif kind == "include":
dep_path = self.search_include_directories(name, pos)
else:
continue
if dep_path and Utils.file_newer_than(dep_path, c_time):
return 1
return 0
def find_cimported_module_names(self, source_path):
return [ name for kind, name in self.read_dependency_file(source_path)
if kind == "cimport" ]
def is_package_dir(self, dir_path):
# Return true if the given directory is a package directory.
for filename in ("__init__.py", "__init__.pyx"):
path = os.path.join(dir_path, filename)
if os.path.exists(path):
return 1
def read_dependency_file(self, source_path):
dep_path = Utils.replace_suffix(source_path, ".dep")
if os.path.exists(dep_path):
f = open(dep_path, "rU")
chunks = [ line.strip().split(" ", 1)
for line in f.readlines()
if " " in line.strip() ]
f.close()
return chunks
else:
return ()
def lookup_submodule(self, name):
# Look up a top-level module. Returns None if not found.
return self.modules.get(name, None)
def find_submodule(self, name):
# Find a top-level module, creating a new one if needed.
scope = self.lookup_submodule(name)
if not scope:
scope = ModuleScope(name,
parent_module = None, context = self)
self.modules[name] = scope
return scope
def parse(self, source_desc, scope, pxd, full_module_name):
if not isinstance(source_desc, FileSourceDescriptor):
raise RuntimeError("Only file sources for code supported")
source_filename = Utils.encode_filename(source_desc.filename)
# Parse the given source file and return a parse tree.
try:
f = Utils.open_source_file(source_filename, "rU")
try:
s = PyrexScanner(f, source_desc, source_encoding = f.encoding,
scope = scope, context = self)
tree = Parsing.p_module(s, pxd, full_module_name)
finally:
f.close()
except UnicodeDecodeError, msg:
#import traceback
#traceback.print_exc()
error((source_desc, 0, 0), "Decoding error, missing or incorrect coding= at top of source (%s)" % msg)
if Errors.num_errors > 0:
raise CompileError
return tree
def extract_module_name(self, path, options):
# Find fully_qualified module name from the full pathname
# of a source file.
dir, filename = os.path.split(path)
module_name, _ = os.path.splitext(filename)
if "." in module_name:
return module_name
if module_name == "__init__":
dir, module_name = os.path.split(dir)
names = [module_name]
while self.is_package_dir(dir):
parent, package_name = os.path.split(dir)
if parent == dir:
break
names.append(package_name)
dir = parent
names.reverse()
return ".".join(names)
def setup_errors(self, options):
if options.use_listing_file:
result.listing_file = Utils.replace_suffix(source, ".lis")
Errors.open_listing_file(result.listing_file,
echo_to_stderr = options.errors_to_stderr)
else:
Errors.open_listing_file(None)
def teardown_errors(self, err, options, result):
source_desc = result.compilation_source.source_desc
if not isinstance(source_desc, FileSourceDescriptor):
raise RuntimeError("Only file sources for code supported")
Errors.close_listing_file()
result.num_errors = Errors.num_errors
if result.num_errors > 0:
err = True
if err and result.c_file:
try:
Utils.castrate_file(result.c_file, os.stat(source_desc.filename))
except EnvironmentError:
pass
result.c_file = None
if result.c_file and not options.c_only and c_compile:
result.object_file = c_compile(result.c_file,
verbose_flag = options.show_version,
cplus = options.cplus)
if not options.obj_only and c_link:
result.extension_file = c_link(result.object_file,
extra_objects = options.objects,
verbose_flag = options.show_version,
cplus = options.cplus)
def create_parse(context):
def parse(compsrc):
source_desc = compsrc.source_desc
full_module_name = compsrc.full_module_name
initial_pos = (source_desc, 1, 0)
scope = context.find_module(full_module_name, pos = initial_pos, need_pxd = 0)
tree = context.parse(source_desc, scope, pxd = 0, full_module_name = full_module_name)
tree.compilation_source = compsrc
tree.scope = scope
tree.is_pxd = False
return tree
return parse
def create_default_resultobj(compilation_source, options):
result = CompilationResult()
result.main_source_file = compilation_source.source_desc.filename
result.compilation_source = compilation_source
source_desc = compilation_source.source_desc
if options.output_file:
result.c_file = os.path.join(compilation_source.cwd, options.output_file)
else:
if options.cplus:
c_suffix = ".cpp"
else:
c_suffix = ".c"
result.c_file = Utils.replace_suffix(source_desc.filename, c_suffix)
return result
def run_pipeline(source, options, full_module_name = None):
# Set up context
context = Context(options.include_path, options.pragma_overrides)
# Set up source object
cwd = os.getcwd()
source_desc = FileSourceDescriptor(os.path.join(cwd, source))
full_module_name = full_module_name or context.extract_module_name(source, options)
source = CompilationSource(source_desc, full_module_name, cwd)
# Set up result object
result = create_default_resultobj(source, options)
# Get pipeline
if source_desc.filename.endswith(".py"):
pipeline = context.create_py_pipeline(options, result)
else:
pipeline = context.create_pyx_pipeline(options, result)
context.setup_errors(options)
err, enddata = context.run_pipeline(pipeline, source)
context.teardown_errors(err, options, result)
return result
#------------------------------------------------------------------------
#
# Main Python entry points
#
#------------------------------------------------------------------------
class CompilationSource(object):
"""
Contains the data necesarry to start up a compilation pipeline for
a single compilation unit.
"""
def __init__(self, source_desc, full_module_name, cwd):
self.source_desc = source_desc
self.full_module_name = full_module_name
self.cwd = cwd
class CompilationOptions:
"""
Options to the Cython compiler:
show_version boolean Display version number
use_listing_file boolean Generate a .lis file
errors_to_stderr boolean Echo errors to stderr when using .lis
include_path [string] Directories to search for include files
output_file string Name of generated .c file
generate_pxi boolean Generate .pxi file for public declarations
recursive boolean Recursively find and compile dependencies
timestamps boolean Only compile changed source files. If None,
defaults to true when recursive is true.
verbose boolean Always print source names being compiled
quiet boolean Don't print source names in recursive mode
pragma_overrides dict Overrides for pragma options (see Options.py)
Following options are experimental and only used on MacOSX:
c_only boolean Stop after generating C file (default)
obj_only boolean Stop after compiling to .o file
objects [string] Extra .o files to link with
cplus boolean Compile as c++ code
"""
def __init__(self, defaults = None, c_compile = 0, c_link = 0, **kw):
self.include_path = []
self.objects = []
if defaults:
if isinstance(defaults, CompilationOptions):
defaults = defaults.__dict__
else:
defaults = default_options
self.__dict__.update(defaults)
self.__dict__.update(kw)
if c_compile:
self.c_only = 0
if c_link:
self.obj_only = 0
class CompilationResult:
"""
Results from the Cython compiler:
c_file string or None The generated C source file
h_file string or None The generated C header file
i_file string or None The generated .pxi file
api_file string or None The generated C API .h file
listing_file string or None File of error messages
object_file string or None Result of compiling the C file
extension_file string or None Result of linking the object file
num_errors integer Number of compilation errors
compilation_source CompilationSource
"""
def __init__(self):
self.c_file = None
self.h_file = None
self.i_file = None
self.api_file = None
self.listing_file = None
self.object_file = None
self.extension_file = None
self.main_source_file = None
class CompilationResultSet(dict):
"""
Results from compiling multiple Pyrex source files. A mapping
from source file paths to CompilationResult instances. Also
has the following attributes:
num_errors integer Total number of compilation errors
"""
num_errors = 0
def add(self, source, result):
self[source] = result
self.num_errors += result.num_errors
def compile_single(source, options, full_module_name = None):
"""
compile_single(source, options, full_module_name)
Compile the given Pyrex implementation file and return a CompilationResult.
Always compiles a single file; does not perform timestamp checking or
recursion.
"""
return run_pipeline(source, options, full_module_name)
def compile_multiple(sources, options):
"""
compile_multiple(sources, options)
Compiles the given sequence of Pyrex implementation files and returns
a CompilationResultSet. Performs timestamp checking and/or recursion
if these are specified in the options.
"""
sources = [os.path.abspath(source) for source in sources]
processed = set()
results = CompilationResultSet()
recursive = options.recursive
timestamps = options.timestamps
if timestamps is None:
timestamps = recursive
verbose = options.verbose or ((recursive or timestamps) and not options.quiet)
for source in sources:
if source not in processed:
# Compiling multiple sources in one context doesn't quite
# work properly yet.
if not timestamps or context.c_file_out_of_date(source):
if verbose:
sys.stderr.write("Compiling %s\n" % source)
result = run_pipeline(source, options)
results.add(source, result)
processed.add(source)
if recursive:
for module_name in context.find_cimported_module_names(source):
path = context.find_pyx_file(module_name, [source])
if path:
sources.append(path)
else:
sys.stderr.write(
"Cannot find .pyx file for cimported module '%s'\n" % module_name)
return results
def compile(source, options = None, c_compile = 0, c_link = 0,
full_module_name = None, **kwds):
"""
compile(source [, options], [,