Commit e37ac1ed authored by Vitja Makarov's avatar Vitja Makarov

Implement Py3k noargs super(), ticket #696

parent c228cfff
......@@ -5766,6 +5766,59 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
class ClassCellInjectorNode(ExprNode):
# Initialize CyFunction.func_classobj
is_temp = True
type = py_object_type
subexprs = []
is_active = False
def analyse_expressions(self, env):
if self.is_active:
env.use_utility_code(cyfunction_class_cell_utility_code)
def generate_evaluation_code(self, code):
if self.is_active:
self.allocate_temp_result(code)
code.putln(
'%s = PyList_New(0); %s' % (
self.result(),
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.result())
def generate_injection_code(self, code, classobj_cname):
if self.is_active:
code.putln('__Pyx_CyFunction_InitClassCell(%s, %s);' % (
self.result(), classobj_cname))
class ClassCellNode(ExprNode):
# Class Cell for noargs super()
subexprs = []
is_temp = True
is_generator = False
type = py_object_type
def analyse_types(self, env):
pass
def generate_result_code(self, code):
if not self.is_generator:
code.putln('%s = __Pyx_CyFunction_GetClassObj(%s);' % (
self.result(),
Naming.self_cname))
else:
code.putln('%s = %s->classobj;' % (
self.result(), Naming.cur_scope_cname))
code.putln(
'if (!%s) { PyErr_SetString(PyExc_SystemError, '
'"super(): empty __class__ cell"); %s }' % (
self.result(),
code.error_goto(self.pos)));
code.put_incref(self.result(), py_object_type)
class BoundMethodNode(ExprNode):
# Helper class used in the implementation of Python
# class definitions. Constructs an bound method
......@@ -5926,6 +5979,16 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
code.put_gotref(self.py_result())
if self.def_node.requires_classobj:
assert code.pyclass_stack, "pyclass_stack is empty"
class_node = code.pyclass_stack[-1]
code.put_incref(self.py_result(), py_object_type)
code.putln(
'PyList_Append(%s, %s);' % (
class_node.class_cell.result(),
self.result()))
code.put_giveref(self.py_result())
if self.specialized_cpdefs:
self.generate_fused_cpdef(code, code_object_result, flags)
......@@ -6074,6 +6137,7 @@ class LambdaNode(InnerFunctionNode):
self.def_node.no_assignment_synthesis = True
self.def_node.pymethdef_required = True
self.def_node.analyse_declarations(env)
self.def_node.is_cyfunction = True
self.pymethdef_cname = self.def_node.entry.pymethdef_cname
env.add_lambda_def(self.def_node)
......@@ -6101,6 +6165,7 @@ class GeneratorExpressionNode(LambdaNode):
super(GeneratorExpressionNode, self).analyse_declarations(env)
# No pymethdef required
self.def_node.pymethdef_required = False
self.def_node.is_cyfunction = False
# Force genexpr signature
self.def_node.entry.signature = TypeSlots.pyfunction_noargs
......@@ -9885,7 +9950,10 @@ fused_function_utility_code = UtilityCode.load(
"CythonFunction.c",
context=vars(Naming),
requires=[binding_cfunc_utility_code])
cyfunction_class_cell_utility_code = UtilityCode.load(
"CyFunctionClassCell",
"CythonFunction.c",
requires=[binding_cfunc_utility_code])
generator_utility_code = UtilityCode(
proto="""
......
......@@ -1299,6 +1299,7 @@ class FuncDefNode(StatNode, BlockNode):
has_fused_arguments = False
star_arg = None
starstar_arg = None
is_cyfunction = False
def analyse_default_values(self, env):
genv = env.global_scope()
......@@ -1507,10 +1508,16 @@ class FuncDefNode(StatNode, BlockNode):
code.put_gotref(Naming.cur_scope_cname)
# Note that it is unsafe to decref the scope at this point.
if self.needs_outer_scope:
code.putln("%s = (%s)%s;" % (
outer_scope_cname,
cenv.scope_class.type.declaration_code(''),
Naming.self_cname))
if self.is_cyfunction:
code.putln("%s = (%s) __Pyx_CyFunction_GetClosure(%s);" % (
outer_scope_cname,
cenv.scope_class.type.declaration_code(''),
Naming.self_cname))
else:
code.putln("%s = (%s) %s;" % (
outer_scope_cname,
cenv.scope_class.type.declaration_code(''),
Naming.self_cname))
if lenv.is_passthrough:
code.putln("%s = %s;" % (Naming.cur_scope_cname, outer_scope_cname));
elif self.needs_closure:
......@@ -2633,6 +2640,7 @@ class DefNode(FuncDefNode):
acquire_gil = 0
self_in_stararg = 0
py_cfunc_node = None
requires_classobj = False
doc = None
fused_py_func = False
......@@ -3009,6 +3017,8 @@ class DefNode(FuncDefNode):
if env.is_py_class_scope:
rhs.binding = True
self.is_cyfunction = rhs.binding
if self.decorators:
for decorator in self.decorators[::-1]:
rhs = ExprNodes.SimpleCallNode(
......@@ -3744,6 +3754,12 @@ class GeneratorDefNode(DefNode):
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)
if self.requires_classobj:
classobj_cname = '%s->classobj' % Naming.cur_scope_cname
code.putln('%s = __Pyx_CyFunction_GetClassObj(%s);' % (
classobj_cname, Naming.self_cname))
code.put_incref(classobj_cname, py_object_type)
code.put_giveref(classobj_cname)
code.put_finish_refcount_context()
code.putln("return (PyObject *) %s;" % Naming.cur_scope_cname);
......@@ -3939,7 +3955,8 @@ class PyClassDefNode(ClassDefNode):
# classobj ClassNode Class object
# target NameNode Variable to assign class object to
child_attrs = ["body", "dict", "metaclass", "mkw", "bases", "class_result", "target"]
child_attrs = ["body", "dict", "metaclass", "mkw", "bases", "class_result",
"target", "class_cell"]
decorators = None
class_result = None
py3_style_class = False # Python3 style class (bases+kwargs)
......@@ -3996,6 +4013,7 @@ class PyClassDefNode(ClassDefNode):
self.classobj = ExprNodes.ClassNode(pos, name = name,
bases = bases, dict = self.dict, doc = doc_node)
self.target = ExprNodes.NameNode(pos, name = name)
self.class_cell = ExprNodes.ClassCellInjectorNode(self.pos)
def as_cclass(self):
"""
......@@ -4074,6 +4092,7 @@ class PyClassDefNode(ClassDefNode):
cenv = self.scope
self.body.analyse_expressions(cenv)
self.target.analyse_target_expression(env, self.classobj)
self.class_cell.analyse_expressions(cenv)
def generate_function_definitions(self, env, code):
self.generate_lambda_definitions(self.scope, code)
......@@ -4088,8 +4107,12 @@ class PyClassDefNode(ClassDefNode):
self.metaclass.generate_evaluation_code(code)
self.dict.generate_evaluation_code(code)
cenv.namespace_cname = cenv.class_obj_cname = self.dict.result()
self.class_cell.generate_evaluation_code(code)
self.body.generate_execution_code(code)
self.class_result.generate_evaluation_code(code)
self.class_cell.generate_injection_code(
code, self.class_result.result())
self.class_cell.generate_disposal_code(code)
cenv.namespace_cname = cenv.class_obj_cname = self.classobj.result()
self.target.generate_assignment_code(self.class_result, code)
self.dict.generate_disposal_code(code)
......
......@@ -2137,6 +2137,12 @@ class CreateClosureClasses(CythonTransform):
class_scope = entry.type.scope
class_scope.is_internal = True
if node.is_generator and node.requires_classobj:
class_scope.declare_var(pos=node.pos, name='classobj',
cname='classobj',
type=PyrexTypes.py_object_type,
is_cdef=True)
if from_closure:
assert cscope.is_closure_scope
class_scope.declare_var(pos=node.pos,
......@@ -2411,6 +2417,28 @@ class TransformBuiltinMethods(EnvTransform):
node.pos, self.current_scope_node(), lenv))
return node
def _inject_super(self, node, func_name):
lenv = self.current_env()
entry = lenv.lookup_here(func_name)
if entry or node.args:
return node
# Inject no-args super
def_node = self.current_scope_node()
if (not isinstance(def_node, Nodes.DefNode) or
len(self.env_stack) < 2):
return node
class_node, class_scope = self.env_stack[-2]
if class_scope.is_py_class_scope and def_node.args:
def_node.requires_classobj = True
class_node.class_cell.is_active = True
node.args = [
ExprNodes.ClassCellNode(
node.pos, is_generator=def_node.is_generator),
ExprNodes.NameNode(node.pos, name=def_node.args[0].name)
]
return node
return node
def visit_SimpleCallNode(self, node):
# cython.foo
function = node.function.as_cython_attribute()
......@@ -2471,6 +2499,8 @@ class TransformBuiltinMethods(EnvTransform):
return self._inject_locals(node, func_name)
if func_name == 'eval':
return self._inject_eval(node, func_name)
if func_name == 'super':
return self._inject_super(node, func_name)
return node
......
......@@ -7,6 +7,12 @@
#define __Pyx_CYFUNCTION_STATICMETHOD 0x01
#define __Pyx_CYFUNCTION_CLASSMETHOD 0x02
#define __Pyx_CyFunction_GetClosure(f) \
(((__pyx_CyFunctionObject *) (f))->func_closure)
#define __Pyx_CyFunction_GetClassObj(f) \
(((__pyx_CyFunctionObject *) (f))->func_classobj)
typedef struct {
PyCFunctionObject func;
int flags;
......@@ -15,6 +21,8 @@ typedef struct {
PyObject *func_name;
PyObject *func_doc;
PyObject *func_code;
PyObject *func_closure;
PyObject *func_classobj; /* No-args super() class cell */
} __pyx_CyFunctionObject;
static PyTypeObject *__pyx_CyFunctionType = 0;
......@@ -102,7 +110,7 @@ __Pyx_CyFunction_get_self(__pyx_CyFunctionObject *m, CYTHON_UNUSED void *closure
{
PyObject *self;
self = m->func.m_self;
self = m->func_closure;
if (self == NULL)
self = Py_None;
Py_INCREF(self);
......@@ -219,20 +227,22 @@ static PyMethodDef __pyx_CyFunction_methods[] = {
static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags,
PyObject *self, PyObject *module, PyObject* code) {
PyObject *closure, PyObject *module, PyObject* code) {
__pyx_CyFunctionObject *op = PyObject_GC_New(__pyx_CyFunctionObject, type);
if (op == NULL)
return NULL;
op->flags = flags;
op->func_weakreflist = NULL;
op->func.m_ml = ml;
Py_XINCREF(self);
op->func.m_self = self;
op->func.m_self = (PyObject *) op;
Py_XINCREF(closure);
op->func_closure = closure;
Py_XINCREF(module);
op->func.m_module = module;
op->func_dict = NULL;
op->func_name = NULL;
op->func_doc = NULL;
op->func_classobj = NULL;
Py_XINCREF(code);
op->func_code = code;
PyObject_GC_Track(op);
......@@ -242,12 +252,13 @@ static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int f
static int
__Pyx_CyFunction_clear(__pyx_CyFunctionObject *m)
{
Py_CLEAR(m->func.m_self);
Py_CLEAR(m->func_closure);
Py_CLEAR(m->func.m_module);
Py_CLEAR(m->func_dict);
Py_CLEAR(m->func_name);
Py_CLEAR(m->func_doc);
Py_CLEAR(m->func_code);
Py_CLEAR(m->func_classobj);
return 0;
}
......@@ -262,12 +273,13 @@ static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m)
static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg)
{
Py_VISIT(m->func.m_self);
Py_VISIT(m->func_closure);
Py_VISIT(m->func.m_module);
Py_VISIT(m->func_dict);
Py_VISIT(m->func_name);
Py_VISIT(m->func_doc);
Py_VISIT(m->func_code);
Py_VISIT(m->func_classobj);
return 0;
}
......@@ -371,7 +383,23 @@ static int __Pyx_CyFunction_init(void)
return 0;
}
//////////////////// CyFunctionClassCell.proto ////////////////////
static CYTHON_INLINE void __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions,
PyObject *classobj);
//////////////////// CyFunctionClassCell ////////////////////
void __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions,
PyObject *classobj)
{
int i;
for (i = 0; i < PyList_GET_SIZE(cyfunctions); i++) {
__pyx_CyFunctionObject *m =
(__pyx_CyFunctionObject *) PyList_GET_ITEM(cyfunctions, i);
m->func_classobj = classobj;
Py_INCREF(classobj);
}
}
//////////////////// FusedFunction.proto ////////////////////
typedef struct {
......@@ -456,7 +484,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
meth = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_NewEx(
((PyCFunctionObject *) func)->m_ml,
((__pyx_CyFunctionObject *) func)->flags,
((PyCFunctionObject *) func)->m_self,
((__pyx_CyFunctionObject *) func)->func_closure,
((PyCFunctionObject *) func)->m_module,
((__pyx_CyFunctionObject *) func)->func_code);
if (!meth)
......
# mode: run
# tags: py3k_super
class A(object):
def method(self):
return 1
@classmethod
def class_method(cls):
return 2
@staticmethod
def static_method():
return 3
def generator_test(self):
return [1, 2, 3]
class B(A):
"""
>>> obj = B()
>>> obj.method()
1
>>> B.class_method()
2
>>> B.static_method(obj)
3
>>> list(obj.generator_test())
[1, 2, 3]
"""
def method(self):
return super().method()
@classmethod
def class_method(cls):
return super().class_method()
@staticmethod
def static_method(instance):
return super().static_method()
def generator_test(self):
for i in super().generator_test():
yield i
def test_class_cell_empty():
"""
>>> test_class_cell_empty()
Traceback (most recent call last):
...
SystemError: super(): empty __class__ cell
"""
class Base(type):
def __new__(cls, name, bases, attrs):
attrs['foo'](None)
class EmptyClassCell(metaclass=Base):
def foo(self):
super()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment