Bugs fixed
----------
+* The ``__defaults__`` attribute was not writable for Cython implemented
+ functions.
+
+* Default values of keyword-only arguments showed up in ``__defaults__`` instead
+ of ``__kwdefaults__`` (which was not implemented). Both are available for
+ Cython implemented functions now, as specified in Python 3.x.
+
* ``yield`` works inside of ``with gil`` sections. It previously lead to a crash.
This fixes trac ticket 803.
# module_name EncodedString Name of defining module
# code_object CodeObjectNode the PyCodeObject creator node
- subexprs = ['code_object', 'defaults_tuple']
+ subexprs = ['code_object', 'defaults_tuple', 'defaults_kwdict']
self_object = None
code_object = None
defaults_struct = None
defaults_pyobjects = 0
defaults_tuple = None
+ defaults_kwdict = None
type = py_object_type
is_temp = 1
nonliteral_objects = []
nonliteral_other = []
default_args = []
+ default_kwargs = []
for arg in self.def_node.args:
if arg.default:
if not arg.default.is_literal:
nonliteral_other.append(arg)
else:
arg.default = DefaultLiteralArgNode(arg.pos, arg.default)
- default_args.append(arg)
+ if arg.kw_only:
+ default_kwargs.append(arg)
+ else:
+ default_args.append(arg)
if nonliteral_objects or nonliteral_other:
module_scope = env.global_scope()
cname = module_scope.next_id(Naming.defaults_struct_prefix)
Naming.dynamic_args_cname, entry.cname)
self.def_node.defaults_struct = self.defaults_struct.name
- if default_args:
+ if default_args or default_kwargs:
if self.defaults_struct is None:
- defaults_tuple = TupleNode(self.pos, args=[
- arg.default for arg in default_args])
- self.defaults_tuple = defaults_tuple.analyse_types(env)
+ if default_args:
+ defaults_tuple = TupleNode(self.pos, args=[
+ arg.default for arg in default_args])
+ self.defaults_tuple = defaults_tuple.analyse_types(env)
+ if default_kwargs:
+ defaults_kwdict = DictNode(self.pos, key_value_pairs=[
+ DictItemNode(
+ arg.pos,
+ key=IdentifierStringNode(arg.pos, value=arg.name),
+ value=arg.default)
+ for arg in default_kwargs])
+ self.defaults_kwdict = defaults_kwdict.analyse_types(env)
else:
+ if default_args:
+ defaults_tuple = DefaultsTupleNode(
+ self.pos, default_args, self.defaults_struct)
+ else:
+ defaults_tuple = NoneNode(self.pos)
+ if default_kwargs:
+ defaults_kwdict = DefaultsKwDictNode(
+ self.pos, default_kwargs, self.defaults_struct)
+ else:
+ defaults_kwdict = NoneNode(self.pos)
+
defaults_getter = Nodes.DefNode(
self.pos, args=[], star_arg=None, starstar_arg=None,
body=Nodes.ReturnStatNode(
self.pos, return_type=py_object_type,
- value=DefaultsTupleNode(
- self.pos, default_args,
- self.defaults_struct)),
- decorators=None, name=StringEncoding.EncodedString("__defaults__"))
+ value=TupleNode(
+ self.pos, args=[defaults_tuple, defaults_kwdict])),
+ decorators=None,
+ name=StringEncoding.EncodedString("__defaults__"))
defaults_getter.analyse_declarations(env)
defaults_getter = defaults_getter.analyse_expressions(env)
defaults_getter.body = defaults_getter.body.analyse_expressions(
if self.defaults_tuple:
code.putln('__Pyx_CyFunction_SetDefaultsTuple(%s, %s);' % (
self.result(), self.defaults_tuple.py_result()))
+ if self.defaults_kwdict:
+ code.putln('__Pyx_CyFunction_SetDefaultsKwDict(%s, %s);' % (
+ self.result(), self.defaults_kwdict.py_result()))
if def_node.defaults_getter:
code.putln('__Pyx_CyFunction_SetDefaultsGetter(%s, %s);' % (
self.result(), def_node.defaults_getter.entry.pyfunc_cname))
super(DefaultsTupleNode, self).__init__(pos, args=args)
+class DefaultsKwDictNode(DictNode):
+ # CyFunction's __kwdefaults__ dict
+
+ def __init__(self, pos, defaults, defaults_struct):
+ items = []
+ for arg in defaults:
+ name = IdentifierStringNode(arg.pos, value=arg.name)
+ if not arg.default.is_literal:
+ arg = DefaultNonLiteralArgNode(pos, arg, defaults_struct)
+ else:
+ arg = arg.default
+ items.append(DictItemNode(arg.pos, key=name, value=arg))
+ super(DefaultsKwDictNode, self).__init__(pos, key_value_pairs=items)
+
+
class LambdaNode(InnerFunctionNode):
# Lambda expression node (only used as a function reference)
#
/* Defaults info */
PyObject *defaults_tuple; /* Const defaults tuple */
+ PyObject *defaults_kwdict; /* Const kwonly defaults dict */
PyObject *(*defaults_getter)(PyObject *);
} __pyx_CyFunctionObject;
int pyobjects);
static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *m,
PyObject *tuple);
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *m,
+ PyObject *dict);
static int __Pyx_CyFunction_init(void);
return result;
}
+static int
+__Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) {
+ PyObject *res = op->defaults_getter((PyObject *) op);
+ if (unlikely(!res))
+ return -1;
+
+ /* Cache result */
+ op->defaults_tuple = PyTuple_GET_ITEM(res, 0);
+ Py_INCREF(op->defaults_tuple);
+ op->defaults_kwdict = PyTuple_GET_ITEM(res, 1);
+ Py_INCREF(op->defaults_kwdict);
+ Py_DECREF(res);
+ return 0;
+}
+
+static int
+__Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value) {
+ PyObject* tmp;
+ if (!value) {
+ // del => explicit None to prevent rebuilding
+ value = Py_None;
+ } else if (value != Py_None && !PyTuple_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
+ "__defaults__ must be set to a tuple object");
+ return -1;
+ }
+ Py_INCREF(value);
+ tmp = op->defaults_tuple;
+ op->defaults_tuple = value;
+ Py_XDECREF(tmp);
+ return 0;
+}
+
static PyObject *
-__Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op)
-{
- if (op->defaults_tuple) {
- Py_INCREF(op->defaults_tuple);
- return op->defaults_tuple;
+__Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op) {
+ PyObject* result = op->defaults_tuple;
+ if (unlikely(!result)) {
+ if (op->defaults_getter) {
+ if (__Pyx_CyFunction_init_defaults(op) < 0) return NULL;
+ result = op->defaults_tuple;
+ } else {
+ result = Py_None;
+ }
}
+ Py_INCREF(result);
+ return result;
+}
- if (op->defaults_getter) {
- PyObject *res = op->defaults_getter((PyObject *) op);
+static int
+__Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value) {
+ PyObject* tmp;
+ if (!value) {
+ // del => explicit None to prevent rebuilding
+ value = Py_None;
+ } else if (value != Py_None && !PyDict_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
+ "__kwdefaults__ must be set to a dict object");
+ return -1;
+ }
+ Py_INCREF(value);
+ tmp = op->defaults_kwdict;
+ op->defaults_kwdict = value;
+ Py_XDECREF(tmp);
+ return 0;
+}
- /* Cache result */
- if (likely(res)) {
- Py_INCREF(res);
- op->defaults_tuple = res;
+static PyObject *
+__Pyx_CyFunction_get_kwdefaults(__pyx_CyFunctionObject *op) {
+ PyObject* result = op->defaults_kwdict;
+ if (unlikely(!result)) {
+ if (op->defaults_getter) {
+ if (__Pyx_CyFunction_init_defaults(op) < 0) return NULL;
+ result = op->defaults_kwdict;
+ } else {
+ result = Py_None;
}
- return res;
}
-
- Py_INCREF(Py_None);
- return Py_None;
+ Py_INCREF(result);
+ return result;
}
static PyGetSetDef __pyx_CyFunction_getsets[] = {
{(char *) "__closure__", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0},
{(char *) "func_code", (getter)__Pyx_CyFunction_get_code, 0, 0, 0},
{(char *) "__code__", (getter)__Pyx_CyFunction_get_code, 0, 0, 0},
- {(char *) "func_defaults", (getter)__Pyx_CyFunction_get_defaults, 0, 0, 0},
- {(char *) "__defaults__", (getter)__Pyx_CyFunction_get_defaults, 0, 0, 0},
+ {(char *) "func_defaults", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0},
+ {(char *) "__defaults__", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0},
+ {(char *) "__kwdefaults__", (getter)__Pyx_CyFunction_get_kwdefaults, (setter)__Pyx_CyFunction_set_kwdefaults, 0, 0},
{0, 0, 0, 0, 0}
};
op->defaults_pyobjects = 0;
op->defaults = NULL;
op->defaults_tuple = NULL;
+ op->defaults_kwdict = NULL;
op->defaults_getter = NULL;
PyObject_GC_Track(op);
return (PyObject *) op;
Py_CLEAR(m->func_code);
Py_CLEAR(m->func_classobj);
Py_CLEAR(m->defaults_tuple);
+ Py_CLEAR(m->defaults_kwdict);
if (m->defaults) {
PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m);
Py_VISIT(m->func_code);
Py_VISIT(m->func_classobj);
Py_VISIT(m->defaults_tuple);
+ Py_VISIT(m->defaults_kwdict);
if (m->defaults) {
PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m);
Py_INCREF(tuple);
}
+static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *func, PyObject *dict) {
+ __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func;
+ m->defaults_kwdict = dict;
+ Py_INCREF(dict);
+}
+
//////////////////// CyFunctionClassCell.proto ////////////////////
static CYTHON_INLINE void __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions,
PyObject *classobj);
cimport cython
import sys
+IS_PY3 = sys.version_info[0] >= 3
+
def get_defaults(func):
if sys.version_info >= (2, 6, 0):
return func.__defaults__
return a
return func
+
+def cy_kwonly_default_args(a, x=1, *, b=2):
+ l = m = 1
+
+def test_kwdefaults(value):
+ """
+ >>> cy_kwonly_default_args.__defaults__
+ (1,)
+ >>> cy_kwonly_default_args.func_defaults
+ (1,)
+
+ >>> cy_kwonly_default_args.__kwdefaults__
+ {'b': 2}
+
+ >>> if IS_PY3: test_kwdefaults.__defaults__ is None
+ ... else: print(True)
+ True
+ >>> test_kwdefaults.__kwdefaults__ is None
+ ... else: print(True)
+ True
+
+ >>> f = test_kwdefaults(5)
+ >>> f.__defaults__
+ (1,)
+ >>> f.__kwdefaults__
+ {'b': 5}
+ >>> f.__kwdefaults__ = ()
+ Traceback (most recent call last):
+ TypeError: __kwdefaults__ must be set to a dict object
+ >>> f.__kwdefaults__ = None
+ >>> f.__kwdefaults__
+ >>> f.__kwdefaults__ = {}
+ >>> f.__kwdefaults__
+ {}
+ >>> f.__kwdefaults__ = {'a': 2}
+ >>> f.__kwdefaults__
+ {'a': 2}
+ """
+ def kwonly_default_args(a, x=1, *, b=value):
+ return a, x, b
+ return kwonly_default_args
+
+
_counter2 = 1.0
def counter2():
global _counter2