Skip to content

Commit d44f023

Browse files
committed
add fix for ki protection leaking accross local functions
1 parent 0ed35a7 commit d44f023

File tree

1 file changed

+68
-4
lines changed

1 file changed

+68
-4
lines changed

src/trio/_core/_ki.py

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import sys
55
import types
66
import weakref
7-
from typing import TYPE_CHECKING, Protocol, TypeVar
7+
from typing import TYPE_CHECKING, Any, Generic, Protocol, TypeVar
88

99
import attrs
1010

@@ -14,7 +14,7 @@
1414
import types
1515
from collections.abc import Callable
1616

17-
from typing_extensions import TypeGuard
17+
from typing_extensions import Self, TypeGuard
1818
# In ordinary single-threaded Python code, when you hit control-C, it raises
1919
# an exception and automatically does all the regular unwinding stuff.
2020
#
@@ -77,10 +77,74 @@
7777
# for any Python program that's written to catch and ignore
7878
# KeyboardInterrupt.)
7979

80-
_CODE_KI_PROTECTION_STATUS_WMAP: weakref.WeakKeyDictionary[
80+
_T = TypeVar("_T")
81+
82+
83+
class _IdRef(weakref.ref[_T]):
84+
slots = "_hash"
85+
_hash: int
86+
87+
def __new__(cls, ob: _T, callback: Callable[[Self], Any] | None = None, /) -> Self:
88+
self: Self = weakref.ref.__new__(cls, ob, callback)
89+
self._hash = object.__hash__(ob)
90+
return self
91+
92+
def __eq__(self, other: object) -> bool:
93+
if self is other:
94+
return True
95+
96+
if not isinstance(other, _IdRef):
97+
return NotImplemented
98+
99+
my_obj = None
100+
other_obj: Any = None
101+
try:
102+
my_obj = self()
103+
other_obj = other()
104+
return my_obj is not None and other_obj is not None and my_obj is other_obj
105+
finally:
106+
del my_obj, other_obj
107+
108+
def __hash__(self) -> int:
109+
return self._hash
110+
111+
112+
_KT = TypeVar("_KT")
113+
_VT = TypeVar("_VT")
114+
115+
116+
class WeakKeyIdentityDictionary(Generic[_KT, _VT]):
117+
def __init__(self) -> None:
118+
self._data: dict[_IdRef[_KT], _VT] = {}
119+
120+
def remove(
121+
k: _IdRef[_KT],
122+
selfref: weakref.ref[
123+
WeakKeyIdentityDictionary[_KT, _VT]
124+
] = weakref.ref( # noqa: B008 # function-call-in-default-argument
125+
self,
126+
),
127+
) -> None:
128+
self = selfref()
129+
if self is not None:
130+
try: # noqa: SIM105 # supressible-exception
131+
del self._data[k]
132+
except KeyError:
133+
pass
134+
135+
self._remove = remove
136+
137+
def __getitem__(self, k: _KT) -> _VT:
138+
return self._data[_IdRef(k)]
139+
140+
def __setitem__(self, k: _KT, v: _VT) -> None:
141+
self._data[_IdRef(k, self._remove)] = v
142+
143+
144+
_CODE_KI_PROTECTION_STATUS_WMAP: WeakKeyIdentityDictionary[
81145
types.CodeType,
82146
bool,
83-
] = weakref.WeakKeyDictionary()
147+
] = WeakKeyIdentityDictionary()
84148

85149

86150
# This is to support the async_generator package necessary for aclosing on <3.10

0 commit comments

Comments
 (0)