Skip to content

Commit db78893

Browse files
authored
Merge pull request RustPython#3547 from fanninpm/test-support-os-helper
Align test/support/os_helper with CPython 3.10
2 parents 2a39dd0 + 54ae3bd commit db78893

9 files changed

Lines changed: 43 additions & 165 deletions

File tree

Lib/test/support/__init__.py

Lines changed: 1 addition & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,6 @@
8383
# io
8484
"record_original_stdout", "get_original_stdout", "captured_stdout",
8585
"captured_stdin", "captured_stderr",
86-
# filesystem
87-
"TESTFN", "SAVEDCWD", "unlink", "rmtree", "temp_cwd", "findfile",
88-
"create_empty_file", "can_symlink", "fs_is_case_insensitive",
8986
# unittest
9087
"is_resource_enabled", "requires", "requires_freebsd_version",
9188
"requires_linux_version", "requires_mac_ver",
@@ -279,135 +276,6 @@ def _force_run(path, func, *args):
279276
os.chmod(path, stat.S_IRWXU)
280277
return func(*args)
281278

282-
if sys.platform.startswith("win"):
283-
def _waitfor(func, pathname, waitall=False):
284-
# Perform the operation
285-
func(pathname)
286-
# Now setup the wait loop
287-
if waitall:
288-
dirname = pathname
289-
else:
290-
dirname, name = os.path.split(pathname)
291-
dirname = dirname or '.'
292-
# Check for `pathname` to be removed from the filesystem.
293-
# The exponential backoff of the timeout amounts to a total
294-
# of ~1 second after which the deletion is probably an error
295-
# anyway.
296-
# Testing on an i7@4.3GHz shows that usually only 1 iteration is
297-
# required when contention occurs.
298-
timeout = 0.001
299-
while timeout < 1.0:
300-
# Note we are only testing for the existence of the file(s) in
301-
# the contents of the directory regardless of any security or
302-
# access rights. If we have made it this far, we have sufficient
303-
# permissions to do that much using Python's equivalent of the
304-
# Windows API FindFirstFile.
305-
# Other Windows APIs can fail or give incorrect results when
306-
# dealing with files that are pending deletion.
307-
L = os.listdir(dirname)
308-
if not (L if waitall else name in L):
309-
return
310-
# Increase the timeout and try again
311-
time.sleep(timeout)
312-
timeout *= 2
313-
warnings.warn('tests may fail, delete still pending for ' + pathname,
314-
RuntimeWarning, stacklevel=4)
315-
316-
def _unlink(filename):
317-
# XXX RUSTPYTHON: on ci, unlink() raises PermissionError when target doesn't exist.
318-
# Might also happen locally, but not sure
319-
if not os.path.exists(filename):
320-
return
321-
_waitfor(os.unlink, filename)
322-
323-
def _rmdir(dirname):
324-
# XXX RUSTPYTHON: on ci, unlink() raises PermissionError when target doesn't exist.
325-
# Might also happen locally, but not sure
326-
if not os.path.exists(dirname):
327-
return
328-
_waitfor(os.rmdir, dirname)
329-
330-
def _rmtree(path):
331-
# XXX RUSTPYTHON: on ci, unlink() raises PermissionError when target doesn't exist.
332-
# Might also happen locally, but not sure
333-
if not os.path.exists(path):
334-
return
335-
def _rmtree_inner(path):
336-
for name in _force_run(path, os.listdir, path):
337-
fullname = os.path.join(path, name)
338-
try:
339-
mode = os.lstat(fullname).st_mode
340-
except OSError as exc:
341-
print("os_helper.rmtree(): os.lstat(%r) failed with %s" % (fullname, exc),
342-
file=sys.__stderr__)
343-
mode = 0
344-
if stat.S_ISDIR(mode):
345-
_waitfor(_rmtree_inner, fullname, waitall=True)
346-
_force_run(fullname, os.rmdir, fullname)
347-
else:
348-
_force_run(fullname, os.unlink, fullname)
349-
_waitfor(_rmtree_inner, path, waitall=True)
350-
_waitfor(lambda p: _force_run(p, os.rmdir, p), path)
351-
352-
def _longpath(path):
353-
try:
354-
import ctypes
355-
except ImportError:
356-
# No ctypes means we can't expands paths.
357-
pass
358-
else:
359-
buffer = ctypes.create_unicode_buffer(len(path) * 2)
360-
length = ctypes.windll.kernel32.GetLongPathNameW(path, buffer,
361-
len(buffer))
362-
if length:
363-
return buffer[:length]
364-
return path
365-
else:
366-
_unlink = os.unlink
367-
_rmdir = os.rmdir
368-
369-
def _rmtree(path):
370-
try:
371-
shutil.rmtree(path)
372-
return
373-
except OSError:
374-
pass
375-
376-
def _rmtree_inner(path):
377-
for name in _force_run(path, os.listdir, path):
378-
fullname = os.path.join(path, name)
379-
try:
380-
mode = os.lstat(fullname).st_mode
381-
except OSError:
382-
mode = 0
383-
if stat.S_ISDIR(mode):
384-
_rmtree_inner(fullname)
385-
_force_run(path, os.rmdir, fullname)
386-
else:
387-
_force_run(path, os.unlink, fullname)
388-
_rmtree_inner(path)
389-
os.rmdir(path)
390-
391-
def _longpath(path):
392-
return path
393-
394-
def unlink(filename):
395-
try:
396-
_unlink(filename)
397-
except (FileNotFoundError, NotADirectoryError):
398-
pass
399-
400-
def rmdir(dirname):
401-
try:
402-
_rmdir(dirname)
403-
except FileNotFoundError:
404-
pass
405-
406-
def rmtree(path):
407-
try:
408-
_rmtree(path)
409-
except FileNotFoundError:
410-
pass
411279

