Skip to content

Commit 6ac2278

Browse files
committed
Address CR
1 parent 8e61b79 commit 6ac2278

File tree

3 files changed

+20
-20
lines changed

3 files changed

+20
-20
lines changed

mypy/test/testutil.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@
1414

1515

1616
def lock_file(filename: str, duration: float) -> Thread:
17-
'''
18-
Opens filename (which must exist) for reading
19-
After duration sec, releases the handle
20-
'''
17+
"""Open a file and keep it open in a background thread for duration sec."""
2118
def _lock_file() -> None:
2219
with open(filename):
2320
time.sleep(duration)
@@ -28,7 +25,6 @@ def _lock_file() -> None:
2825

2926
@skipUnless(WIN32, "only relevant for Windows")
3027
class ReliableReplace(TestCase):
31-
# will be cleaned up automatically when this class goes out of scope
3228
tmpdir = tempfile.TemporaryDirectory(prefix='mypy-test-',
3329
dir=os.path.abspath('tmp-test-dirs'))
3430
timeout = 1
@@ -39,12 +35,15 @@ class ReliableReplace(TestCase):
3935

4036
@classmethod
4137
def tearDownClass(cls) -> None:
38+
# Need to wait for threads to complete, otherwise we'll get PermissionError
39+
# at the end (whether tmpdir goes out of scope or we explicitly call cleanup).
4240
for t in cls.threads:
4341
t.join()
42+
cls.tmpdir.cleanup()
4443

4544
def prepare_src_dest(self, src_lock_duration: float, dest_lock_duration: float
4645
) -> Tuple[str, str]:
47-
# create two temporary files
46+
# Create two temporary files with random names.
4847
src = os.path.join(self.tmpdir.name, random_string())
4948
dest = os.path.join(self.tmpdir.name, random_string())
5049

@@ -63,16 +62,16 @@ def replace_ok(self, src_lock_duration: float, dest_lock_duration: float,
6362
self.assertEqual(open(dest).read(), src, 'replace failed')
6463

6564
def test_everything(self) -> None:
66-
# normal use, no locks
65+
# No files locked.
6766
self.replace_ok(0, 0, self.timeout)
68-
# make sure the problem is real
67+
# Make sure we can reproduce the issue with our setup.
6968
src, dest = self.prepare_src_dest(self.short_lock, 0)
7069
with self.assertRaises(PermissionError):
7170
os.replace(src, dest)
72-
# short lock
71+
# Lock files for a time short enough that util.replace won't timeout.
7372
self.replace_ok(self.short_lock, 0, self.timeout)
7473
self.replace_ok(0, self.short_lock, self.timeout)
75-
# long lock
74+
# Lock files for a time long enough that util.replace times out.
7675
with self.assertRaises(PermissionError):
7776
self.replace_ok(self.long_lock, 0, self.timeout)
7877
with self.assertRaises(PermissionError):

mypy/util.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,21 +143,22 @@ def id(self, o: object) -> int:
143143

144144

145145
if sys.version_info >= (3, 6):
146-
PathType = Union[AnyStr, os.PathLike]
146+
PathType = Union[AnyStr, 'os.PathLike[AnyStr]']
147147
else:
148148
PathType = AnyStr
149149

150150

151-
def _replace(src: PathType, dest: PathType, timeout: float = 10) -> None:
152-
'''
153-
Replace src with dest using os.replace(src, dest)
154-
Wait and retry if OSError exception is raised
155-
Increase wait time exponentially until total wait of timeout sec
156-
On timeout, give up and reraise the last exception seen
157-
'''
151+
def _replace(src: PathType[AnyStr], dest: PathType[AnyStr], timeout: float = 1) -> None:
152+
"""Replace src with dest using os.replace(src, dest).
153+
154+
Wait and retry if OSError exception is raised.
155+
156+
Increase wait time exponentially until total wait of timeout sec;
157+
on timeout, give up and reraise the last exception seen.
158+
"""
158159
n_iter = max(1, math.ceil(math.log2(timeout / 0.001)))
159160
for i in range(n_iter):
160-
# last wait is ~ timeout/2, so that total wait ~ timeout
161+
# Last wait is ~ timeout/2, so that total wait ~ timeout.
161162
wait = timeout / 2 ** (n_iter - i - 1)
162163
try:
163164
os.replace(src, dest)

0 commit comments

Comments
 (0)