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): ...@@ -5766,6 +5766,59 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) 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): class BoundMethodNode(ExprNode):
# Helper class used in the implementation of Python # Helper class used in the implementation of Python
# class definitions. Constructs an bound method # class definitions. Constructs an bound method
...@@ -5926,6 +5979,16 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin): ...@@ -5926,6 +5979,16 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
code.put_gotref(self.py_result()) 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: if self.specialized_cpdefs:
self.generate_fused_cpdef(code, code_object_result, flags) self.generate_fused_cpdef(code, code_object_result, flags)
...@@ -6074,6 +6137,7 @@ class LambdaNode(InnerFunctionNode): ...@@ -6074,6 +6137,7 @@ class LambdaNode(InnerFunctionNode):
self.def_node.no_assignment_synthesis = True self.def_node.no_assignment_synthesis = True
self.def_node.pymethdef_required = True self.def_node.pymethdef_required = True
self.def_node.analyse_declarations(env) self.def_node.analyse_declarations(env)
self.def_node.is_cyfunction = True
self.pymethdef_cname = self.def_node.entry.pymethdef_cname self.pymethdef_cname = self.def_node.entry.pymethdef_cname
env.add_lambda_def(self.def_node) env.add_lambda_def(self.def_node)
...@@ -6101,6 +6165,7 @@ class GeneratorExpressionNode(LambdaNode): ...@@ -6101,6 +6165,7 @@ class GeneratorExpressionNode(LambdaNode):
super(GeneratorExpressionNode, self).analyse_declarations(env) super(GeneratorExpressionNode, self).analyse_declarations(env)
# No pymethdef required # No pymethdef required
self.def_node.pymethdef_required = False self.def_node.pymethdef_required = False
self.def_node.is_cyfunction = False
# Force genexpr signature # Force genexpr signature
self.def_node.entry.signature = TypeSlots.pyfunction_noargs self.def_node.entry.signature = TypeSlots.pyfunction_noargs
...@@ -9885,7 +9950,10 @@ fused_function_utility_code = UtilityCode.load( ...@@ -9885,7 +9950,10 @@ fused_function_utility_code = UtilityCode.load(
"CythonFunction.c", "CythonFunction.c",
context=vars(Naming), context=vars(Naming),
requires=[binding_cfunc_utility_code]) requires=[binding_cfunc_utility_code])
cyfunction_class_cell_utility_code = UtilityCode.load(
"CyFunctionClassCell",
"CythonFunction.c",
requires=[binding_cfunc_utility_code])
generator_utility_code = UtilityCode( generator_utility_code = UtilityCode(
proto=""" proto="""
......
...@@ -1299,6 +1299,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1299,6 +1299,7 @@ class FuncDefNode(StatNode, BlockNode):
has_fused_arguments = False has_fused_arguments = False
star_arg = None star_arg = None
starstar_arg = None starstar_arg = None
is_cyfunction = False
def analyse_default_values(self, env): def analyse_default_values(self, env):
genv = env.global_scope() genv = env.global_scope()
...@@ -1507,10 +1508,16 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1507,10 +1508,16 @@ class FuncDefNode(StatNode, BlockNode):
code.put_gotref(Naming.cur_scope_cname) code.put_gotref(Naming.cur_scope_cname)
# Note that it is unsafe to decref the scope at this point. # Note that it is unsafe to decref the scope at this point.
if self.needs_outer_scope: if self.needs_outer_scope:
code.putln("%s = (%s)%s;" % ( if self.is_cyfunction:
outer_scope_cname, code.putln("%s = (%s) __Pyx_CyFunction_GetClosure(%s);" % (
cenv.scope_class.type.declaration_code(''), outer_scope_cname,
Naming.self_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: if lenv.is_passthrough:
code.putln("%s = %s;" % (Naming.cur_scope_cname, outer_scope_cname)); code.putln("%s = %s;" % (Naming.cur_scope_cname, outer_scope_cname));
elif self.needs_closure: elif self.needs_closure:
...@@ -2633,6 +2640,7 @@ class DefNode(FuncDefNode): ...@@ -2633,6 +2640,7 @@ class DefNode(FuncDefNode):
acquire_gil = 0 acquire_gil = 0
self_in_stararg = 0 self_in_stararg = 0
py_cfunc_node = None py_cfunc_node = None
requires_classobj = False
doc = None doc = None
fused_py_func = False fused_py_func = False
...@@ -3009,6 +3017,8 @@ class DefNode(FuncDefNode): ...@@ -3009,6 +3017,8 @@ class DefNode(FuncDefNode):
if env.is_py_class_scope: if env.is_py_class_scope:
rhs.binding = True rhs.binding = True
self.is_cyfunction = rhs.binding
if self.decorators: if self.decorators:
for decorator in self.decorators[::-1]: for decorator in self.decorators[::-1]:
rhs = ExprNodes.SimpleCallNode( rhs = ExprNodes.SimpleCallNode(
...@@ -3744,6 +3754,12 @@ class GeneratorDefNode(DefNode): ...@@ -3744,6 +3754,12 @@ class GeneratorDefNode(DefNode):
code.putln('%s.resume_label = 0;' % generator_cname) code.putln('%s.resume_label = 0;' % generator_cname)
code.putln('%s.body = (__pyx_generator_body_t) %s;' % (generator_cname, body_cname)) code.putln('%s.body = (__pyx_generator_body_t) %s;' % (generator_cname, body_cname))
code.put_giveref(Naming.cur_scope_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.put_finish_refcount_context()
code.putln("return (PyObject *) %s;" % Naming.cur_scope_cname); code.putln("return (PyObject *) %s;" % Naming.cur_scope_cname);
...@@ -3939,7 +3955,8 @@ class PyClassDefNode(ClassDefNode): ...@@ -3939,7 +3955,8 @@ class PyClassDefNode(ClassDefNode):
# classobj ClassNode Class object # classobj ClassNode Class object
# target NameNode Variable to assign class object to # 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 decorators = None
class_result = None class_result = None
py3_style_class = False # Python3 style class (bases+kwargs) py3_style_class = False # Python3 style class (bases+kwargs)
...@@ -3996,6 +4013,7 @@ class PyClassDefNode(ClassDefNode): ...@@ -3996,6 +4013,7 @@ class PyClassDefNode(ClassDefNode):
self.classobj = ExprNodes.ClassNode(pos, name = name, self.classobj = ExprNodes.ClassNode(pos, name = name,
bases = bases, dict = self.dict, doc = doc_node) bases = bases, dict = self.dict, doc = doc_node)
self.target = ExprNodes.NameNode(pos, name = name) self.target = ExprNodes.NameNode(pos, name = name)
self.class_cell = ExprNodes.ClassCellInjectorNode(self.pos)
def as_cclass(self): def as_cclass(self):
""" """
...@@ -4074,6 +4092,7 @@ class PyClassDefNode(ClassDefNode): ...@@ -4074,6 +4092,7 @@ class PyClassDefNode(ClassDefNode):
cenv = self.scope cenv = self.scope
self.body.analyse_expressions(cenv) self.body.analyse_expressions(cenv)
self.target.analyse_target_expression(env, self.classobj) self.target.analyse_target_expression(env, self.classobj)
self.class_cell.analyse_expressions(cenv)
def generate_function_definitions(self, env, code): def generate_function_definitions(self, env, code):
self.generate_lambda_definitions(self.scope, code) self.generate_lambda_definitions(self.scope, code)
...@@ -4088,8 +4107,12 @@ class PyClassDefNode(ClassDefNode): ...@@ -4088,8 +4107,12 @@ class PyClassDefNode(ClassDefNode):
self.metaclass.generate_evaluation_code(code) self.metaclass.generate_evaluation_code(code)
self.dict.generate_evaluation_code(code) self.dict.generate_evaluation_code(code)
cenv.namespace_cname = cenv.class_obj_cname = self.dict.result() cenv.namespace_cname = cenv.class_obj_cname = self.dict.result()
self.class_cell.generate_evaluation_code(code)
self.body.generate_execution_code(code) self.body.generate_execution_code(code)
self.class_result.generate_evaluation_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() cenv.namespace_cname = cenv.class_obj_cname = self.classobj.result()
self.target.generate_assignment_code(self.class_result, code) self.target.generate_assignment_code(self.class_result, code)
self.dict.generate_disposal_code(code) self.dict.generate_disposal_code(code)
......
...@@ -2137,6 +2137,12 @@ class CreateClosureClasses(CythonTransform): ...@@ -2137,6 +2137,12 @@ class CreateClosureClasses(CythonTransform):
class_scope = entry.type.scope class_scope = entry.type.scope
class_scope.is_internal = True 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: if from_closure:
assert cscope.is_closure_scope assert cscope.is_closure_scope
class_scope.declare_var(pos=node.pos, class_scope.declare_var(pos=node.pos,
...@@ -2411,6 +2417,28 @@ class TransformBuiltinMethods(EnvTransform): ...@@ -2411,6 +2417,28 @@ class TransformBuiltinMethods(EnvTransform):
node.pos, self.current_scope_node(), lenv)) node.pos, self.current_scope_node(), lenv))
return node 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): def visit_SimpleCallNode(self, node):
# cython.foo # cython.foo
function = node.function.as_cython_attribute() function = node.function.as_cython_attribute()
...@@ -2471,6 +2499,8 @@ class TransformBuiltinMethods(EnvTransform): ...@@ -2471,6 +2499,8 @@ class TransformBuiltinMethods(EnvTransform):
return self._inject_locals(node, func_name) return self._inject_locals(node, func_name)
if func_name == 'eval': if func_name == 'eval':
return self._inject_eval(node, func_name) return self._inject_eval(node, func_name)
if func_name == 'super':
return self._inject_super(node, func_name)
return node return node
......
...@@ -7,6 +7,12 @@ ...@@ -7,6 +7,12 @@
#define __Pyx_CYFUNCTION_STATICMETHOD 0x01 #define __Pyx_CYFUNCTION_STATICMETHOD 0x01
#define __Pyx_CYFUNCTION_CLASSMETHOD 0x02 #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 { typedef struct {
PyCFunctionObject func; PyCFunctionObject func;
int flags; int flags;
...@@ -15,6 +21,8 @@ typedef struct { ...@@ -15,6 +21,8 @@ typedef struct {
PyObject *func_name; PyObject *func_name;
PyObject *func_doc; PyObject *func_doc;
PyObject *func_code; PyObject *func_code;
PyObject *func_closure;
PyObject *func_classobj; /* No-args super() class cell */
} __pyx_CyFunctionObject; } __pyx_CyFunctionObject;
static PyTypeObject *__pyx_CyFunctionType = 0; static PyTypeObject *__pyx_CyFunctionType = 0;
...@@ -102,7 +110,7 @@ __Pyx_CyFunction_get_self(__pyx_CyFunctionObject *m, CYTHON_UNUSED void *closure ...@@ -102,7 +110,7 @@ __Pyx_CyFunction_get_self(__pyx_CyFunctionObject *m, CYTHON_UNUSED void *closure
{ {
PyObject *self; PyObject *self;
self = m->func.m_self; self = m->func_closure;
if (self == NULL) if (self == NULL)
self = Py_None; self = Py_None;
Py_INCREF(self); Py_INCREF(self);
...@@ -219,20 +227,22 @@ static PyMethodDef __pyx_CyFunction_methods[] = { ...@@ -219,20 +227,22 @@ static PyMethodDef __pyx_CyFunction_methods[] = {
static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags, 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); __pyx_CyFunctionObject *op = PyObject_GC_New(__pyx_CyFunctionObject, type);
if (op == NULL) if (op == NULL)
return NULL; return NULL;
op->flags = flags; op->flags = flags;
op->func_weakreflist = NULL; op->func_weakreflist = NULL;
op->func.m_ml = ml; op->func.m_ml = ml;
Py_XINCREF(self); op->func.m_self = (PyObject *) op;
op->func.m_self = self; Py_XINCREF(closure);
op->func_closure = closure;
Py_XINCREF(module); Py_XINCREF(module);
op->func.m_module = module; op->func.m_module = module;
op->func_dict = NULL; op->func_dict = NULL;
op->func_name = NULL; op->func_name = NULL;
op->func_doc = NULL; op->func_doc = NULL;
op->func_classobj = NULL;
Py_XINCREF(code); Py_XINCREF(code);
op->func_code = code; op->func_code = code;
PyObject_GC_Track(op); PyObject_GC_Track(op);
...@@ -242,12 +252,13 @@ static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int f ...@@ -242,12 +252,13 @@ static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int f
static int static int
__Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) __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.m_module);
Py_CLEAR(m->func_dict); Py_CLEAR(m->func_dict);
Py_CLEAR(m->func_name); Py_CLEAR(m->func_name);
Py_CLEAR(m->func_doc); Py_CLEAR(m->func_doc);
Py_CLEAR(m->func_code); Py_CLEAR(m->func_code);
Py_CLEAR(m->func_classobj);
return 0; return 0;
} }
...@@ -262,12 +273,13 @@ static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m) ...@@ -262,12 +273,13 @@ static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m)
static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg) 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.m_module);
Py_VISIT(m->func_dict); Py_VISIT(m->func_dict);
Py_VISIT(m->func_name); Py_VISIT(m->func_name);
Py_VISIT(m->func_doc); Py_VISIT(m->func_doc);
Py_VISIT(m->func_code); Py_VISIT(m->func_code);
Py_VISIT(m->func_classobj);
return 0; return 0;
} }
...@@ -371,7 +383,23 @@ static int __Pyx_CyFunction_init(void) ...@@ -371,7 +383,23 @@ static int __Pyx_CyFunction_init(void)
return 0; 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 //////////////////// //////////////////// FusedFunction.proto ////////////////////
typedef struct { typedef struct {
...@@ -456,7 +484,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type) ...@@ -456,7 +484,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
meth = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_NewEx( meth = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_NewEx(
((PyCFunctionObject *) func)->m_ml, ((PyCFunctionObject *) func)->m_ml,
((__pyx_CyFunctionObject *) func)->flags, ((__pyx_CyFunctionObject *) func)->flags,
((PyCFunctionObject *) func)->m_self, ((__pyx_CyFunctionObject *) func)->func_closure,
((PyCFunctionObject *) func)->m_module, ((PyCFunctionObject *) func)->m_module,
((__pyx_CyFunctionObject *) func)->func_code); ((__pyx_CyFunctionObject *) func)->func_code);
if (!meth) 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