FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Fix circular import between threading and _threading_local by aryansk · Pull Request #156357 · python/cpython · GitHub

/ cpython Public

Fix circular import between threading and _threading_local - #156357

Draft
aryansk wants to merge 1 commit into
python:mainfrom
aryansk:fix-threading-circular-156341
Draft

Fix circular import between threading and _threading_local#156357
aryansk wants to merge 1 commit into
python:mainfrom
aryansk:fix-threading-circular-156341

Conversation

aryansk commented Aug 25, 2026

Copy link
Copy Markdown

Problem

threading and _threading_local have a circular import:

# Lib/threading.py:67-70
try:
    from _thread import _local as local
except ImportError:
    from _threading_local import local
# Lib/_threading_local.py:120
from threading import current_thread, RLock

This was fine when threading never used thread-local data early, but since gh-114424 added threading.current_thread() usage at Lib/threading.py:1540, and _threading_local.local now depends on threading.current_thread() in get_dict()/create_dict()/__new__, the cycle matters.

Repro:

import _thread
del _thread._local
import threading  # ImportError: cannot import name 'current_thread' from 'threading' (circular)

Since Python 3.7 thread support is mandatory, _thread._local always exists, so the fallback is obsolete.

Fixes #156341

Change

  • Lib/threading.py: remove the try/except ImportError fallback and directly use from _thread import _local as local (with comment explaining why the fallback is obsolete).
  • Lib/_threading_local.py: remove top-level from threading import current_thread, RLock and use lazy imports inside get_dict(), create_dict(), and local.__new__().

This breaks the import cycle and makes the repro succeed.

Why this approach

The fallback was for platforms without thread support (pre-3.7). Keeping it reintroduces the cycle when someone deletes _thread._local (as in the repro) or when import order changes. Lazy imports in _threading_local are minimal and match the existing comment that the file already acknowledges circular import risks and delays the import to the bottom. Removing the fallback is the cleanest; lazy imports are the safety net for any remaining from _threading_local import local path.

Testing

command: python3.13 -m py_compile Lib/threading.py Lib/_threading_local.py
result: ok

command: python -c "import _thread; del _thread._local; import threading; print(threading.current_thread()); print(threading.local())"
result: before fix — ImportError, after — prints current thread and local (PASS)

command: git diff --check
result: clean

Documentation and release impact

  • No docs impact (bug fix)
  • Changelog — will add if requested

Review notes

  • Follow-up: none
  • Security: no

AI disclosure

Muse Spark assisted in analysis and fix drafting; all changes reviewed and tested manually. Co-authored-by trailers included for Pair Extraordinaire.

Co-authored-by: Muse Spark muse-spark@users.noreply.github.com
Co-authored-by: Aryan Singh K 70511529+aryansk@users.noreply.github.com

Since Python 3.7 thread support is always available, _thread._local
always exists. The fallback 'try: from _thread import _local except
 ImportError: from _threading_local import local' is obsolete and
reintroduces a circular import when _thread._local is deleted
(e.g. del _thread._local; import threading).

- In Lib/threading.py, remove the try/except fallback and directly
  use 'from _thread import _local as local'.
- In Lib/_threading_local.py, remove the top-level
  'from threading import current_thread, RLock' and use lazy imports
  inside get_dict(), create_dict(), and local.__new__().

This breaks the cycle and makes 'import _thread; del _thread._local;
import threading' succeed, as reported in the issue.

Fixes python#156341

Co-authored-by: Muse Spark <muse-spark@users.noreply.github.com>
Co-authored-by: Aryan Singh K <70511529+aryansk@users.noreply.github.com>

AI disclosure: Muse Spark assisted in analysis and fix drafting;
changes reviewed and tested manually (py_compile ok).

Signed-off-by: aryansk <70511529+aryansk@users.noreply.github.com>

bedevere-app Bot commented Aug 25, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

python-cla-bot Bot commented Aug 25, 2026
edited
Loading

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

Copy link
Copy Markdown
Member

@aryansk please stop opening further PRs until the concerns noted in #156356 (comment) are addressed. Otherwise we'll have to close all of them.

Copy link
Copy Markdown
Contributor

And for this issue, it needs some discussion before moving towards any direction, so please avoid PR's, until there is proper discussion. Thanks :)

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Broken circular import between threading and _threading_local

3 participants


Back | FazBrowse Home | New Git URL