412280
# Check whether a gui is actually available
413281
def _is_gui_available():
@@ -2245,6 +2113,7 @@ def skip_unless_bind_unix_socket(test):
22452113
except OSError as e:
22462114
_bind_nix_socket_error = e
22472115
finally:
2116+
from .os_helper import unlink
22482117
unlink(path)
22492118
if _bind_nix_socket_error:
22502119
msg = 'Requires a functional unix bind(): %s' % _bind_nix_socket_error

Lib/test/support/os_helper.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ def _rmtree_inner(path):
302302
try:
303303
mode = os.lstat(fullname).st_mode
304304
except OSError as exc:
305-
print("os_helper.rmtree(): os.lstat(%r) failed with %s"
305+
print("support.rmtree(): os.lstat(%r) failed with %s"
306306
% (fullname, exc),
307307
file=sys.__stderr__)
308308
mode = 0
@@ -479,6 +479,17 @@ def create_empty_file(filename):
479479
os.close(fd)
480480

481481

482+
@contextlib.contextmanager
483+
def open_dir_fd(path):
484+
"""Open a file descriptor to a directory."""
485+
assert os.path.isdir(path)
486+
dir_fd = os.open(path, os.O_RDONLY)
487+
try:
488+
yield dir_fd
489+
finally:
490+
os.close(dir_fd)
491+
492+
482493
def fs_is_case_insensitive(directory):
483494
"""Detects if the file system for the specified directory
484495
is case-insensitive."""
@@ -623,10 +634,6 @@ def set(self, envvar, value):
623634
def unset(self, envvar):
624635
del self[envvar]
625636

626-
def copy(self):
627-
# We do what os.environ.copy() does.
628-
return dict(self)
629-
630637
def __enter__(self):
631638
return self
632639

Lib/test/test__osx_support.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import sys
99
import unittest
1010

11-
import test.support
1211
from test.support import os_helper
1312

1413
import _osx_support
@@ -40,9 +39,9 @@ def test__find_executable(self):
4039
if self.env['PATH']:
4140
self.env['PATH'] = self.env['PATH'] + ':'
4241
self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir)
43-
test.support.unlink(self.prog_name)
42+
os_helper.unlink(self.prog_name)
4443
self.assertIsNone(_osx_support._find_executable(self.prog_name))
45-
self.addCleanup(test.support.unlink, self.prog_name)
44+
self.addCleanup(os_helper.unlink, self.prog_name)
4645
with open(self.prog_name, 'w') as f:
4746
f.write("#!/bin/sh\n/bin/echo OK\n")
4847
os.chmod(self.prog_name, stat.S_IRWXU)
@@ -53,8 +52,8 @@ def test__read_output(self):
5352
if self.env['PATH']:
5453
self.env['PATH'] = self.env['PATH'] + ':'
5554
self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir)
56-
test.support.unlink(self.prog_name)
57-
self.addCleanup(test.support.unlink, self.prog_name)
55+
os_helper.unlink(self.prog_name)
56+
self.addCleanup(os_helper.unlink, self.prog_name)
5857
with open(self.prog_name, 'w') as f:
5958
f.write("#!/bin/sh\n/bin/echo ExpectedOutput\n")
6059
os.chmod(self.prog_name, stat.S_IRWXU)
@@ -144,8 +143,8 @@ def test__find_appropriate_compiler(self):
144143
suffix = (':' + self.env['PATH']) if self.env['PATH'] else ''
145144
self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix
146145
for c_name, c_output in compilers:
147-
test.support.unlink(c_name)
148-
self.addCleanup(test.support.unlink, c_name)
146+
os_helper.unlink(c_name)
147+
self.addCleanup(os_helper.unlink, c_name)
149148
with open(c_name, 'w') as f:
150149
f.write("#!/bin/sh\n/bin/echo " + c_output)
151150
os.chmod(c_name, stat.S_IRWXU)
@@ -224,8 +223,8 @@ def test__remove_unsupported_archs(self):
224223
suffix = (':' + self.env['PATH']) if self.env['PATH'] else ''
225224
self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix
226225
c_name = 'clang'
227-
test.support.unlink(c_name)
228-
self.addCleanup(test.support.unlink, c_name)
226+
os_helper.unlink(c_name)
227+
self.addCleanup(os_helper.unlink, c_name)
229228
# exit status 255 means no PPC support in this compiler chain
230229
with open(c_name, 'w') as f:
231230
f.write("#!/bin/sh\nexit 255")

