Skip to content

Commit ad90d62

Browse files
authored
[mypyc] Invoke __init__ functions inherited from non-ext classes (python#7639)
Currently if a class inherits from a nonext class or an interpreted class and doesn't define an __init__, the parent's __init__ is not invoked. Fix this.
1 parent bd00106 commit ad90d62

File tree

3 files changed

+71
-1
lines changed

3 files changed

+71
-1
lines changed

mypyc/emitclass.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,14 +427,27 @@ def generate_constructor_for_class(cl: ClassIR,
427427
emitter.emit_line('PyObject *self = {}();'.format(setup_name))
428428
emitter.emit_line('if (self == NULL)')
429429
emitter.emit_line(' return NULL;')
430+
args = ', '.join(['self'] + [REG_PREFIX + arg.name for arg in fn.sig.args])
430431
if init_fn is not None:
431-
args = ', '.join(['self'] + [REG_PREFIX + arg.name for arg in fn.sig.args])
432432
emitter.emit_line('char res = {}{}({});'.format(
433433
NATIVE_PREFIX, init_fn.cname(emitter.names), args))
434434
emitter.emit_line('if (res == 2) {')
435435
emitter.emit_line('Py_DECREF(self);')
436436
emitter.emit_line('return NULL;')
437437
emitter.emit_line('}')
438+
439+
# If there is a nontrivial ctor that we didn't define, invoke it via tp_init
440+
elif len(fn.sig.args) > 1:
441+
emitter.emit_line(
442+
'int res = {}->tp_init({});'.format(
443+
emitter.type_struct_name(cl),
444+
args))
445+
446+
emitter.emit_line('if (res < 0) {')
447+
emitter.emit_line('Py_DECREF(self);')
448+
emitter.emit_line('return NULL;')
449+
emitter.emit_line('}')
450+
438451
emitter.emit_line('return self;')
439452
emitter.emit_line('}')
440453

mypyc/genops.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,19 @@ def prepare_class_def(path: str, module_name: str, cdef: ClassDef,
557557
init_node = cdef.info['__init__'].node
558558
if not ir.is_trait and not ir.builtin_base and isinstance(init_node, FuncDef):
559559
init_sig = mapper.fdef_to_sig(init_node)
560+
561+
defining_ir = mapper.type_to_ir.get(init_node.info)
562+
# If there is a nontrivial __init__ that wasn't defined in an
563+
# extension class, we need to make the constructor take *args,
564+
# **kwargs so it can call tp_init.
565+
if ((defining_ir is None or not defining_ir.is_ext_class)
566+
and init_node.info.fullname() != 'builtins.object'):
567+
init_sig = FuncSignature(
568+
[init_sig.args[0],
569+
RuntimeArg("args", tuple_rprimitive, ARG_STAR),
570+
RuntimeArg("kwargs", dict_rprimitive, ARG_STAR2)],
571+
init_sig.ret_type)
572+
560573
ctor_sig = FuncSignature(init_sig.args[1:], RInstance(ir))
561574
ir.ctor = FuncDecl(cdef.name, None, module_name, ctor_sig)
562575
mapper.func_to_decl[cdef.info] = ir.ctor

mypyc/test-data/run-classes.test

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,3 +1109,47 @@ e.y = 20
11091109
assert e.__getstate__() == {'y': 20, '__dict__': {'x': 10}}
11101110
e2 = pickle.loads(pickle.dumps(e))
11111111
assert e is not e2 and e.x == e2.x and e.y == e2.y
1112+
1113+
1114+
[case testInterpretedParentInit]
1115+
from interp import C
1116+
from typing import TypeVar
1117+
1118+
T = TypeVar('T')
1119+
1120+
def dec(x: T) -> T:
1121+
return x
1122+
1123+
@dec
1124+
class A:
1125+
def __init__(self, x: int) -> None:
1126+
self.x = x
1127+
1128+
class B(A):
1129+
s = 'test'
1130+
1131+
def b(x: int) -> B: return B(x)
1132+
1133+
class D(C):
1134+
s = 'test'
1135+
1136+
def d(x: int) -> D: return D(x)
1137+
1138+
1139+
[file interp.py]
1140+
class C:
1141+
def __init__(self, x: int) -> None:
1142+
self.x = x
1143+
1144+
[file driver.py]
1145+
from native import b, d, B, D
1146+
1147+
def test(f, v):
1148+
x = f(v)
1149+
assert x.x == v
1150+
assert x.s == 'test'
1151+
1152+
test(b, 20)
1153+
test(d, 30)
1154+
test(B, -1)
1155+
test(D, -2)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy