[3.6] bpo-29694: race condition in pathlib mkdir with flags parents=T… · python/cpython@cbc46af (original) (raw)

`@@ -8,6 +8,7 @@

`

8

8

`import stat

`

9

9

`import tempfile

`

10

10

`import unittest

`

``

11

`+

from unittest import mock

`

11

12

``

12

13

`from test import support

`

13

14

`android_not_root = support.android_not_root

`

`@@ -1816,6 +1817,35 @@ def test_mkdir_no_parents_file(self):

`

1816

1817

`p.mkdir(exist_ok=True)

`

1817

1818

`self.assertEqual(cm.exception.errno, errno.EEXIST)

`

1818

1819

``

``

1820

`+

def test_mkdir_concurrent_parent_creation(self):

`

``

1821

`+

for pattern_num in range(32):

`

``

1822

`+

p = self.cls(BASE, 'dirCPC%d' % pattern_num)

`

``

1823

`+

self.assertFalse(p.exists())

`

``

1824

+

``

1825

`+

def my_mkdir(path, mode=0o777):

`

``

1826

`+

path = str(path)

`

``

1827

`+

Emulate another process that would create the directory

`

``

1828

`+

just before we try to create it ourselves. We do it

`

``

1829

`+

in all possible pattern combinations, assuming that this

`

``

1830

`+

function is called at most 5 times (dirCPC/dir1/dir2,

`

``

1831

`+

dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2).

`

``

1832

`+

if pattern.pop():

`

``

1833

`+

os.mkdir(path, mode) # from another process

`

``

1834

`+

concurrently_created.add(path)

`

``

1835

`+

os.mkdir(path, mode) # our real call

`

``

1836

+

``

1837

`+

pattern = [bool(pattern_num & (1 << n)) for n in range(5)]

`

``

1838

`+

concurrently_created = set()

`

``

1839

`+

p12 = p / 'dir1' / 'dir2'

`

``

1840

`+

try:

`

``

1841

`+

with mock.patch("pathlib._normal_accessor.mkdir", my_mkdir):

`

``

1842

`+

p12.mkdir(parents=True, exist_ok=False)

`

``

1843

`+

except FileExistsError:

`

``

1844

`+

self.assertIn(str(p12), concurrently_created)

`

``

1845

`+

else:

`

``

1846

`+

self.assertNotIn(str(p12), concurrently_created)

`

``

1847

`+

self.assertTrue(p.exists())

`

``

1848

+

1819

1849

`@with_symlinks

`

1820

1850

`def test_symlink_to(self):

`

1821

1851

`P = self.cls(BASE)

`