Skip to content

Commit 940fceb

Browse files
authored
[mypyc] Fix direct __dict__ access on inner functions in new Python (#16084)
Fixes #16077
1 parent feb0fa7 commit 940fceb

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

mypyc/codegen/emitclass.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None:
217217
fields["tp_name"] = f'"{name}"'
218218

219219
generate_full = not cl.is_trait and not cl.builtin_base
220-
needs_getseters = cl.needs_getseters or not cl.is_generated
220+
needs_getseters = cl.needs_getseters or not cl.is_generated or cl.has_dict
221221

222222
if not cl.builtin_base:
223223
fields["tp_new"] = new_name
@@ -886,6 +886,9 @@ def generate_getseters_table(cl: ClassIR, name: str, emitter: Emitter) -> None:
886886
else:
887887
emitter.emit_line("NULL, NULL, NULL},")
888888

889+
if cl.has_dict:
890+
emitter.emit_line('{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},')
891+
889892
emitter.emit_line("{NULL} /* Sentinel */")
890893
emitter.emit_line("};")
891894

mypyc/test-data/run-functions.test

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,3 +1256,33 @@ def foo(**kwargs: Unpack[Person]) -> None:
12561256
foo(name='Jennifer', age=38)
12571257
[out]
12581258
Jennifer
1259+
1260+
[case testNestedFunctionDunderDict312]
1261+
import sys
1262+
1263+
def foo() -> None:
1264+
def inner() -> str: return "bar"
1265+
print(inner.__dict__) # type: ignore[attr-defined]
1266+
inner.__dict__.update({"x": 1}) # type: ignore[attr-defined]
1267+
print(inner.__dict__) # type: ignore[attr-defined]
1268+
print(inner.x) # type: ignore[attr-defined]
1269+
1270+
if sys.version_info >= (3, 12): # type: ignore
1271+
foo()
1272+
[out]
1273+
[out version>=3.12]
1274+
{}
1275+
{'x': 1}
1276+
1
1277+
1278+
[case testFunctoolsUpdateWrapper]
1279+
import functools
1280+
1281+
def bar() -> None:
1282+
def inner() -> str: return "bar"
1283+
functools.update_wrapper(inner, bar) # type: ignore
1284+
print(inner.__dict__) # type: ignore
1285+
1286+
bar()
1287+
[out]
1288+
{'__module__': 'native', '__name__': 'bar', '__qualname__': 'bar', '__doc__': None, '__wrapped__': <built-in function bar>}

0 commit comments

Comments
 (0)