From b0e700d96238081b2e8879a236c81a5ec8f3435a Mon Sep 17 00:00:00 2001 From: aryansk <70511529+aryansk@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:33:35 +0530 Subject: [PATCH] Fix circular import between threading and _threading_local 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/cpython#156341 Co-authored-by: Muse Spark 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> --- Lib/_threading_local.py | 6 +++--- Lib/threading.py | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Lib/_threading_local.py b/Lib/_threading_local.py index 2af3885458b54f2..9b16979c7cb4187 100644 --- a/Lib/_threading_local.py +++ b/Lib/_threading_local.py @@ -36,11 +36,13 @@ def __init__(self): def get_dict(self): """Return the dict for the current thread. Raises KeyError if none defined.""" + from threading import current_thread thread = current_thread() return self.dicts[id(thread)][1] def create_dict(self): """Create a new dict for the current thread, and return it.""" + from threading import current_thread localdict = {} key = self.key thread = current_thread() @@ -85,6 +87,7 @@ class local: def __new__(cls, /, *args, **kw): if (args or kw) and (cls.__init__ is object.__init__): raise TypeError("Initialization arguments are not supported") + from threading import RLock self = object.__new__(cls) impl = _localimpl() impl.localargs = (args, kw) @@ -115,6 +118,3 @@ def __delattr__(self, name): % self.__class__.__name__) with _patch(self): return object.__delattr__(self, name) - - -from threading import current_thread, RLock diff --git a/Lib/threading.py b/Lib/threading.py index abac31e25886fae..5e54ee2087e73d5 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -61,13 +61,11 @@ TIMEOUT_MAX = _thread.TIMEOUT_MAX del _thread -# get thread-local implementation, either from the thread -# module, or from the python fallback - -try: - from _thread import _local as local -except ImportError: - from _threading_local import local +# get thread-local implementation from the thread module +# (fallback to _threading_local is obsolete since Python 3.7 - thread support +# is always available, and _thread._local always exists; keeping the fallback +# would reintroduce the circular import with _threading_local) +from _thread import _local as local # Support for profile and trace hooks