# # Code generation for wrapping cypclass as a Python Extension Type # # Will be generated: # - a PyTypeObject definition for each user defined cypclass # - Python wrappers for cypclass methods # - Python getters/setters for cypclass attributes # - Specific 'tp slots' for handling cycplass objects from Python: # . tp_new # . tp_init # . tp_dealloc # ... # # Functions defined here will be called from ModuleNode.py # # Reasons for using a separate file: # - avoid cluttering ModuleNode.py # - regroup common logic # - decouple the code generation process from that of 'cdef class' # # Code generation for cypclass will be similar to code generation for 'cdef class' in ModuleNode.py, # but differences are significant enough that it is better to introduce some redundancy than try to # handle both 'cdef class' and 'cypclass' in ModuleNode.py. # from __future__ import absolute_import import cython cython.declare(Naming=object, Options=object, PyrexTypes=object, TypeSlots=object, error=object, warning=object, py_object_type=object, cy_object_type=object, UtilityCode=object, EncodedString=object, re=object) from collections import defaultdict import json import operator import os import re from .PyrexTypes import CPtrType from . import Future from . import Annotate from . import Code from . import CypclassWrapper from . import Naming from . import Nodes from . import Options from . import TypeSlots from . import PyrexTypes from . import Pythran from .Errors import error, warning from .PyrexTypes import py_object_type, cy_object_type from ..Utils import open_new_file, replace_suffix, decode_filename, build_hex_version from .Code import UtilityCode, IncludeCode from .StringEncoding import EncodedString from .Pythran import has_np_pythran def cypclass_iter(scope): """ Recursively iterate over nested cypclasses """ for entry in scope.cypclass_entries: yield entry cypclass_scope = entry.type.scope if cypclass_scope: for e in cypclass_iter(cypclass_scope): yield e def cypclass_iter_scopes(scope): """ Recursively iterate over nested cypclasses and their associated scope """ for entry in scope.cypclass_entries: cypclass_scope = entry.type.scope yield entry, cypclass_scope if cypclass_scope: for e, s in cypclass_iter_scopes(cypclass_scope): yield e, s def generate_cypclass_typeobj_declarations(env, code, definition): """ Generate declarations of global pointers to the PyTypeObject for each cypclass """ for entry, scope in cypclass_iter_scopes(env): if definition or entry.defined_in_pxd: # the entry regrouping the constructors for this cypclass c = scope.lookup_here("<constructor>") # Todo: determine whether the __new__ called in the constructor # actually returns an instance of the cypclass type. # and do this computation only once # (cf generate_cyp_class_wrapper_definition) # # n = scope.lookup_here("<new>") # # whether a user-defined __new__ actually returns another type # is_new_return_type = not n or (n.type.return_type == entry.type) # if a constructor is unused it will not be defined, and if none are # defined the PyTypeObject pointer is never assigned to an instance, # so no need to declare it. if entry.type.templates or any(map(lambda e: e.used, c.all_alternatives())): code.putln("static PyTypeObject *%s = 0;" % (entry.type.typeptr_cname)) # # Cypclass generation, originally authored by Gwenaƫl Samain, moved here from ModuleNode.py # def generate_cyp_class_deferred_definitions(env, code, definition): """ Generate all cypclass method definitions, deferred till now """ for entry, scope in cypclass_iter_scopes(env): if definition or entry.defined_in_pxd: if entry.type.activable: # Generate acthon-specific classes generate_cyp_class_reifying_entries(entry, code) generate_cyp_class_activated_class(entry, code) generate_cyp_class_activate_function(entry, code) # Generate cypclass attr destructor generate_cyp_class_attrs_destructor_definition(entry, code) # Generate wrapper constructor wrapper = scope.lookup_here("<constructor>") constructor = scope.lookup_here("<init>") new = scope.lookup_here("__new__") alloc = scope.lookup_here("<alloc>") for wrapper_entry in wrapper.all_alternatives(): if wrapper_entry.used or entry.type.templates: generate_cyp_class_wrapper_definition(entry.type, wrapper_entry, constructor, new, alloc, code) def generate_cyp_class_attrs_destructor_definition(entry, code): """ Generate destructor definition for the given cypclass entry """ scope = entry.type.scope cypclass_attrs = [e for e in scope.var_entries if e.type.is_cyp_class and not e.name == "this" and not e.is_type] if cypclass_attrs: cypclass_attrs_destructor_name = "%s__cypclass_attrs_destructor__%s" % (Naming.func_prefix, entry.name) destructor_with_namespace = "void %s::%s()" % (entry.type.empty_declaration_code(), cypclass_attrs_destructor_name) code.putln(destructor_with_namespace) code.putln("{") for attr in cypclass_attrs: code.putln("Cy_XDECREF(this->%s);" % attr.cname) code.putln("}") def generate_cyp_class_activate_function(entry, code): """ Generate activate function for activable cypclass entries """ active_self_entry = entry.type.scope.lookup_here("<active_self>") dunder_activate_entry = entry.type.scope.lookup_here("__activate__") # Here we generate the function header like Nodes.CFuncDefNode would do, # but we streamline the process because we know the exact prototype. dunder_activate_arg = dunder_activate_entry.type.op_arg_struct.declaration_code(Naming.optional_args_cname) dunder_activate_entity = dunder_activate_entry.type.function_header_code(dunder_activate_entry.func_cname, dunder_activate_arg) dunder_activate_header = dunder_activate_entry.type.return_type.declaration_code(dunder_activate_entity) code.putln("%s {" % dunder_activate_header) code.putln("%s;" % dunder_activate_entry.type.return_type.declaration_code("activated_instance")) code.putln('if (%s) {' % Naming.optional_args_cname) activated_class_constructor_optargs_list = ["this"] activated_class_constructor_defaultargs_list = ["this->_active_queue_class", "this->_active_result_class"] for i, arg in enumerate(dunder_activate_entry.type.args): code.putln("if (%s->%sn <= %s) {" % (Naming.optional_args_cname, Naming.pyrex_prefix, i)) code.putln("activated_instance = new %s::Activated(%s);" % (entry.type.empty_declaration_code(), ", ".join(activated_class_constructor_optargs_list + activated_class_constructor_defaultargs_list[i:]))) code.putln("} else {") activated_class_constructor_optargs_list.append("%s->%s" % (Naming.optional_args_cname, dunder_activate_entry.type.opt_arg_cname(arg.name))) # We're in the final else clause, corresponding to all optional arguments specified) code.putln("activated_instance = new %s::Activated(%s);" % (entry.type.empty_declaration_code(), ", ".join(activated_class_constructor_optargs_list))) for _ in dunder_activate_entry.type.args: code.putln("}") code.putln("}") code.putln("else {") code.putln("if (this->%s == NULL) {" % active_self_entry.cname) code.putln("this->%s = new %s::Activated(this, %s);" % (active_self_entry.cname, entry.type.empty_declaration_code(), ", ".join(activated_class_constructor_defaultargs_list)) ) code.putln("}") code.putln("Cy_INCREF(this->%s);" % active_self_entry.cname) code.putln("activated_instance = this->%s;" % active_self_entry.cname) code.putln("}") code.putln("return activated_instance;") code.putln("}") def generate_cyp_class_activated_class(entry, code): """ Generate activated class """ from . import Builtin sync_interface_type = Builtin.acthon_sync_type result_interface_type = Builtin.acthon_result_type queue_interface_type = Builtin.acthon_queue_type result_attr_cname = "_active_result_class" queue_attr_cname = "_active_queue_class" passive_self_attr_cname = Naming.builtin_prefix + entry.type.empty_declaration_code().replace('::', '__') + "_passive_self" activable_bases_cnames = [base.cname for base in entry.type.base_classes if base.activable] activable_bases_inheritance_list = ["public %s::Activated" % cname for cname in activable_bases_cnames] if activable_bases_cnames: base_classes_code = ", ".join(activable_bases_inheritance_list) initialize_code = ", ".join([ "%s::Activated(passive_object, active_queue, active_result_constructor)" % cname for cname in activable_bases_cnames ]) else: base_classes_code = "public ActhonActivableClass" initialize_code = "ActhonActivableClass(active_queue, active_result_constructor)" code.putln("struct %s::Activated : %s {" % (entry.type.empty_declaration_code(), base_classes_code)) code.putln("%s;" % entry.type.declaration_code(passive_self_attr_cname)) code.putln(("Activated(%s * passive_object, %s, %s)" ": %s, %s(passive_object){} // Used by _passive_self.__activate__()" % ( entry.type.empty_declaration_code(), queue_interface_type.declaration_code("active_queue"), entry.type.scope.lookup_here("__activate__").type.args[1].type.declaration_code("active_result_constructor"), initialize_code, passive_self_attr_cname ) )) for reifying_class_entry in entry.type.scope.reifying_entries: reified_function_entry = reifying_class_entry.reified_entry code.putln("// generating reified of %s" % reified_function_entry.name) reified_arg_cname_list = [] reified_arg_decl_list = [] for i in range(len(reified_function_entry.type.args)-reified_function_entry.type.optional_arg_count): arg = reified_function_entry.type.args[i] reified_arg_cname_list.append(arg.cname) reified_arg_decl_list.append(arg.type.declaration_code(arg.cname)) if reified_function_entry.type.optional_arg_count: opt_cname = Naming.optional_args_cname reified_arg_cname_list.append(opt_cname) reified_arg_decl_list.append(reified_function_entry.type.op_arg_struct.declaration_code(opt_cname)) activated_method_arg_decl_code = ", ".join([sync_interface_type.declaration_code("sync_object")] + reified_arg_decl_list) function_header = reified_function_entry.type.function_header_code(reified_function_entry.cname, activated_method_arg_decl_code) function_code = result_interface_type.declaration_code(function_header) code.putln("%s {" % function_code) code.putln("%s = this->%s();" % (result_interface_type.declaration_code("result_object"), result_attr_cname)) message_constructor_args_list = ["this->%s" % passive_self_attr_cname, "sync_object", "result_object"] + reified_arg_cname_list message_constructor_args_code = ", ".join(message_constructor_args_list) code.putln("%s = new %s(%s);" % ( reifying_class_entry.type.declaration_code("message"), reifying_class_entry.type.empty_declaration_code(), message_constructor_args_code )) code.putln("/* Push message in the queue */") code.putln("if (this->%s != NULL) {" % queue_attr_cname) code.putln("Cy_WLOCK(%s);" % queue_attr_cname) code.putln("this->%s->push(message);" % queue_attr_cname) code.putln("Cy_UNLOCK(%s);" % queue_attr_cname) code.putln("} else {") code.putln("/* We should definitely shout here */") code.putln('fprintf(stderr, "Acthon error: No queue to push to for %s remote call !\\n");' % reified_function_entry.name) code.putln("}") code.putln("Cy_DECREF(message);") code.putln("/* Return result object */") code.putln("return result_object;") code.putln("}") code.putln("};") def generate_cyp_class_reifying_entries(entry, code): """ Generate code to reify the cypclass entry ?? TODO """ target_object_type = entry.type target_object_cname = Naming.builtin_prefix + "target_object" target_object_code = target_object_type.declaration_code(target_object_cname) sync_arg_name = "sync_method" result_arg_name = "result_object" from . import Builtin message_base_type = Builtin.acthon_message_type sync_type = Builtin.acthon_sync_type result_type = Builtin.acthon_result_type sync_attr_cname = message_base_type.scope.lookup_here("_sync_method").cname result_attr_cname = message_base_type.scope.lookup_here("_result").cname def put_cypclass_op_on_narg_optarg(op_lbda, func_type, opt_arg_name, code): opt_arg_count = func_type.optional_arg_count narg_count = len(func_type.args) - opt_arg_count for narg in func_type.args[:narg_count]: if narg.type.is_cyp_class: code.putln("%s(this->%s);" % (op_lbda(narg), narg.cname)) if opt_arg_count: opt_arg_guard = code.insertion_point() code.increase_indent() num_if = 0 for opt_idx, optarg in enumerate(func_type.args[narg_count:]): if optarg.type.is_cyp_class: code.putln("if (this->%s->%sn > %s) {" % (opt_arg_name, Naming.pyrex_prefix, opt_idx )) code.putln("%s(this->%s->%s);" % (op_lbda(optarg), opt_arg_name, func_type.opt_arg_cname(optarg.name) )) num_if += 1 for _ in range(num_if): code.putln("}") if num_if: opt_arg_guard.putln("if (this->%s != NULL) {" % opt_arg_name) code.putln("}") else: code.decrease_indent() for reifying_class_entry in entry.type.scope.reifying_entries: reified_function_entry = reifying_class_entry.reified_entry reifying_class_full_name = reifying_class_entry.type.empty_declaration_code() class_name = reifying_class_full_name.split('::')[-1] code.putln("struct %s : public %s {" % (reifying_class_full_name, message_base_type.empty_declaration_code())) # Declaring target object & reified method arguments code.putln("%s;" % target_object_code) constructor_args_decl_list = [ target_object_code, sync_type.declaration_code(sync_arg_name), result_type.declaration_code(result_arg_name) ] initialized_args_list = [target_object_cname] opt_arg_count = reified_function_entry.type.optional_arg_count for i in range(len(reified_function_entry.type.args) - opt_arg_count): arg = reified_function_entry.type.args[i] arg_cname_code = arg.type.declaration_code(arg.cname) code.putln("%s;" % arg_cname_code) constructor_args_decl_list.append(arg_cname_code) initialized_args_list.append(arg.cname) if opt_arg_count: # We cannot initialize the struct before allocating memory, so # it must be handled in constructor body, not initializer list opt_decl_code = reified_function_entry.type.op_arg_struct.declaration_code(Naming.optional_args_cname) code.putln("%s;" % opt_decl_code) constructor_args_decl_list.append(opt_decl_code) # Putting them into constructor constructor_args_decl_code = ", ".join(constructor_args_decl_list) initializer_list = ["%s(%s)" % (name, name) for name in initialized_args_list] initializer_list_code = ", ".join(initializer_list) code.putln("%s(%s) : %s(%s, %s), %s {" % ( class_name, constructor_args_decl_code, message_base_type.empty_declaration_code(), sync_arg_name, result_arg_name, initializer_list_code )) if opt_arg_count: mem_size = "sizeof(%s)" % reified_function_entry.type.op_arg_struct.base_type.empty_declaration_code() code.putln("if (%s != NULL) {" % Naming.optional_args_cname) code.putln("this->%s = (%s) malloc(%s);" % ( Naming.optional_args_cname, reified_function_entry.type.op_arg_struct.empty_declaration_code(), mem_size )) code.putln("memcpy(this->%s, %s, %s);" % ( Naming.optional_args_cname, Naming.optional_args_cname, mem_size )) code.putln("} else {") code.putln("this->%s = NULL;" % Naming.optional_args_cname) code.putln("}") # Acquire a ref on CyObject, as we don't know when the message will be processed put_cypclass_op_on_narg_optarg(lambda _: "Cy_INCREF", reified_function_entry.type, Naming.optional_args_cname, code) code.putln("Cy_INCREF(this->%s);" % target_object_cname) code.putln("}") code.putln("int activate() {") sync_result = "sync_result" code.putln("int %s = 0;" % sync_result) code.putln("/* Activate only if its sync object agrees to do so */") code.putln("if (this->%s != NULL) {" % sync_attr_cname) code.putln("if (!Cy_TRYRLOCK(this->%s)) {" % sync_attr_cname) code.putln("%s = this->%s->isActivable();" % (sync_result, sync_attr_cname)) code.putln("Cy_UNLOCK(this->%s);" % sync_attr_cname) code.putln("}") code.putln("if (%s == 0) return 0;" % sync_result) code.putln("}") result_assignment = "" # Drop the target_object argument to perform the actual method call reified_call_args_list = initialized_args_list[1:] if opt_arg_count: reified_call_args_list.append(Naming.optional_args_cname) # Locking CyObjects # Here we completely ignore the lock mode (nolock/checklock/autolock) # because the mode is used for direct calls, when the user have the possibility # to manually lock or let the compiler handle it. # Here, the user cannot lock manually, so we're taking the lock automatically. #put_cypclass_op_on_narg_optarg(lambda arg: "Cy_RLOCK" if arg.type.is_const else "Cy_WLOCK", # reified_function_entry.type, Naming.optional_args_cname, code) func_type = reified_function_entry.type opt_arg_name = Naming.optional_args_cname trylock_result = "trylock_result" failed_trylock = "failed_trylock" code.putln("int %s = 0;" % trylock_result) code.putln("int %s = 0;" % failed_trylock) opt_arg_count = func_type.optional_arg_count narg_count = len(func_type.args) - opt_arg_count num_trylock = 1 op = "Cy_TRYRLOCK" if reified_function_entry.type.is_const_method else "Cy_TRYWLOCK" code.putln("%s = %s(this->%s) != 0;" % (failed_trylock, op, target_object_cname)) code.putln("if (!%s) {" % failed_trylock) code.putln("++%s;" % trylock_result) for i, narg in enumerate(func_type.args[:narg_count]): if narg.type.is_cyp_class: try_op = "Cy_TRYRLOCK" if narg.type.is_const else "Cy_TRYWLOCK" code.putln("%s = %s(this->%s) != 0;" % (failed_trylock, try_op, narg.cname)) code.putln("if (!%s) {" % failed_trylock) code.putln("++%s;" % trylock_result) num_trylock += 1 num_optional_if = 0 if opt_arg_count: opt_arg_guard = code.insertion_point() code.increase_indent() for opt_idx, optarg in enumerate(func_type.args[narg_count:]): if optarg.type.is_cyp_class: try_op = "Cy_TRYRLOCK" if optarg.type.is_const else "Cy_TRYWLOCK" code.putln("if (this->%s->%sn > %s) {" % (opt_arg_name, Naming.pyrex_prefix, opt_idx, )) code.putln("%s = %s(this->%s->%s) != 0;" % ( failed_trylock, try_op, opt_arg_name, func_type.opt_arg_cname(optarg.name) )) code.putln("if (!%s) {" % failed_trylock) code.putln("++%s;" % trylock_result) num_optional_if += 1 num_trylock += 1 for _ in range(num_optional_if): code.putln("}") if num_optional_if > 0: opt_arg_guard.putln("if (this->%s != NULL) {" % opt_arg_name) code.putln("}") # The check for optional_args != NULL else: code.decrease_indent() for _ in range(num_trylock): code.putln("}") if num_trylock: # If there is any lock failure, we unlock all and return 0 code.putln("if (%s) {" % failed_trylock) num_unlock = 0 # Target object first, then arguments code.putln("if (%s > %s) {" % (trylock_result, num_unlock)) code.putln("Cy_UNLOCK(this->%s);" % target_object_cname) num_unlock += 1 for i, narg in enumerate(func_type.args[:narg_count]): if narg.type.is_cyp_class: code.putln("if (%s > %s) {" % (trylock_result, num_unlock)) code.putln("Cy_UNLOCK(this->%s);" % narg.cname) num_unlock += 1 if opt_arg_count and num_optional_if: code.putln("if (this->%s != NULL) {" % opt_arg_name) for opt_idx, optarg in enumerate(func_type.args[narg_count:]): if optarg.type.is_cyp_class: code.putln("if (%s > %s) {" % (trylock_result, num_unlock)) code.putln("Cy_UNLOCK(this->%s->%s);" % (opt_arg_name, func_type.opt_arg_cname(optarg.name))) num_unlock += 1 # Note: we do not respect the semantic order of end-blocks here for simplification purpose. # This one is for the "not NULL opt arg" check code.putln("}") # These ones are all the checks for mandatory and optional arguments for _ in range(num_unlock): code.putln("}") code.putln("return 0;") code.putln("}") does_return = reified_function_entry.type.return_type is not PyrexTypes.c_void_type if does_return: result_assignment = "%s = " % reified_function_entry.type.return_type.declaration_code("result") code.putln("%sthis->%s->%s(%s);" % ( result_assignment, target_object_cname, reified_function_entry.cname, ", ".join("this->%s" % arg_cname for arg_cname in reified_call_args_list) ) ) code.putln("Cy_UNLOCK(this->%s);" % target_object_cname) put_cypclass_op_on_narg_optarg(lambda _: "Cy_UNLOCK", reified_function_entry.type, Naming.optional_args_cname, code) code.putln("/* Push result in the result object */") if does_return: code.putln("Cy_WLOCK(this->%s);" % result_attr_cname) if reified_function_entry.type.return_type is PyrexTypes.c_int_type: code.putln("this->%s->pushIntResult(result);" % result_attr_cname) else: code.putln("this->%s->pushVoidStarResult((void*)result);" % result_attr_cname) code.putln("Cy_UNLOCK(this->%s);" % result_attr_cname) code.putln("return 1;") code.putln("}") # Destructor code.putln("virtual ~%s() {" % class_name) code.putln("Cy_DECREF(this->%s);" % target_object_cname) put_cypclass_op_on_narg_optarg(lambda _: "Cy_DECREF", reified_function_entry.type, Naming.optional_args_cname, code) if opt_arg_count: code.putln("free(this->%s);" % Naming.optional_args_cname) code.putln("}") code.putln("};") def generate_cyp_class_wrapper_definition(type, wrapper_entry, constructor_entry, new_entry, alloc_entry, code): """ Generate cypclass constructor wrapper ?? TODO """ if type.templates: code.putln("template <typename %s>" % ", class ".join( [T.empty_declaration_code() for T in type.templates])) init_entry = constructor_entry self_type = wrapper_entry.type.return_type.declaration_code('') type_string = type.empty_declaration_code() class_name = type.name wrapper_cname = "%s::%s__constructor__%s" % (type_string, Naming.func_prefix, class_name) wrapper_type = wrapper_entry.type arg_decls = [] arg_names = [] for arg in wrapper_type.args[:len(wrapper_type.args)-wrapper_type.optional_arg_count]: arg_decl = arg.declaration_code() arg_decls.append(arg_decl) arg_names.append(arg.cname) if wrapper_type.optional_arg_count: arg_decls.append(wrapper_type.op_arg_struct.declaration_code(Naming.optional_args_cname)) arg_names.append(Naming.optional_args_cname) if wrapper_type.has_varargs: # We can't safely handle varargs because we need # to know where the size argument is to start a va_list error(wrapper_entry.pos, "Cypclass cannot handle variable arguments constructors, but you can use optional arguments (arg=some_value)") if not arg_decls: arg_decls = ["void"] decl_arg_string = ', '.join(arg_decls) code.putln("%s %s(%s)" % (self_type, wrapper_cname, decl_arg_string)) code.putln("{") wrapper_arg_types = [arg.type for arg in wrapper_entry.type.args] pos = wrapper_entry.pos or type.entry.pos if new_entry: alloc_type = alloc_entry.type new_arg_types = [alloc_type] + wrapper_arg_types new_entry = PyrexTypes.best_match(new_arg_types, new_entry.all_alternatives(), pos) if new_entry: alloc_call_string = "(" + new_entry.type.original_alloc_type.type.declaration_code("") + ") %s" % alloc_entry.func_cname new_arg_names = [alloc_call_string] + arg_names new_arg_string = ', '.join(new_arg_names) code.putln("%s self =(%s) %s(%s);" % (self_type, self_type, new_entry.func_cname, new_arg_string)) else: code.putln("%s self = %s();" % (self_type, alloc_entry.func_cname)) # __new__ can be defined by user and return another type is_new_return_type = not new_entry or new_entry.type.return_type == type # initialise PyObject fields if (is_new_return_type): code.putln("if(self) {") code.putln("self->ob_refcnt = 0;") # code.putln("self->ob_type = NULL;") code.putln("self->ob_type = %s;" % type.typeptr_cname) code.putln("}") if init_entry: init_entry = PyrexTypes.best_match(wrapper_arg_types, init_entry.all_alternatives(), None) if init_entry and (is_new_return_type): # Calling __init__ max_init_nargs = len(init_entry.type.args) min_init_nargs = max_init_nargs - init_entry.type.optional_arg_count max_wrapper_nargs = len(wrapper_entry.type.args) min_wrapper_nargs = max_wrapper_nargs - wrapper_entry.type.optional_arg_count if min_init_nargs == min_wrapper_nargs: # The optional arguments begin at the same rank for both function # => just pass the wrapper opt args structure, and everything will be fine. if max_wrapper_nargs > min_wrapper_nargs: # The wrapper has optional args arg_names[-1] = "(%s) %s" % (init_entry.type.op_arg_struct.declaration_code(''), arg_names[-1]) elif max_init_nargs > min_init_nargs: # The wrapper has no optional args but the __init__ function does arg_names.append("(%s) NULL" % init_entry.type.op_arg_struct.declaration_code('')) # else, neither __init__ nor __new__ have optional arguments, nothing to do elif min_wrapper_nargs < min_init_nargs: # It means some args from the wrapper should be at # their default values, which we cannot know from here, # so shout and stop, sadly. error(init_entry.pos, "Could not call this __init__ function because the corresponding __new__ wrapper isn't aware of default values") error(wrapper_entry.pos, "Wrapped __new__ is here (some args passed to __init__ could be at their default values)") elif min_wrapper_nargs > min_init_nargs: # Here, the __init__ optional arguments start before # the __new__ ones. We have to unpack the __new__ opt args struct # in some variables and then repack in the __init__ opt args struct. init_opt_args_name_list = [arg.cname for arg in wrapper_entry.type.args[min_init_nargs:]] # The first __init__ optional arguments are mandatory # in the __new__ signature, so they will always appear # in the __init__ optional arguments structure init_opt_args_number = "init_opt_n" code.putln("int %s = %s;" % (init_opt_args_number, min_wrapper_nargs - min_init_nargs)) if wrapper_entry.type.optional_arg_count: for i, arg in enumerate(wrapper_entry.type.args[min_wrapper_nargs:]): # It's an opt arg => it's not declared in the (c++) function scope => declare a variable for it arg_name = arg.cname code.putln("%s;" % arg.type.declaration_code(arg_name)) # Arguments unpacking optional_struct_name = arg_names.pop() code.putln("if (%s) {" % optional_struct_name) # This is necessary to keep __init__ informed of # how many optional arguments were explicitely given code.putln("%s += %s->%sn;" % (init_opt_args_number, optional_struct_name, Naming.pyrex_prefix)) braces_number = 1 + max_wrapper_nargs - min_wrapper_nargs for i, arg in enumerate(wrapper_entry.type.args[min_wrapper_nargs:]): code.putln("if(%s->%sn > %s) {" % (optional_struct_name, Naming.pyrex_prefix, i)) code.putln("%s = %s->%s;" % ( arg.cname, optional_struct_name, wrapper_entry.type.op_arg_struct.base_type.scope.var_entries[i+1].cname )) for _ in range(braces_number): code.putln('}') # Arguments packing init_opt_args_struct_name = "init_opt_args" code.putln("%s;" % init_entry.type.op_arg_struct.base_type.declaration_code(init_opt_args_struct_name)) code.putln("%s.%sn = %s;" % (init_opt_args_struct_name, Naming.pyrex_prefix, init_opt_args_number)) for i, arg_name in enumerate(init_opt_args_name_list): # The second tuple member is a bit tricky. # Actually, the only way we have to precisely know the attribute cname # which corresponds to the argument in the opt args struct # is to rely on the declaration order in the struct scope. # FuncDefNode doesn't do this because it has it's declarator node, # which is not our case here. code.putln("%s.%s = %s;" % ( init_opt_args_struct_name, init_entry.type.opt_arg_cname(init_entry.type.args[min_init_nargs+i].name), arg_name )) arg_names = arg_names[:min_init_nargs] + ["&"+init_opt_args_struct_name] init_arg_string = ','.join(arg_names) code.putln("self->%s(%s);" % (init_entry.cname, init_arg_string)) code.putln("return self;") code.putln("}")