https://github.com/python/cpython/commit/d9b4eef71e7904fbe3a3786a908e493be7debbff
commit: d9b4eef71e7904fbe3a3786a908e493be7debbff
branch: main
author: Sam Gross <[email protected]>
committer: colesbury <[email protected]>
date: 2025-10-10T17:20:18-04:00
summary:

gh-139001: Fix thread-safety issue in `pathlib.Path` (gh-139066)

Don't cache the joined path in `_raw_path` because the caching isn't thread 
safe.

files:
A Misc/NEWS.d/next/Library/2025-09-17-12-07-21.gh-issue-139001.O6tseN.rst
M Lib/pathlib/__init__.py
M Lib/test/test_pathlib/test_join.py

diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py
index 6c07cd9ab010ad..51359cec8b0f9e 100644
--- a/Lib/pathlib/__init__.py
+++ b/Lib/pathlib/__init__.py
@@ -336,13 +336,8 @@ def _raw_path(self):
             return paths[0]
         elif paths:
             # Join path segments from the initializer.
-            path = self.parser.join(*paths)
-            # Cache the joined path.
-            paths.clear()
-            paths.append(path)
-            return path
+            return self.parser.join(*paths)
         else:
-            paths.append('')
             return ''
 
     @property
diff --git a/Lib/test/test_pathlib/test_join.py 
b/Lib/test/test_pathlib/test_join.py
index 2f4e79345f3652..4630210e492291 100644
--- a/Lib/test/test_pathlib/test_join.py
+++ b/Lib/test/test_pathlib/test_join.py
@@ -3,6 +3,8 @@
 """
 
 import unittest
+import threading
+from test.support import threading_helper
 
 from .support import is_pypi
 from .support.lexical_path import LexicalPath
@@ -158,6 +160,26 @@ def test_parts(self):
         parts = p.parts
         self.assertEqual(parts, (sep, 'a', 'b'))
 
+    @threading_helper.requires_working_threading()
+    def test_parts_multithreaded(self):
+        P = self.cls
+
+        NUM_THREADS = 10
+        NUM_ITERS = 10
+
+        for _ in range(NUM_ITERS):
+            b = threading.Barrier(NUM_THREADS)
+            path = P('a') / 'b' / 'c' / 'd' / 'e'
+            expected = ('a', 'b', 'c', 'd', 'e')
+
+            def check_parts():
+                b.wait()
+                self.assertEqual(path.parts, expected)
+
+            threads = [threading.Thread(target=check_parts) for _ in 
range(NUM_THREADS)]
+            with threading_helper.start_threads(threads):
+                pass
+
     def test_parent(self):
         # Relative
         P = self.cls
diff --git 
a/Misc/NEWS.d/next/Library/2025-09-17-12-07-21.gh-issue-139001.O6tseN.rst 
b/Misc/NEWS.d/next/Library/2025-09-17-12-07-21.gh-issue-139001.O6tseN.rst
new file mode 100644
index 00000000000000..3ad5a1272df4af
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-09-17-12-07-21.gh-issue-139001.O6tseN.rst
@@ -0,0 +1,2 @@
+Fix race condition in :class:`pathlib.Path` on the internal ``_raw_paths``
+field.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to