python / python/cpython

Introduction of Thread._handle: _thread._ThreadHandle in Python3.13 has broken an unknown number of packages

オープン
#132,578 コメント 11 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

3.13 3.14 easy stdlib type-bug
主要言語
Python
スター
77.2k
フォーク
35.9k
PR マージ指標
PR 指標を取得中

説明

Bug report

Bug description:

To open with- Python is great, probably the language I have written the most code in; super excited for the progressing into no-GIL, at my day job we've built a massive Python system and we're hopeful that no-GIL (at least for parts of it) will help with a bunch of perf issues that we're presently either enduring or have built complexity to work around.

Onto the issue; in the code of a number now-broken (since Python 3.13) packages (if you Google around for TypeError: '_thread._ThreadHandle' object is not callable you'll get a feel for it), we'll likely see something roughly like this (I believe this is a bit of a common pattern):

class SomeThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.stopped = threading.Event()    

    def _handle(self):
        some_logic_here()

    def run(self):
        while not self.stopped.is_set():
            self._handle()

However, since Python 3.13, attempts to use this pattern result in failures like like this:

Traceback (most recent call last):
  File "/Users/edwardbeech/.venv/ims-mono-py3.13/lib/python3.13/site-packages/ims_utils/utils/thread_utils.py", line 208, in _do_tasks_and_return_duration
    self.tasks()
    ~~~~~~~~~~^^
  File "/Users/edwardbeech/Projects/FTP/ims-mono/wireless-manager-back-end/other/tcp/tcp_socket.py", line 111, in tasks
    self._handle()
    ~~~~~~~~~~~~^^
TypeError: '_thread._ThreadHandle' object is not callable

I believe this is because of the introduction of this code in threading.py; truncated excerpt:

class Thread:
    ....
    def __init__(self, group=None, target=None, name=None,
        ...
        self._handle = _ThreadHandle()

Now, for sure- arguably this pattern is problematic in general because threading.Thread has a bunch of sunder-private properties that this pattern risks clobbering and probably the right thing to do is to use threading.Thread.__init__(target=your_run_func)- but if we consider Hyrum's Law (relevant xkcd) we could re-frame this such that prior to Python 3.13, everyone's usage of threading.Thread was in-keeping with the "interface" (if you can call it that) supported by threading.Thread (despite the dubious and accidental "swiss cheese symbiosis" of property naming) and from Python 3.13 onwards, we've broken that interface for people.

I don't know enough about internal Python development to know if there's a strategy with these sorts of things e.g. perhaps we're happy to break userspace and folks must simply revise their libraries (and if so, then that's fine, I'm happy with that answer).

If there is no such strategy though and we're more focused on interoperation across versions, I think an easy fix here would be to just change to be a dunder property instead of a sunder property and call it a day- I would probably warn against changing all the other existing sunder properties to dunder properties though because again, who knows who is relying on them in some odd way as an interface lol.

Here's some Docker-based testing I was doing; probably not really needed to paint the picture:

docker run --rm -it --entrypoint python3 python:3.12 -c 'from threading import Thread; t = Thread(); print(repr(t._handle)) if hasattr(t, "_handle") else print("property does not exist")'
property does not exist

docker run --rm -it --entrypoint python3 python:3.13 -c 'from threading import Thread; t = Thread(); print(repr(t._handle)) if hasattr(t, "_handle") else print("property does not exist")'
<_thread._ThreadHandle object: ident=0>

docker run --rm -it --entrypoint python3 python:3.14.0a7 -c 'from threading import Thread; t = Thread(); print(repr(t._handle)) if hasattr(t, "_handle") else print("property does not exist")'
<_thread._ThreadHandle object: ident=0>
CPython versions tested on:

3.13

Operating systems tested on:

macOS

Linked PRs
  • gh-132696
  • gh-132789
  • gh-150322

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

Lib/threading.py の、引用されている Thread.init の代入から始め、Python 3.12、3.13、3.14 の Docker コマンドで動作を再現してください。リンクされている PR と議論を確認してください。完了には、_handle を定義するサブクラスについて合意された互換性の解決策があり、影響を受けるバージョン全体で動作が検証されていることが必要です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
operating-systems
issue の種類
バグ
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
25/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。