Skip to content

Commit 4bdeb90

Browse files
author
Mikhail Koviazin
committed
connection: add a pointer to os.getpid to ConnectionPool
This commit adds a local pointer to `os.getpid` to `ConnectionPool` class to mitigate the `AttributeError` that occurs on Python 3.13+ on interpreter exit. This is caused by the fact that interpreter calls `__del__` on variables on exit, but `os` is being destroyed before the `valkey.Valkey` instance. It could be easily reproduced with: >>> import valkey >>> r = valkey.Valkey(host="localhost", port=6379) >>> exit() Exception ignored in: <function Valkey.__del__ at 0x7f29d084e5c0> Traceback (most recent call last): File "/home/mikhail.koviazin/workspace/valkey-py-clean/valkey/client.py", line 521, in __del__ File "/home/mikhail.koviazin/workspace/valkey-py-clean/valkey/client.py", line 536, in close File "/home/mikhail.koviazin/workspace/valkey-py-clean/valkey/connection.py", line 1179, in disconnect File "/home/mikhail.koviazin/workspace/valkey-py-clean/valkey/connection.py", line 1083, in _checkpid TypeError: 'NoneType' object is not callable Having a local pointer to that function keeps `os.getpid()` accessible during interpreter shutdown. Fixes #158 Signed-off-by: Mikhail Koviazin <mikhail.koviazin@aiven.io>
1 parent ebdf9c9 commit 4bdeb90

File tree

1 file changed

+16
-2
lines changed

1 file changed

+16
-2
lines changed

valkey/connection.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,20 @@ def __init__(
10111011
self.connection_kwargs = connection_kwargs
10121012
self.max_connections = max_connections
10131013

1014+
# We need to preserve the pointer to os.getpid because Valkey class
1015+
# contains a __del__ method that causes the call chain:
1016+
# 1. Valkey.close()
1017+
# 2. ConnectionPool.disconnect()
1018+
# 3. ConnectionPool._checkpid()
1019+
# 4. os.getpid()
1020+
#
1021+
# If os.getpid is garbage collected before Valkey, then the __del__
1022+
# method will raise an AttributeError when trying to call os.getpid.
1023+
# It wasn't an issue in practice until Python REPL was reworked in 3.13
1024+
# to collect all globals at the end of the session, which caused
1025+
# os.getpid to be garbage collected before Valkey.
1026+
self._getpid = os.getpid
1027+
10141028
# a lock to protect the critical section in _checkpid().
10151029
# this lock is acquired when the process id changes, such as
10161030
# after a fork. during this time, multiple threads in the child
@@ -1080,14 +1094,14 @@ def _checkpid(self) -> None:
10801094
# seconds to acquire _fork_lock. if _fork_lock cannot be acquired in
10811095
# that time it is assumed that the child is deadlocked and a
10821096
# valkey.ChildDeadlockedError error is raised.
1083-
if self.pid != os.getpid():
1097+
if self.pid != self._getpid():
10841098
acquired = self._fork_lock.acquire(timeout=5)
10851099
if not acquired:
10861100
raise ChildDeadlockedError
10871101
# reset() the instance for the new process if another thread
10881102
# hasn't already done so
10891103
try:
1090-
if self.pid != os.getpid():
1104+
if self.pid != self._getpid():
10911105
self.reset()
10921106
finally:
10931107
self._fork_lock.release()

0 commit comments

Comments
 (0)