Title: Concurrent imports of a package and its submodule raise _DeadlockError in 3.14 and since 3.13.16 · Issue #160045 · python/cpython · GitHub
Open Graph Title: Concurrent imports of a package and its submodule raise _DeadlockError in 3.14 and since 3.13.16 · Issue #160045 · python/cpython
X Title: Concurrent imports of a package and its submodule raise _DeadlockError in 3.14 and since 3.13.16 · Issue #160045 · python/cpython
Description: Bug description: Since the fix for gh-130094 (GH-130101, backported to 3.13 in GH-155915 and first released in 3.13.16), _find_and_load_unlocked() re-imports a parent package whose spec is still _initializing, and it does so while holdin...
Open Graph Description: Bug description: Since the fix for gh-130094 (GH-130101, backported to 3.13 in GH-155915 and first released in 3.13.16), _find_and_load_unlocked() re-imports a parent package whose spec is still _i...
X Description: Bug description: Since the fix for gh-130094 (GH-130101, backported to 3.13 in GH-155915 and first released in 3.13.16), _find_and_load_unlocked() re-imports a parent package whose spec is still _i...
Opengraph URL: https://github.com/python/cpython/issues/160045
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Concurrent imports of a package and its submodule raise _DeadlockError in 3.14 and since 3.13.16","articleBody":"\n### Bug description:\n\nSince the fix for gh-130094 (GH-130101, backported to 3.13 in GH-155915 and first released in 3.13.16), `_find_and_load_unlocked()` re-imports a parent package whose spec is still `_initializing`, and it does so while holding the child module's lock. That creates a lock-order inversion between two common imports that worked before:\n\n- Thread A runs `import pkg`. It holds `pkg`'s module lock while `pkg/__init__.py` runs, and that file does `from .sub import X`, so A then needs the lock for `pkg.sub`.\n- Thread B runs `import pkg.sub` while `pkg` is still initializing. It takes the `pkg.sub` lock, sees `pkg.__spec__._initializing`, and now waits for `pkg`'s lock.\n\nimportlib detects the cycle and raises `_DeadlockError` in thread A. Up to 3.13.15, thread B loaded `pkg.sub` without waiting for the parent, so both imports succeeded. There is no import cycle in user code, and the same imports work in either order on a single thread.\n\nReproducer (fails every time on 3.13.16 and 3.14.4; passes on 3.13.13):\n\n```python\nimport os, sys, tempfile, threading, time\n\nroot = tempfile.mkdtemp()\nos.makedirs(os.path.join(root, \"pkg\"))\nwith open(os.path.join(root, \"pkg\", \"__init__.py\"), \"w\") as f:\n f.write(\"import time\\ntime.sleep(0.5)\\nfrom .sub import X\\n\")\nwith open(os.path.join(root, \"pkg\", \"sub.py\"), \"w\") as f:\n f.write(\"X = 1\\n\")\nsys.path.insert(0, root)\n\nerrors = []\n\ndef run(name):\n try:\n __import__(name)\n except BaseException as e:\n errors.append(f\"{name}: {type(e).__name__}: {e}\")\n\na = threading.Thread(target=run, args=(\"pkg\",))\nb = threading.Thread(target=run, args=(\"pkg.sub\",))\na.start()\ntime.sleep(0.1) # pkg is now initializing; thread a holds its module lock\nb.start()\na.join(); b.join()\nprint(sys.version.split()[0], \"->\", errors or \"OK\")\n```\n\n```\n3.13.13 -> OK\n3.13.16 -> [\"pkg: _DeadlockError: deadlock detected by _ModuleLock('pkg.sub') at 0x...\"]\n3.14.4 -> [\"pkg: _DeadlockError: deadlock detected by _ModuleLock('pkg.sub') at 0x...\"]\n```\n\nRemoving only the new block below from 3.13.16's `_find_and_load_unlocked()` makes the reproducer pass. The `meta_path = list(meta_path)` part of the same change has no effect on it:\n\n```python\n if getattr(parent_spec, '_initializing', False):\n _call_with_frames_removed(import_, parent)\n # Crazy side-effects (again)!\n module = sys.modules.get(name)\n if module is not None:\n return module\n```\n\nReal-world impact: any package whose `__init__` imports its own submodules is affected (`requests`, `prometheus_client`, and so on) as soon as two threads import it and one of its submodules for the first time at once. The Datadog Agent loads Python checks from several threads and hit this right after moving to 3.13.16. Checks failed at startup with `_DeadlockError: deadlock detected by _ModuleLock('requests.exceptions')`, so we went back to 3.13.15 (DataDog/datadog-agent#57902). Other projects report the same error after upgrading to 3.13.16 or 3.14, for example gke-labs/kube-agents#2446, dgunning/edgartools#1325 and marimo-team/marimo#11095.\n\nBecause this is a regression in a bugfix release, could the 3.13 backport be reverted, or this part changed so the thread doesn't wait for the parent's lock while holding the child's? The same code is on 3.14 and `main`.\n\ngh-159963 is related but separate: it covers a 3.15-only change from gh-83065. This report is about the gh-130094 change that is already in 3.13.16 and 3.14.\n\n### CPython versions tested on:\n\n3.13, 3.14\n\n### Operating systems tested on:\n\nmacOS\n","author":{"url":"https://github.com/Kyle-Neale","@type":"Person","name":"Kyle-Neale"},"datePublished":"2026-10-21T15:31:11Z","url":"https://github.com/python/cpython/issues/160045"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:a7a53a38-515f-809c-7c31-b2f5fe6ad311 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | D31C:210678:679565:8BC6D0:6AED2536 |
| html-safe-nonce | 00d791843e4f09e15fc73e803201aed1421810d931c94d950da9cefd62443b6c |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEMzFDOjIxMDY3ODo2Nzk1NjU6OEJDNkQwOjZBRUQyNTM2IiwidmlzaXRvcl9pZCI6IjUwNjgxODU2MjE1MjgxMjY3NzQiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 2be17ca9ea24aee43e7a1c129a2fe2e16747d505fba83c7841d96dccaf460636 |
| hovercard-subject-tag | issue:5963529457 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/python/cpython/160045/issue_layout |
| twitter:image | https://opengraph.githubassets.com/0a0e134e4ed676516f05a64b1065fff5432b1030202b99d8012c3e84fcdec3df/python/cpython/issues/160045 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/0a0e134e4ed676516f05a64b1065fff5432b1030202b99d8012c3e84fcdec3df/python/cpython/issues/160045 |
| og:image:alt | Bug description: Since the fix for gh-130094 (GH-130101, backported to 3.13 in GH-155915 and first released in 3.13.16), _find_and_load_unlocked() re-imports a parent package whose spec is still _i... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | Kyle-Neale |
| hostname | github.com |
| expected-hostname | github.com |
| None | 9d95365dda0576efe9b71f45807595e396bd0ca9286fba5367a05048e6ddf8f2 |
| turbo-cache-control | no-preview |
| go-import | github.com/python/cpython git https://github.com/python/cpython.git |
| octolytics-dimension-user_id | 1525981 |
| octolytics-dimension-user_login | python |
| octolytics-dimension-repository_id | 81598961 |
| octolytics-dimension-repository_nwo | python/cpython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 81598961 |
| octolytics-dimension-repository_network_root_nwo | python/cpython |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 98b41fa4b5635ceb45a4c54320fced73817ff290 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width