overridable = 0
optional_arg_count = 0
- def analyse(self, return_type, env, nonempty = 0):
+ def analyse(self, return_type, env, nonempty = 0, directive_locals = {}):
if nonempty:
nonempty -= 1
func_type_args = []
name_declarator, type = arg_node.analyse(env, nonempty = nonempty,
is_self_arg = (i == 0 and env.is_c_class_scope))
name = name_declarator.name
+ if name in directive_locals:
+ type_node = directive_locals[name]
+ other_type = type_node.analyse_as_type(env)
+ if other_type is None:
+ error(type_node.pos, "Not a type")
+ elif (type is not PyrexTypes.py_object_type
+ and not type.same_as(other_type)):
+ error(self.base.pos, "Signature does not agree with previous declaration")
+ error(type_node.pos, "Previous declaration here")
+ else:
+ type = other_type
if name_declarator.cname:
error(self.pos,
"Function argument cannot have C name specification")
error(self.exception_value.pos,
"Exception value incompatible with function return type")
exc_check = self.exception_check
- if return_type.is_array:
- error(self.pos,
- "Function cannot return an array")
if return_type.is_cfunction:
error(self.pos,
"Function cannot return a function")
is_self_arg = 0
is_type_arg = 0
is_generic = 1
+ kw_only = 0
+ not_none = 0
+ or_none = 0
type = None
name_declarator = None
default_value = None
child_attrs = []
arg_name = None # in case the argument name was interpreted as a type
+ module_path = []
+ is_basic_c_type = False
+ complex = False
def analyse(self, env, could_be_name = False):
# Return type descriptor.
child_attrs = ["base_type", "declarators"]
decorators = None
- directive_locals = {}
+ directive_locals = None
def analyse_declarations(self, env, dest_scope = None):
+ if self.directive_locals is None:
+ self.directive_locals = {}
if not dest_scope:
dest_scope = env
self.dest_scope = dest_scope
visibility = self.visibility
for declarator in self.declarators:
- name_declarator, type = declarator.analyse(base_type, env)
+ if isinstance(declarator, CFuncDeclaratorNode):
+ name_declarator, type = declarator.analyse(base_type, env, directive_locals=self.directive_locals)
+ else:
+ name_declarator, type = declarator.analyse(base_type, env)
if not type.is_complete():
if not (self.visibility == 'extern' and type.is_array):
error(declarator.pos,
cname = cname, visibility = self.visibility, in_pxd = self.in_pxd,
api = self.api)
if entry is not None:
- entry.directive_locals = self.directive_locals
+ entry.directive_locals = copy.copy(self.directive_locals)
else:
if self.directive_locals:
error(self.pos, "Decorators can only be followed by functions")
error(self.pos,
"Only 'extern' C variable declaration allowed in .pxd file")
entry = dest_scope.declare_var(name, type, declarator.pos,
- cname = cname, visibility = visibility, is_cdef = 1)
+ cname=cname, visibility=visibility, api=self.api, is_cdef=1)
entry.needs_property = need_property
# kind "struct" or "union"
# typedef_flag boolean
# visibility "public" or "private"
+ # api boolean
# in_pxd boolean
# attributes [CVarDefNode] or None
# entry Entry
scope = StructOrUnionScope(self.name)
self.entry = env.declare_struct_or_union(
self.name, self.kind, scope, self.typedef_flag, self.pos,
- self.cname, visibility = self.visibility, packed = self.packed)
+ self.cname, visibility = self.visibility, api = self.api,
+ packed = self.packed)
if self.attributes is not None:
if self.in_pxd and not env.in_cinclude:
self.entry.defined_in_pxd = 1
self.entry = env.declare_cpp_class(
self.name, scope, self.pos,
self.cname, base_class_types, visibility = self.visibility, templates = template_types)
+ if self.entry is None:
+ return
self.entry.is_cpp_class = 1
if self.attributes is not None:
if self.in_pxd and not env.in_cinclude:
# items [CEnumDefItemNode]
# typedef_flag boolean
# visibility "public" or "private"
+ # api boolean
# in_pxd boolean
# entry Entry
-
+
child_attrs = ["items"]
def analyse_declarations(self, env):
self.entry = env.declare_enum(self.name, self.pos,
cname = self.cname, typedef_flag = self.typedef_flag,
- visibility = self.visibility)
+ visibility = self.visibility, api = self.api)
if self.items is not None:
if self.in_pxd and not env.in_cinclude:
self.entry.defined_in_pxd = 1
pass
def generate_execution_code(self, code):
- if self.visibility == 'public':
+ if self.visibility == 'public' or self.api:
temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=True)
for item in self.entry.enum_values:
code.putln("%s = PyInt_FromLong(%s); %s" % (
if not self.value.type.is_int:
self.value = self.value.coerce_to(PyrexTypes.c_int_type, env)
self.value.analyse_const_expression(env)
- entry = env.declare_const(self.name, enum_entry.type,
+ entry = env.declare_const(self.name, enum_entry.type,
self.value, self.pos, cname = self.cname,
- visibility = enum_entry.visibility)
+ visibility = enum_entry.visibility, api = enum_entry.api)
enum_entry.enum_values.append(entry)
# base_type CBaseTypeNode
# declarator CDeclaratorNode
# visibility "public" or "private"
+ # api boolean
# in_pxd boolean
child_attrs = ["base_type", "declarator"]
name = name_declarator.name
cname = name_declarator.cname
entry = env.declare_typedef(name, type, self.pos,
- cname = cname, visibility = self.visibility)
+ cname = cname, visibility = self.visibility, api = self.api)
if self.in_pxd and not env.in_cinclude:
entry.defined_in_pxd = 1
-
+
def analyse_expressions(self, env):
pass
def generate_execution_code(self, code):
needs_closure = False
needs_outer_scope = False
is_generator = False
+ is_generator_body = False
modifiers = []
def analyse_default_values(self, env):
elif default_seen:
error(arg.pos, "Non-default argument following default argument")
+ def align_argument_type(self, env, arg):
+ directive_locals = self.directive_locals
+ type = arg.type
+ if arg.name in directive_locals:
+ type_node = directive_locals[arg.name]
+ other_type = type_node.analyse_as_type(env)
+ if other_type is None:
+ error(type_node.pos, "Not a type")
+ elif (type is not PyrexTypes.py_object_type
+ and not type.same_as(other_type)):
+ error(arg.base_type.pos, "Signature does not agree with previous declaration")
+ error(type_node.pos, "Previous declaration here")
+ else:
+ arg.type = other_type
+ return arg
+
def need_gil_acquisition(self, lenv):
return 0
lenv.directives = env.directives
return lenv
+ def generate_function_body(self, env, code):
+ self.body.generate_execution_code(code)
+
def generate_function_definitions(self, env, code):
import Buffer
preprocessor_guard = None
profile = code.globalstate.directives['profile']
+ if profile and lenv.nogil:
+ warning(self.pos, "Cannot profile nogil function.", 1)
+ profile = False
if profile:
- if lenv.nogil:
- error(self.pos, "Cannot profile nogil function.")
code.globalstate.use_utility_code(profile_utility_code)
# Generate C code for header and body of function
(self.return_type.declaration_code(Naming.retval_cname),
init))
tempvardecl_code = code.insertion_point()
- code.put_declare_refcount_context()
- if not self.is_generator:
- self.generate_keyword_list(code)
+ if not lenv.nogil:
+ code.put_declare_refcount_context()
+ self.generate_keyword_list(code)
if profile:
code.put_trace_declarations()
# ----- Extern library function declarations
if is_getbuffer_slot:
self.getbuffer_init(code)
# ----- Create closure scope object
- if self.is_generator:
- code.putln("%s = (%s) %s;" % (
- Naming.cur_scope_cname,
- lenv.scope_class.type.declaration_code(''),
- Naming.self_cname))
- gotref_code = code.insertion_point()
-
- elif self.needs_closure:
+ if self.needs_closure:
code.putln("%s = (%s)%s->tp_new(%s, %s, NULL);" % (
Naming.cur_scope_cname,
lenv.scope_class.type.declaration_code(''),
code.putln("}")
code.put_gotref(Naming.cur_scope_cname)
# Note that it is unsafe to decref the scope at this point.
- if self.needs_outer_scope and not self.is_generator:
+ if self.needs_outer_scope:
code.putln("%s = (%s)%s;" % (
outer_scope_cname,
cenv.scope_class.type.declaration_code(''),
# fatal error before hand, it's not really worth tracing
code.put_trace_call(self.entry.name, self.pos)
# ----- Fetch arguments
- if not self.is_generator:
- self.generate_preamble(env, code)
- if self.is_generator:
- code.funcstate.init_closure_temps(lenv.scope_class.type.scope)
- resume_code = code.insertion_point()
- first_run_label = code.new_label('first_run')
- code.use_label(first_run_label)
- code.put_label(first_run_label)
- code.putln('%s' %
- (code.error_goto_if_null(Naming.sent_value_cname, self.pos)))
+ self.generate_argument_parsing_code(env, code)
+ # If an argument is assigned to in the body, we must
+ # incref it to properly keep track of refcounts.
+ for entry in lenv.arg_entries:
+ if entry.type.is_pyobject:
+ if (acquire_gil or entry.assignments) and not entry.in_closure:
+ code.put_var_incref(entry)
+ # ----- Initialise local variables
+ for entry in lenv.var_entries:
+ if entry.type.is_pyobject and entry.init_to_none and entry.used:
+ code.put_init_var_to_py_none(entry)
+ # ----- Initialise local buffer auxiliary variables
+ for entry in lenv.var_entries + lenv.arg_entries:
+ if entry.type.is_buffer and entry.buffer_aux.buffer_info_var.used:
+ code.putln("%s.buf = NULL;" %
+ entry.buffer_aux.buffer_info_var.cname)
+ # ----- Check and convert arguments
+ self.generate_argument_type_tests(code)
+ # ----- Acquire buffer arguments
+ for entry in lenv.arg_entries:
+ if entry.type.is_buffer:
+ Buffer.put_acquire_arg_buffer(entry, code, self.pos)
+
# -------------------------
# ----- Function body -----
# -------------------------
- self.body.generate_execution_code(code)
-
- if self.is_generator:
- code.putln('PyErr_SetNone(PyExc_StopIteration); %s' % code.error_goto(self.pos))
+ self.generate_function_body(env, code)
# ----- Default return value
code.putln("")
# Decref any increfed args
for entry in lenv.arg_entries:
if entry.type.is_pyobject:
- if entry.assignments and not entry.in_closure:
+ if (acquire_gil or entry.assignments) and not entry.in_closure:
code.put_var_decref(entry)
- if self.needs_closure and not self.is_generator:
+ if self.needs_closure:
code.put_decref(Naming.cur_scope_cname, lenv.scope_class.type)
- if self.is_generator:
- code.putln('%s->%s.resume_label = -1;' % (Naming.cur_scope_cname, Naming.obj_base_cname))
# ----- Return
# This code is duplicated in ModuleNode.generate_module_init_func
if preprocessor_guard:
code.putln("#endif /*!(%s)*/" % preprocessor_guard)
+
# ----- Go back and insert temp variable declarations
tempvardecl_code.put_temp_declarations(code.funcstate)
- # ----- Generator resume code
- if self.is_generator:
- resume_code.putln("switch (%s->%s.resume_label) {" % (Naming.cur_scope_cname, Naming.obj_base_cname));
- resume_code.putln("case 0: goto %s;" % first_run_label)
- for yield_expr in self.yields:
- resume_code.putln("case %d: goto %s;" % (yield_expr.label_num, yield_expr.label_name));
- resume_code.putln("default: /* CPython raises the right error here */");
- resume_code.putln("return NULL;");
- resume_code.putln("}");
# ----- Python version
code.exit_cfunc_scope()
if self.py_func:
self.py_func.generate_function_definitions(env, code)
self.generate_wrapper_functions(code)
- if self.is_generator:
- self.generator.generate_function_body(self.local_scope, code)
-
- def generate_preamble(self, env, code):
- """Parse arguments and prepare scope"""
- import Buffer
-
- lenv = self.local_scope
-
- self.generate_argument_parsing_code(env, code)
- # If an argument is assigned to in the body, we must
- # incref it to properly keep track of refcounts.
- for entry in lenv.arg_entries:
- if entry.type.is_pyobject:
- if entry.assignments and not entry.in_closure:
- code.put_var_incref(entry)
- # ----- Initialise local variables
- for entry in lenv.var_entries:
- if entry.type.is_pyobject and entry.init_to_none and entry.used:
- code.put_init_var_to_py_none(entry)
- # ----- Initialise local buffer auxiliary variables
- for entry in lenv.var_entries + lenv.arg_entries:
- if entry.type.is_buffer and entry.buffer_aux.buffer_info_var.used:
- code.putln("%s.buf = NULL;" %
- entry.buffer_aux.buffer_info_var.cname)
- # ----- Check and convert arguments
- self.generate_argument_type_tests(code)
- # ----- Acquire buffer arguments
- for entry in lenv.arg_entries:
- if entry.type.is_buffer:
- Buffer.put_acquire_arg_buffer(entry, code, self.pos)
-
def declare_argument(self, env, arg):
if arg.type.is_void:
error(arg.pos, "Invalid use of 'void'")
def generate_arg_none_check(self, arg, code):
# Generate None check for one argument.
- code.globalstate.use_utility_code(arg_type_test_utility_code)
code.putln('if (unlikely(((PyObject *)%s) == Py_None)) {' % arg.entry.cname)
code.putln('''PyErr_Format(PyExc_TypeError, "Argument '%s' must not be None"); %s''' % (
arg.name,
info = self.local_scope.arg_entries[1].cname
# Python 3.0 betas have a bug in memoryview which makes it call
# getbuffer with a NULL parameter. For now we work around this;
- # the following line should be removed when this bug is fixed.
- code.putln("if (%s == NULL) return 0;" % info)
+ # the following block should be removed when this bug is fixed.
+ code.putln("if (%s != NULL) {" % info)
code.putln("%s->obj = Py_None; __Pyx_INCREF(Py_None);" % info)
code.put_giveref("%s->obj" % info) # Do not refnanny object within structs
+ code.putln("}")
def getbuffer_error_cleanup(self, code):
info = self.local_scope.arg_entries[1].cname
+ code.putln("if (%s != NULL && %s->obj != NULL) {"
+ % (info, info))
code.put_gotref("%s->obj" % info)
- code.putln("__Pyx_DECREF(%s->obj); %s->obj = NULL;" %
- (info, info))
+ code.putln("__Pyx_DECREF(%s->obj); %s->obj = NULL;"
+ % (info, info))
+ code.putln("}")
def getbuffer_normal_cleanup(self, code):
info = self.local_scope.arg_entries[1].cname
- code.putln("if (%s->obj == Py_None) {" % info)
+ code.putln("if (%s != NULL && %s->obj == Py_None) {" % (info, info))
code.put_gotref("Py_None")
code.putln("__Pyx_DECREF(Py_None); %s->obj = NULL;" % info)
code.putln("}")
inline_in_pxd = False
decorators = None
- directive_locals = {}
+ directive_locals = None
def unqualified_name(self):
return self.entry.name
def analyse_declarations(self, env):
+ if self.directive_locals is None:
+ self.directive_locals = {}
self.directive_locals.update(env.directives['locals'])
base_type = self.base_type.analyse(env)
# The 2 here is because we need both function and argument names.
- name_declarator, type = self.declarator.analyse(base_type, env, nonempty = 2 * (self.body is not None))
+ if isinstance(self.declarator, CFuncDeclaratorNode):
+ name_declarator, type = self.declarator.analyse(base_type, env,
+ nonempty = 2 * (self.body is not None),
+ directive_locals = self.directive_locals)
+ else:
+ name_declarator, type = self.declarator.analyse(base_type, env, nonempty = 2 * (self.body is not None))
if not type.is_cfunction:
error(self.pos,
"Suite attached to non-function declaration")
declarator = declarator.base
self.args = declarator.args
for formal_arg, type_arg in zip(self.args, type.args):
+ self.align_argument_type(env, type_arg)
formal_arg.type = type_arg.type
formal_arg.name = type_arg.name
formal_arg.cname = type_arg.cname
+ if type_arg.type.is_buffer and 'inline' in self.modifiers:
+ warning(formal_arg.pos, "Buffer unpacking not optimized away.", 1)
name = name_declarator.name
cname = name_declarator.cname
self.entry = env.declare_cfunction(
api = self.api, modifiers = self.modifiers)
self.entry.inline_func_in_pxd = self.inline_in_pxd
self.return_type = type.return_type
+ if self.return_type.is_array and visibility != 'extern':
+ error(self.pos,
+ "Function cannot return an array")
if self.overridable and not env.is_module_scope:
if len(self.args) < 1 or not self.args[0].type.is_pyobject:
def generate_function_header(self, code, with_pymethdef, with_opt_args = 1, with_dispatch = 1, cname = None):
arg_decls = []
type = self.type
- visibility = self.entry.visibility
for arg in type.args[:len(type.args)-type.optional_arg_count]:
arg_decls.append(arg.declaration_code())
if with_dispatch and self.overridable:
if cname is None:
cname = self.entry.func_cname
entity = type.function_header_code(cname, ', '.join(arg_decls))
- if visibility == 'public':
- dll_linkage = "DL_EXPORT"
+ if self.entry.visibility == 'private':
+ storage_class = "static "
else:
- dll_linkage = None
- header = self.return_type.declaration_code(entity,
- dll_linkage = dll_linkage)
- if visibility == 'extern':
- storage_class = "%s " % Naming.extern_c_macro
- elif visibility == 'public':
storage_class = ""
- else:
- storage_class = "static "
+ dll_linkage = None
+ modifiers = ""
if 'inline' in self.modifiers:
self.modifiers[self.modifiers.index('inline')] = 'cython_inline'
- code.putln("%s%s %s {" % (
- storage_class,
- ' '.join(self.modifiers).upper(), # macro forms
- header))
+ if self.modifiers:
+ modifiers = "%s " % ' '.join(self.modifiers).upper()
+
+ header = self.return_type.declaration_code(entity, dll_linkage=dll_linkage)
+ #print (storage_class, modifiers, header)
+ code.putln("%s%s%s {" % (storage_class, modifiers, header))
def generate_argument_declarations(self, env, code):
for arg in self.args:
child_attrs = ['decorator']
-class GeneratorWrapperNode(object):
- # Wrapper
- def __init__(self, def_node, func_cname=None, body_cname=None, header=None):
- self.def_node = def_node
- self.func_cname = func_cname
- self.body_cname = body_cname
- self.header = header
-
- def generate_function_body(self, env, code):
- code.mark_pos(self.def_node.pos)
- cenv = env.outer_scope # XXX: correct?
- while cenv.is_py_class_scope or cenv.is_c_class_scope:
- cenv = cenv.outer_scope
- lenv = self.def_node.local_scope
- code.enter_cfunc_scope()
- code.putln()
- code.putln('%s {' % self.header)
- code.put_declare_refcount_context()
- self.def_node.generate_keyword_list(code)
- code.put(lenv.scope_class.type.declaration_code(Naming.cur_scope_cname))
- code.putln(";")
- code.put_setup_refcount_context(self.def_node.entry.name)
- code.putln("%s = (%s)%s->tp_new(%s, %s, NULL);" % (
- Naming.cur_scope_cname,
- lenv.scope_class.type.declaration_code(''),
- lenv.scope_class.type.typeptr_cname,
- lenv.scope_class.type.typeptr_cname,
- Naming.empty_tuple))
- code.putln("if (unlikely(!%s)) {" % Naming.cur_scope_cname)
- code.put_finish_refcount_context()
- code.putln("return NULL;");
- code.putln("}");
- code.put_gotref(Naming.cur_scope_cname)
-
- if self.def_node.needs_outer_scope:
- outer_scope_cname = '%s->%s' % (Naming.cur_scope_cname, Naming.outer_scope_cname)
- code.putln("%s = (%s)%s;" % (
- outer_scope_cname,
- cenv.scope_class.type.declaration_code(''),
- Naming.self_cname))
- code.put_incref(outer_scope_cname, cenv.scope_class.type)
- code.put_giveref(outer_scope_cname)
-
- self.def_node.generate_preamble(env, code)
-
- generator_cname = '%s->%s' % (Naming.cur_scope_cname, Naming.obj_base_cname)
-
- code.putln('%s.resume_label = 0;' % generator_cname)
- code.putln('%s.body = %s;' % (generator_cname, self.body_cname))
- code.put_giveref(Naming.cur_scope_cname)
- code.put_finish_refcount_context()
- code.putln("return (PyObject *) %s;" % Naming.cur_scope_cname);
- code.putln('}\n')
- code.exit_cfunc_scope()
-
class DefNode(FuncDefNode):
# A Python function definition.
#
num_required_kw_args = 0
reqd_kw_flags_cname = "0"
is_wrapper = 0
+ no_assignment_synthesis = 0
decorators = None
return_type_annotation = None
entry = None
acquire_gil = 0
self_in_stararg = 0
+ star_arg = None
+ starstar_arg = None
+ doc = None
def __init__(self, pos, **kwds):
FuncDefNode.__init__(self, pos, **kwds)
api = False,
directive_locals = getattr(cfunc, 'directive_locals', {}))
+ def is_cdef_func_compatible(self):
+ """Determines if the function's signature is compatible with a
+ cdef function. This can be used before calling
+ .as_cfunction() to see if that will be successful.
+ """
+ if self.needs_closure:
+ return False
+ if self.star_arg or self.starstar_arg:
+ return False
+ return True
+
def analyse_declarations(self, env):
self.is_classmethod = self.is_staticmethod = False
if self.decorators:
# staticmethod() was overridden - not much we can do here ...
self.is_staticmethod = False
- if self.name == '__new__':
+ if self.name == '__new__' and env.is_py_class_scope:
self.is_staticmethod = 1
self.analyse_argument_types(env)
allow_none_for_extension_args = env.directives['allow_none_for_extension_args']
for arg in self.args:
if hasattr(arg, 'name'):
- type = arg.type
name_declarator = None
else:
base_type = arg.base_type.analyse(env)
name_declarator, type = \
arg.declarator.analyse(base_type, env)
arg.name = name_declarator.name
- if arg.name in directive_locals:
- type_node = directive_locals[arg.name]
- other_type = type_node.analyse_as_type(env)
- if other_type is None:
- error(type_node.pos, "Not a type")
- elif (type is not PyrexTypes.py_object_type
- and not type.same_as(other_type)):
- error(arg.base_type.pos, "Signature does not agree with previous declaration")
- error(type_node.pos, "Previous declaration here")
- else:
- type = other_type
+ arg.type = type
+ self.align_argument_type(env, arg)
if name_declarator and name_declarator.cname:
error(self.pos,
"Python function argument cannot have C name specification")
- arg.type = type.as_argument_type()
+ arg.type = arg.type.as_argument_type()
arg.hdr_type = None
arg.needs_conversion = 0
arg.needs_type_test = 0
def analyse_signature(self, env):
if self.entry.is_special:
+ if self.decorators:
+ error(self.pos, "special functions of cdef classes cannot have decorators")
self.entry.trivial_signature = len(self.args) == 1 and not (self.star_arg or self.starstar_arg)
elif not env.directives['always_allow_keywords'] and not (self.star_arg or self.starstar_arg):
# Use the simpler calling signature for zero- and one-argument functions.
Naming.pyfunc_prefix + prefix + name
entry.pymethdef_cname = \
Naming.pymethdef_prefix + prefix + name
-
- if self.is_generator:
- self.generator_body_cname = Naming.genbody_prefix + env.next_id(env.scope_prefix) + name
-
if Options.docstrings:
entry.doc = embed_position(self.pos, self.doc)
entry.doc_cname = \
entry.doc = None
def declare_lambda_function(self, env):
- name = self.name
- prefix = env.scope_prefix
- func_cname = \
- Naming.lambda_func_prefix + u'funcdef' + prefix + self.lambda_name
- entry = env.declare_lambda_function(func_cname, self.pos)
- entry.pymethdef_cname = \
- Naming.lambda_func_prefix + u'methdef' + prefix + self.lambda_name
- entry.qualified_name = env.qualify_name(self.lambda_name)
+ entry = env.declare_lambda_function(self.lambda_name, self.pos)
entry.doc = None
self.entry = entry
self.synthesize_assignment_node(env)
def needs_assignment_synthesis(self, env, code=None):
+ if self.no_assignment_synthesis:
+ return False
# Should enable for module level as well, that will require more testing...
if self.entry.is_anonymous:
return True
arg.hdr_type.declaration_code(arg.hdr_cname))
if not self.entry.is_special and sig.method_flags() == [TypeSlots.method_noargs]:
arg_code_list.append("CYTHON_UNUSED PyObject *unused")
+ if (self.entry.scope.is_c_class_scope and self.entry.name == "__ipow__"):
+ arg_code_list.append("CYTHON_UNUSED PyObject *unused")
if sig.has_generic_args:
arg_code_list.append(
"PyObject *%s, PyObject *%s"
"static PyMethodDef %s = " %
self.entry.pymethdef_cname)
code.put_pymethoddef(self.entry, ";", allow_skip=False)
- if self.is_generator:
- code.putln("static PyObject *%s(PyObject *%s, PyObject *%s) /* generator body */\n{" %
- (self.generator_body_cname, Naming.self_cname, Naming.sent_value_cname))
- self.generator = GeneratorWrapperNode(self,
- func_cname=self.entry.func_cname,
- body_cname=self.generator_body_cname,
- header=header)
- else:
- code.putln("%s {" % header)
+ code.putln("%s {" % header)
def generate_argument_declarations(self, env, code):
for arg in self.args:
self.name, Naming.args_cname, self.error_value()))
code.putln("}")
- code.globalstate.use_utility_code(keyword_string_check_utility_code)
-
if self.starstar_arg:
if self.star_arg:
kwarg_check = "unlikely(%s)" % Naming.kwds_cname
else:
kwarg_check = "unlikely(%s) && unlikely(PyDict_Size(%s) > 0)" % (
Naming.kwds_cname, Naming.kwds_cname)
+ code.globalstate.use_utility_code(keyword_string_check_utility_code)
code.putln(
"if (%s && unlikely(!__Pyx_CheckKeywordStrings(%s, \"%s\", %d))) return %s;" % (
kwarg_check, Naming.kwds_cname, self.name,
has_fixed_positional_count = not self.star_arg and \
min_positional_args == max_positional_args
- code.globalstate.use_utility_code(raise_double_keywords_utility_code)
- code.globalstate.use_utility_code(raise_argtuple_invalid_utility_code)
if self.num_required_kw_args:
code.globalstate.use_utility_code(raise_keyword_required_utility_code)
if code.label_used(argtuple_error_label):
code.put_goto(success_label)
code.put_label(argtuple_error_label)
+ code.globalstate.use_utility_code(raise_argtuple_invalid_utility_code)
code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d, PyTuple_GET_SIZE(%s)); ' % (
self.name, has_fixed_positional_count,
min_positional_args, max_positional_args,
max_positional_args, Naming.args_cname))
code.putln("if (unlikely(!%s)) {" % self.star_arg.entry.cname)
if self.starstar_arg:
- code.put_decref(self.starstar_arg.entry.cname, py_object_type)
+ code.put_decref_clear(self.starstar_arg.entry.cname, py_object_type)
if self.needs_closure:
code.put_decref(Naming.cur_scope_cname, self.local_scope.scope_class.type)
code.put_finish_refcount_context()
# kwargs) that were passed into positional
# arguments up to this point
code.putln('else {')
+ code.globalstate.use_utility_code(raise_argtuple_invalid_utility_code)
code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d, %d); ' % (
self.name, has_fixed_positional_count,
min_positional_args, max_positional_args, i))
def caller_will_check_exceptions(self):
return 1
+
+class GeneratorDefNode(DefNode):
+ # Generator DefNode.
+ #
+ # gbody GeneratorBodyDefNode
+ #
+
+ is_generator = True
+ needs_closure = True
+
+ child_attrs = DefNode.child_attrs + ["gbody"]
+
+ def __init__(self, **kwargs):
+ # XXX: don't actually needs a body
+ kwargs['body'] = StatListNode(kwargs['pos'], stats=[])
+ super(GeneratorDefNode, self).__init__(**kwargs)
+
+ def analyse_declarations(self, env):
+ super(GeneratorDefNode, self).analyse_declarations(env)
+ self.gbody.local_scope = self.local_scope
+ self.gbody.analyse_declarations(env)
+
+ def generate_function_body(self, env, code):
+ body_cname = self.gbody.entry.func_cname
+ generator_cname = '%s->%s' % (Naming.cur_scope_cname, Naming.obj_base_cname)
+
+ code.putln('%s.resume_label = 0;' % generator_cname)
+ code.putln('%s.body = (__pyx_generator_body_t) %s;' % (generator_cname, body_cname))
+ code.put_giveref(Naming.cur_scope_cname)
+ code.put_finish_refcount_context()
+ code.putln("return (PyObject *) %s;" % Naming.cur_scope_cname);
+
+ def generate_function_definitions(self, env, code):
+ self.gbody.generate_function_header(code, proto=True)
+ super(GeneratorDefNode, self).generate_function_definitions(env, code)
+ self.gbody.generate_function_definitions(env, code)
+
+
+class GeneratorBodyDefNode(DefNode):
+ # Generator body DefNode.
+ #
+
+ is_generator_body = True
+
+ def __init__(self, pos=None, name=None, body=None):
+ super(GeneratorBodyDefNode, self).__init__(pos=pos, body=body, name=name, doc=None,
+ args=[],
+ star_arg=None, starstar_arg=None)
+
+ def declare_generator_body(self, env):
+ prefix = env.next_id(env.scope_prefix)
+ name = env.next_id('generator')
+ entry = env.declare_var(prefix + name, py_object_type, self.pos, visibility='private')
+ entry.func_cname = Naming.genbody_prefix + prefix + name
+ entry.qualified_name = EncodedString(self.name)
+ self.entry = entry
+
+ def analyse_declarations(self, env):
+ self.analyse_argument_types(env)
+ self.declare_generator_body(env)
+
+ def generate_function_header(self, code, proto=False):
+ header = "static PyObject *%s(%s, PyObject *%s)" % (
+ self.entry.func_cname,
+ self.local_scope.scope_class.type.declaration_code(Naming.cur_scope_cname),
+ Naming.sent_value_cname)
+ if proto:
+ code.putln('%s; /* proto */' % header)
+ else:
+ code.putln('%s /* generator body */\n{' % header);
+
+ def generate_function_definitions(self, env, code):
+ lenv = self.local_scope
+
+ # Generate closure function definitions
+ self.body.generate_function_definitions(lenv, code)
+
+ # Generate C code for header and body of function
+ code.enter_cfunc_scope()
+ code.return_from_error_cleanup_label = code.new_label()
+
+ # ----- Top-level constants used by this function
+ code.mark_pos(self.pos)
+ self.generate_cached_builtins_decls(lenv, code)
+ # ----- Function header
+ code.putln("")
+ self.generate_function_header(code)
+ # ----- Local variables
+ code.putln("PyObject *%s = NULL;" % Naming.retval_cname)
+ tempvardecl_code = code.insertion_point()
+ code.put_declare_refcount_context()
+ code.put_setup_refcount_context(self.entry.name)
+
+ # ----- Resume switch point.
+ code.funcstate.init_closure_temps(lenv.scope_class.type.scope)
+ resume_code = code.insertion_point()
+ first_run_label = code.new_label('first_run')
+ code.use_label(first_run_label)
+ code.put_label(first_run_label)
+ code.putln('%s' %
+ (code.error_goto_if_null(Naming.sent_value_cname, self.pos)))
+
+ # ----- Function body
+ self.generate_function_body(env, code)
+ code.putln('PyErr_SetNone(PyExc_StopIteration); %s' % code.error_goto(self.pos))
+ # ----- Error cleanup
+ if code.error_label in code.labels_used:
+ code.put_goto(code.return_label)
+ code.put_label(code.error_label)
+ for cname, type in code.funcstate.all_managed_temps():
+ code.put_xdecref(cname, type)
+ code.putln('__Pyx_AddTraceback("%s");' % self.entry.qualified_name)
+
+ # ----- Non-error return cleanup
+ code.put_label(code.return_label)
+ code.put_xdecref(Naming.retval_cname, py_object_type)
+ code.putln('%s->%s.resume_label = -1;' % (Naming.cur_scope_cname, Naming.obj_base_cname))
+ code.put_finish_refcount_context()
+ code.putln('return NULL;');
+ code.putln("}")
+
+ # ----- Go back and insert temp variable declarations
+ tempvardecl_code.put_temp_declarations(code.funcstate)
+ # ----- Generator resume code
+ resume_code.putln("switch (%s->%s.resume_label) {" % (Naming.cur_scope_cname, Naming.obj_base_cname));
+ resume_code.putln("case 0: goto %s;" % first_run_label)
+
+ from ParseTreeTransforms import YieldNodeCollector
+ collector = YieldNodeCollector()
+ collector.visitchildren(self)
+ for yield_expr in collector.yields:
+ resume_code.putln("case %d: goto %s;" % (yield_expr.label_num, yield_expr.label_name));
+ resume_code.putln("default: /* CPython raises the right error here */");
+ resume_code.put_finish_refcount_context()
+ resume_code.putln("return NULL;");
+ resume_code.putln("}");
+
+ code.exit_cfunc_scope()
+
+
class OverrideCheckNode(StatNode):
# A Node for dispatching to the def method if it
# is overriden.
objstruct_name = None
typeobj_name = None
decorators = None
+ shadow = False
def analyse_declarations(self, env):
#print "CClassDefNode.analyse_declarations:", self.class_name
home_scope = env
if self.visibility == 'extern':
- if self.module_name == '__builtin__' and self.class_name in Builtin.builtin_types:
+ if (self.module_name == '__builtin__' and
+ self.class_name in Builtin.builtin_types and
+ env.qualified_name[:8] != 'cpython.'): # allow overloaded names for cimporting from cpython
warning(self.pos, "%s already a builtin Cython type" % self.class_name, 1)
self.entry = home_scope.declare_c_class(
visibility = self.visibility,
typedef_flag = self.typedef_flag,
api = self.api,
- buffer_defaults = buffer_defaults)
+ buffer_defaults = buffer_defaults,
+ shadow = self.shadow)
+ if self.shadow:
+ home_scope.lookup(self.class_name).as_variable = self.entry
if home_scope is not env and self.visibility == 'extern':
env.add_imported_entry(self.class_name, self.entry, self.pos)
self.scope = scope = self.entry.type.scope
self.expr.result_is_used = False # hint that .result() may safely be left empty
self.expr.analyse_expressions(env)
+ def nogil_check(self, env):
+ if self.expr.type.is_pyobject and self.expr.is_temp:
+ self.gil_error()
+
+ gil_message = "Discarding owned Python object"
+
def generate_execution_code(self, code):
self.expr.generate_evaluation_code(code)
if not self.expr.is_temp and self.expr.result():
if self.exc_tb:
self.exc_tb.analyse_types(env)
self.exc_tb = self.exc_tb.coerce_to_pyobject(env)
- env.use_utility_code(raise_utility_code)
+ # special cases for builtin exceptions
+ self.builtin_exc_name = None
+ if self.exc_type and not self.exc_value and not self.exc_tb:
+ exc = self.exc_type
+ import ExprNodes
+ if (isinstance(exc, ExprNodes.SimpleCallNode) and
+ not (exc.args or (exc.arg_tuple is not None and
+ exc.arg_tuple.args))):
+ exc = exc.function # extract the exception type
+ if exc.is_name and exc.entry.is_builtin:
+ self.builtin_exc_name = exc.name
+ if self.builtin_exc_name == 'MemoryError':
+ self.exc_type = None # has a separate implementation
nogil_check = Node.gil_error
gil_message = "Raising exception"
def generate_execution_code(self, code):
+ if self.builtin_exc_name == 'MemoryError':
+ code.putln('PyErr_NoMemory(); %s' % code.error_goto(self.pos))
+ return
+
if self.exc_type:
self.exc_type.generate_evaluation_code(code)
type_code = self.exc_type.py_result()
tb_code = self.exc_tb.py_result()
else:
tb_code = "0"
+ code.globalstate.use_utility_code(raise_utility_code)
code.putln(
"__Pyx_Raise(%s, %s, %s);" % (
type_code,
self.target.analyse_target_types(env)
self.iterator.analyse_expressions(env)
self.item = ExprNodes.NextNode(self.iterator, env)
- self.item = self.item.coerce_to(self.target.type, env)
+ if (self.iterator.type.is_ptr or self.iterator.type.is_array) and \
+ self.target.type.assignable_from(self.iterator.type):
+ # C array slice optimization.
+ pass
+ else:
+ self.item = self.item.coerce_to(self.target.type, env)
self.body.analyse_expressions(env)
if self.else_clause:
self.else_clause.analyse_expressions(env)
code.put_goto(try_end_label)
if code.label_used(try_return_label):
code.put_label(try_return_label)
- for var in exc_save_vars: code.put_xgiveref(var)
+ for var in exc_save_vars:
+ code.put_xgiveref(var)
code.putln("__Pyx_ExceptionReset(%s);" %
', '.join(exc_save_vars))
code.put_goto(old_return_label)
if error_label_used or not self.has_default_clause:
if error_label_used:
code.put_label(except_error_label)
- for var in exc_save_vars: code.put_xgiveref(var)
+ for var in exc_save_vars:
+ code.put_xgiveref(var)
code.putln("__Pyx_ExceptionReset(%s);" %
', '.join(exc_save_vars))
code.put_goto(old_error_label)
if code.label_used(exit_label):
code.put_label(exit_label)
- for var in exc_save_vars: code.put_xgiveref(var)
+ for var in exc_save_vars:
+ code.put_xgiveref(var)
code.putln("__Pyx_ExceptionReset(%s);" %
', '.join(exc_save_vars))
code.put_goto(old_label)
if code.label_used(except_end_label):
code.put_label(except_end_label)
- for var in exc_save_vars: code.put_xgiveref(var)
+ for var in exc_save_vars:
+ code.put_xgiveref(var)
code.putln("__Pyx_ExceptionReset(%s);" %
', '.join(exc_save_vars))
code.put_label(try_end_label)
code.putln("#endif")
else:
code.putln("#ifdef WITH_THREAD")
- code.putln("PyThreadState *_save;")
+ code.putln("PyThreadState *_save = NULL;")
code.putln("#endif")
code.putln("Py_UNBLOCK_THREADS")
TryFinallyStatNode.generate_execution_code(self, code)
#------------------------------------------------------------------------------------
+swap_exception_utility_code = UtilityCode(
+proto = """
+static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb); /*proto*/
+""",
+impl = """
+static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb) {
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ PyThreadState *tstate = PyThreadState_GET();
+
+ 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;
+
+ *type = tmp_type;
+ *value = tmp_value;
+ *tb = tmp_tb;
+}
+""")
+
+#------------------------------------------------------------------------------------
+
arg_type_test_utility_code = UtilityCode(
proto = """
static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed,
bad:
return -1;
}
-""")
+""",
+requires=[raise_double_keywords_utility_code])
#------------------------------------------------------------------------------------
get_vtable_utility_code = UtilityCode(
proto = """
-static int __Pyx_GetVtable(PyObject *dict, void *vtabptr); /*proto*/
+static void* __Pyx_GetVtable(PyObject *dict); /*proto*/
""",
impl = r"""
-static int __Pyx_GetVtable(PyObject *dict, void *vtabptr) {
+static void* __Pyx_GetVtable(PyObject *dict) {
+ void* ptr;
PyObject *ob = PyMapping_GetItemString(dict, (char *)"__pyx_vtable__");
if (!ob)
goto bad;
#if PY_VERSION_HEX >= 0x02070000 && !(PY_MAJOR_VERSION==3&&PY_MINOR_VERSION==0)
- *(void **)vtabptr = PyCapsule_GetPointer(ob, 0);
+ ptr = PyCapsule_GetPointer(ob, 0);
#else
- *(void **)vtabptr = PyCObject_AsVoidPtr(ob);
+ ptr = PyCObject_AsVoidPtr(ob);
#endif
- if (!*(void **)vtabptr)
- goto bad;
+ if (!ptr && !PyErr_Occurred())
+ PyErr_SetString(PyExc_RuntimeError, "invalid vtable found for imported type");
Py_DECREF(ob);
- return 0;
+ return ptr;
bad:
Py_XDECREF(ob);
- return -1;
+ return NULL;
}
""")