Lib/test/test_genericpath.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import sys
88
import unittest
99
import warnings
10-
from test import support
1110
from test.support import os_helper
1211
from test.support.script_helper import assert_python_ok
1312
from test.support.os_helper import FakePath
@@ -186,7 +185,7 @@ def test_isdir(self):
186185
self.assertIs(self.pathmodule.isdir(filename), True)
187186
self.assertIs(self.pathmodule.isdir(bfilename), True)
188187
finally:
189-
support.rmdir(filename)
188+
os_helper.rmdir(filename)
190189

191190
def test_isfile(self):
192191
filename = os_helper.TESTFN
@@ -211,7 +210,7 @@ def test_isfile(self):
211210
self.assertIs(self.pathmodule.isfile(filename), False)
212211
self.assertIs(self.pathmodule.isfile(bfilename), False)
213212
finally:
214-
support.rmdir(filename)
213+
os_helper.rmdir(filename)
215214

216215
def test_samefile(self):
217216
file1 = os_helper.TESTFN
@@ -526,7 +525,8 @@ def test_nonascii_abspath(self):
526525

527526
def test_join_errors(self):
528527
# Check join() raises friendly TypeErrors.
529-
with support.check_warnings(('', BytesWarning), quiet=True):
528+
from .support import check_warnings
529+
with check_warnings(('', BytesWarning), quiet=True):
530530
errmsg = "Can't mix strings and bytes in path components"
531531
with self.assertRaisesRegex(TypeError, errmsg):
532532
self.pathmodule.join(b'bytes', 'str')
@@ -546,7 +546,8 @@ def test_join_errors(self):
546546

