Skip to content

Commit

Permalink
move __class__ special case out of the fast path
Browse files Browse the repository at this point in the history
  • Loading branch information
carljm committed Apr 20, 2023
1 parent f161268 commit df442c0
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 11 deletions.
17 changes: 17 additions & 0 deletions Lib/test/test_super.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,23 @@ def method(self):
with self.assertRaisesRegex(TypeError, "argument 1 must be a type"):
C().method()

def test_super___class__(self):
class C:
def method(self):
return super().__class__

self.assertEqual(C().method(), super)

def test_super_subclass___class__(self):
class mysuper(super):
pass

class C:
def method(self):
return mysuper(C, self).__class__

self.assertEqual(C().method(), mysuper)


if __name__ == "__main__":
unittest.main()
15 changes: 8 additions & 7 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -9370,13 +9370,6 @@ do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
if (su_obj_type == NULL)
goto skip;

/* We want __class__ to return the class of the super object
(i.e. super, or a subclass), not the class of su->obj. */
if (PyUnicode_Check(name) &&
PyUnicode_GET_LENGTH(name) == 9 &&
_PyUnicode_Equal(name, &_Py_ID(__class__)))
goto skip;

mro = su_obj_type->tp_mro;
if (mro == NULL)
goto skip;
Expand Down Expand Up @@ -9452,6 +9445,14 @@ static PyObject *
super_getattro(PyObject *self, PyObject *name)
{
superobject *su = (superobject *)self;

/* We want __class__ to return the class of the super object
(i.e. super, or a subclass), not the class of su->obj. */
if (PyUnicode_Check(name) &&
PyUnicode_GET_LENGTH(name) == 9 &&
_PyUnicode_Equal(name, &_Py_ID(__class__)))
return PyObject_GenericGetAttr(self, name);

return do_super_lookup(su, su->type, su->obj, su->obj_type, name, NULL);
}

Expand Down
10 changes: 6 additions & 4 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -4237,18 +4237,20 @@ is_import_originated(struct compiler *c, expr_ty e)
}

static int
can_optimize_super_call(struct compiler *c, expr_ty e)
can_optimize_super_call(struct compiler *c, expr_ty attr)
{
expr_ty e = attr->v.Attribute.value;
if (e->kind != Call_kind ||
e->v.Call.func->kind != Name_kind ||
!_PyUnicode_EqualToASCIIString(e->v.Call.func->v.Name.id, "super") ||
_PyUnicode_EqualToASCIIString(attr->v.Attribute.attr, "__class__") ||
asdl_seq_LEN(e->v.Call.keywords) != 0) {
return 0;
}
Py_ssize_t num_args = asdl_seq_LEN(e->v.Call.args);

PyObject *super_name = e->v.Call.func->v.Name.id;
// try to detect statically-visible shadowing of 'super' name
// detect statically-visible shadowing of 'super' name
int scope = _PyST_GetScope(c->u->u_ste, super_name);
if (scope != GLOBAL_IMPLICIT) {
return 0;
Expand Down Expand Up @@ -4388,7 +4390,7 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e)
/* Alright, we can optimize the code. */
location loc = LOC(meth);

if (can_optimize_super_call(c, meth->v.Attribute.value)) {
if (can_optimize_super_call(c, meth)) {
RETURN_IF_ERROR(load_args_for_super(c, meth->v.Attribute.value));
int opcode = asdl_seq_LEN(meth->v.Attribute.value->v.Call.args) ?
LOAD_SUPER_METHOD : LOAD_ZERO_SUPER_METHOD;
Expand Down Expand Up @@ -5406,7 +5408,7 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
return compiler_formatted_value(c, e);
/* The following exprs can be assignment targets. */
case Attribute_kind:
if (e->v.Attribute.ctx == Load && can_optimize_super_call(c, e->v.Attribute.value)) {
if (e->v.Attribute.ctx == Load && can_optimize_super_call(c, e)) {
RETURN_IF_ERROR(load_args_for_super(c, e->v.Attribute.value));
int opcode = asdl_seq_LEN(e->v.Attribute.value->v.Call.args) ?
LOAD_SUPER_ATTR : LOAD_ZERO_SUPER_ATTR;
Expand Down

0 comments on commit df442c0

Please sign in to comment.