Browse files

Merge pull request #71 from vitek/_dynamic_args_T674

Support for dynamic default arguments, fix #674
  • Loading branch information...
2 parents 987e27c + 52981fe commit 8c0f27d039c44d8dab9e0882145b22fcc27d1e08 @vitek vitek committed Dec 22, 2011
View
52 Cython/Compiler/ExprNodes.py
@@ -5924,6 +5924,9 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
code_object = None
binding = False
def_node = None
+ defaults = None
+ defaults_struct = None
+ defaults_pyobjects = 0
type = py_object_type
is_temp = 1
@@ -5939,10 +5942,48 @@ def analyse_types(self, env):
env.use_utility_code(fused_function_utility_code)
else:
env.use_utility_code(binding_cfunc_utility_code)
+ self.analyse_default_args(env)
#TODO(craig,haoyu) This should be moved to a better place
self.set_mod_name(env)
+ def analyse_default_args(self, env):
+ """
+ Handle non-literal function's default arguments.
+ """
+ nonliteral_objects = []
+ nonliteral_other = []
+ for arg in self.def_node.args:
+ if arg.default and not arg.default.is_literal:
+ arg.is_dynamic = True
+ if arg.type.is_pyobject:
+ nonliteral_objects.append(arg)
+ else:
+ nonliteral_other.append(arg)
+ if nonliteral_objects or nonliteral_objects:
+ module_scope = env.global_scope()
+ cname = module_scope.next_id(Naming.defaults_struct_prefix)
+ scope = Symtab.StructOrUnionScope(cname)
+ self.defaults = []
+ for arg in nonliteral_objects:
+ entry = scope.declare_var(arg.name, arg.type, None,
+ Naming.arg_prefix + arg.name,
+ allow_pyobject=True)
+ self.defaults.append((arg, entry))
+ for arg in nonliteral_other:
+ entry = scope.declare_var(arg.name, arg.type, None,
+ Naming.arg_prefix + arg.name,
+ allow_pyobject=False)
+ self.defaults.append((arg, entry))
+ entry = module_scope.declare_struct_or_union(
+ None, 'struct', scope, 1, None, cname=cname)
+ self.defaults_struct = scope
+ self.defaults_pyobjects = len(nonliteral_objects)
+ for arg, entry in self.defaults:
+ arg.default_value = '%s->%s' % (
+ Naming.dynamic_args_cname, entry.cname)
+ self.def_node.defaults_struct = self.defaults_struct.name
+
def may_be_none(self):
return False
@@ -6024,6 +6065,17 @@ def generate_cyfunction_code(self, code):
self.result()))
code.put_giveref(self.py_result())
+ if self.defaults:
+ code.putln(
+ 'if (!__Pyx_CyFunction_InitDefaults(%s, sizeof(%s), %d)) %s' % (
+ self.result(), self.defaults_struct.name,
+ self.defaults_pyobjects, code.error_goto(self.pos)))
+ defaults = '__Pyx_CyFunction_Defaults(%s, %s)' % (
+ self.defaults_struct.name, self.result())
+ for arg, entry in self.defaults:
+ arg.generate_assignment_code(code, target='%s->%s' % (
+ defaults, entry.cname))
+
if self.specialized_cpdefs:
self.generate_fused_cpdef(code, code_object_result, flags)
View
2 Cython/Compiler/Naming.py
@@ -49,6 +49,8 @@
closure_class_prefix = pyrex_prefix + "scope_struct_"
lambda_func_prefix = pyrex_prefix + "lambda_"
module_is_main = pyrex_prefix + "module_is_main_"
+defaults_struct_prefix = pyrex_prefix + "defaults"
+dynamic_args_cname = pyrex_prefix + "dynamic_args"
args_cname = pyrex_prefix + "args"
generator_cname = pyrex_prefix + "generator"
View
39 Cython/Compiler/Nodes.py
@@ -677,6 +677,7 @@ class CArgDeclNode(Node):
# is_self_arg boolean Is the "self" arg of an extension type method
# is_type_arg boolean Is the "class" arg of an extension type classmethod
# is_kw_only boolean Is a keyword-only argument
+ # is_dynamic boolean Non-literal arg stored inside CyFunction
child_attrs = ["base_type", "declarator", "default"]
@@ -690,6 +691,7 @@ class CArgDeclNode(Node):
name_declarator = None
default_value = None
annotation = None
+ is_dynamic = 0
def analyse(self, env, nonempty = 0, is_self_arg = False):
if is_self_arg:
@@ -738,6 +740,21 @@ def annotate(self, code):
if self.default:
self.default.annotate(code)
+ def generate_assignment_code(self, code, target=None):
+ default = self.default
+ if default is None or default.is_literal:
+ return
+ if target is None:
+ target = self.calculate_default_value_code(code)
+ default.generate_evaluation_code(code)
+ default.make_owned_reference(code)
+ result = default.result_as(self.type)
+ code.putln("%s = %s;" % (target, result))
+ if self.type.is_pyobject:
+ code.put_giveref(default.result())
+ default.generate_post_assignment_code(code)
+ default.free_temps(code)
+
class CBaseTypeNode(Node):
# Abstract base class for C base type nodes.
@@ -1801,20 +1818,8 @@ def generate_wrapper_functions(self, code):
def generate_execution_code(self, code):
# Evaluate and store argument default values
for arg in self.args:
- default = arg.default
- if default:
- if not default.is_literal:
- default.generate_evaluation_code(code)
- default.make_owned_reference(code)
- result = default.result_as(arg.type)
- code.putln(
- "%s = %s;" % (
- arg.calculate_default_value_code(code),
- result))
- if arg.type.is_pyobject:
- code.put_giveref(default.result())
- default.generate_post_assignment_code(code)
- default.free_temps(code)
+ if not arg.is_dynamic:
+ arg.generate_assignment_code(code)
# For Python class methods, create and store function object
if self.assmt:
self.assmt.generate_execution_code(code)
@@ -2653,6 +2658,7 @@ class DefNode(FuncDefNode):
self_in_stararg = 0
py_cfunc_node = None
requires_classobj = False
+ defaults_struct = None # Dynamic kwrds structure name
doc = None
fused_py_func = False
@@ -3521,6 +3527,11 @@ def generate_argument_values_setup_code(self, args, max_positional_args, argtupl
code.putln("PyObject* values[%d] = {%s};" % (
max_args, ','.join('0'*max_args)))
+ if self.defaults_struct:
+ code.putln('%s *%s = __Pyx_CyFunction_Defaults(%s, %s);' % (
+ self.defaults_struct, Naming.dynamic_args_cname,
+ self.defaults_struct, Naming.self_cname))
+
# assign borrowed Python default values to the values array,
# so that they can be overwritten by received arguments below
for i, arg in enumerate(args):
View
47 Cython/Utility/CythonFunction.c
@@ -13,6 +13,9 @@
#define __Pyx_CyFunction_GetClassObj(f) \
(((__pyx_CyFunctionObject *) (f))->func_classobj)
+#define __Pyx_CyFunction_Defaults(type, f) \
+ ((type *)(((__pyx_CyFunctionObject *) (f))->defaults))
+
typedef struct {
PyCFunctionObject func;
@@ -24,6 +27,10 @@ typedef struct {
PyObject *func_code;
PyObject *func_closure;
PyObject *func_classobj; /* No-args super() class cell */
+
+ /* Dynamic default args*/
+ void *defaults;
+ int defaults_pyobjects;
} __pyx_CyFunctionObject;
static PyTypeObject *__pyx_CyFunctionType = 0;
@@ -36,6 +43,11 @@ static PyObject *__Pyx_CyFunction_New(PyTypeObject *,
PyObject *self, PyObject *module,
PyObject* code);
+static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *m,
+ size_t size,
+ int pyobjects);
+
+
static int __Pyx_CyFunction_init(void);
//////////////////// CythonFunction ////////////////////
@@ -246,6 +258,9 @@ static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int f
op->func_classobj = NULL;
Py_XINCREF(code);
op->func_code = code;
+ /* Dynamic Default args */
+ op->defaults_pyobjects = 0;
+ op->defaults = NULL;
PyObject_GC_Track(op);
return (PyObject *) op;
}
@@ -260,6 +275,18 @@ __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m)
Py_CLEAR(m->func_doc);
Py_CLEAR(m->func_code);
Py_CLEAR(m->func_classobj);
+
+ if (m->defaults) {
+ PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m);
+ int i;
+
+ for (i = 0; i < m->defaults_pyobjects; i++)
+ Py_XDECREF(pydefaults[i]);
+
+ PyMem_Free(m->defaults);
+ m->defaults = NULL;
+ }
+
return 0;
}
@@ -281,6 +308,15 @@ static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit,
Py_VISIT(m->func_doc);
Py_VISIT(m->func_code);
Py_VISIT(m->func_classobj);
+
+ if (m->defaults) {
+ PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m);
+ int i;
+
+ for (i = 0; i < m->defaults_pyobjects; i++)
+ Py_VISIT(pydefaults[i]);
+ }
+
return 0;
}
@@ -384,6 +420,17 @@ static int __Pyx_CyFunction_init(void)
return 0;
}
+void *__Pyx_CyFunction_InitDefaults(PyObject *func, size_t size, int pyobjects)
+{
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+
+ m->defaults = PyMem_Malloc(size);
+ if (!m->defaults)
+ return PyErr_NoMemory();
+ memset(m->defaults, 0, sizeof(size));
+ m->defaults_pyobjects = pyobjects;
+ return m->defaults;
+}
//////////////////// CyFunctionClassCell.proto ////////////////////
static CYTHON_INLINE void __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions,
PyObject *classobj);
View
1 tests/bugs.txt
@@ -18,7 +18,6 @@ temp_sideeffects_T654
class_scope_T671
slice2_T636
builtin_subtype_methods_T653
-default_args_T674
# CPython regression tests that don't current work:
pyregr.test_threadsignals
View
24 tests/run/dynamic_args.pyx
@@ -0,0 +1,24 @@
+# mode: run
+# ticket: 674
+
+cdef class Foo:
+ cdef str name
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return '<%s>' % self.name
+
+def test_exttype_args(a, b, c):
+ """
+ >>> f1 = test_exttype_args([1, 2, 3], 123, Foo('Foo'))
+ >>> f2 = test_exttype_args([0], 0, Foo('Bar'))
+ >>> f1()
+ ([1, 2, 3], 123, <Foo>)
+ >>> f2()
+ ([0], 0, <Bar>)
+ """
+ def inner(a=a, int b=b, Foo c=c):
+ return a, b, c
+ return inner

0 comments on commit 8c0f27d

Please sign in to comment.