547547
def test_relpath_errors(self):
548548
# Check relpath() raises friendly TypeErrors.
549-
with support.check_warnings(('', (BytesWarning, DeprecationWarning)),
549+
from .support import check_warnings
550+
with check_warnings(('', (BytesWarning, DeprecationWarning)),
550551
quiet=True):
551552
errmsg = "Can't mix strings and bytes in path components"
552553
with self.assertRaisesRegex(TypeError, errmsg):

Lib/test/test_ntpath.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import sys
44
import unittest
55
import warnings
6-
from test.support import TestFailed
76
from test.support import os_helper
7+
from test.support import TestFailed
88
from test.support.os_helper import FakePath
9-
from test import support, test_genericpath
9+
from test import test_genericpath
1010
from tempfile import TemporaryFile
1111

1212

Lib/test/test_pathlib.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import unittest
1212
from unittest import mock
1313

14-
from test import support
15-
from test.support import os_helper, import_helper
14+
from test.support import import_helper
15+
from test.support import os_helper
1616
from test.support.os_helper import TESTFN, FakePath
1717

1818
try:
@@ -1525,7 +1525,8 @@ def test_resolve_common(self):
15251525
# resolves to 'dirB/..' first before resolving to parent of dirB.
15261526
self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False)
15271527
# Now create absolute symlinks.
1528-
d = support._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd()))
1528+
d = os_helper._longpath(tempfile.mkdtemp(suffix='-dirD',
1529+
dir=os.getcwd()))
15291530
self.addCleanup(os_helper.rmtree, d)
15301531
os.symlink(os.path.join(d), join('dirA', 'linkX'))
15311532
os.symlink(join('dirB'), os.path.join(d, 'linkY'))

Lib/test/test_socketserver.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ class ErrorHandlerTest(unittest.TestCase):
306306
KeyboardInterrupt are not passed."""
307307

308308
def tearDown(self):
309-
test.os_helper.unlink(test.os_helper.TESTFN)
309+
os_helper.unlink(os_helper.TESTFN)
310310

311311
def test_sync_handled(self):
312312
BaseErrorTestServer(ValueError)
@@ -336,7 +336,7 @@ def test_forking_not_handled(self):
336336
self.check_result(handled=False)
337337

338338
def check_result(self, handled):
339-
with open(test.os_helper.TESTFN) as log:
339+
with open(os_helper.TESTFN) as log:
340340
expected = 'Handler called\n' + 'Error handled\n' * handled
341341
self.assertEqual(log.read(), expected)
342342

@@ -354,7 +354,7 @@ def __init__(self, exception):
354354
self.wait_done()
355355

356356
def handle_error(self, request, client_address):
357-
with open(test.os_helper.TESTFN, 'a') as log:
357+
with open(os_helper.TESTFN, 'a') as log:
358358
log.write('Error handled\n')
359359

360360
def wait_done(self):
@@ -363,7 +363,7 @@ def wait_done(self):
363363

364364
class BadHandler(socketserver.BaseRequestHandler):
365365
def handle(self):
366-
with open(test.os_helper.TESTFN, 'a') as log:
366+
with open(os_helper.TESTFN, 'a') as log:
367367
log.write('Handler called\n')
368368
raise self.server.exception('Test error')
369369

Lib/test/test_tarfile.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,7 +1183,7 @@ def test_directory_size(self):
11831183
finally:
11841184
tar.close()
11851185
finally:
1186-
support.rmdir(path)
1186+
os_helper.rmdir(path)
11871187

11881188
# mock the following:
11891189
# os.listdir: so we know that files are in the wrong order
@@ -1207,7 +1207,7 @@ def test_ordered_recursion(self):
12071207
finally:
12081208
os_helper.unlink(os.path.join(path, "1"))
12091209
os_helper.unlink(os.path.join(path, "2"))
1210-
support.rmdir(path)
1210+
os_helper.rmdir(path)
12111211

12121212
def test_gettarinfo_pathlike_name(self):
12131213
with tarfile.open(tmpname, self.mode) as tar:
@@ -1348,7 +1348,7 @@ def _test_pathname(self, path, cmp_path=None, dir=False):
13481348
if not dir:
13491349
os_helper.unlink(foo)
13501350
else:
1351-
support.rmdir(foo)
1351+
os_helper.rmdir(foo)
13521352

13531353
self.assertEqual(t.name, cmp_path or path.replace(os.sep, "/"))
13541354

0 commit comments

Comments
 (0)