mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Add tool for easier test updates (#6089)
* Add scripts/lib_updater.py * Update `Lib/test/test_os.py` with tool * Update `test_list.py` as well
This commit is contained in:
1 parent
85ca28094e
commit
c5c2bd050d
3 files changed
+396
-137
No files matched your search
Vendored
+3
-8
@@ -48,8 +48,7 @@ class ListTest(list_tests.CommonTest):
|
||||
with self.assertRaisesRegex(TypeError, 'keyword argument'):
|
||||
list(sequence=[])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_keywords_in_subclass(self):
|
||||
class subclass(list):
|
||||
pass
|
||||
@@ -105,8 +104,7 @@ class ListTest(list_tests.CommonTest):
|
||||
x[:] = x
|
||||
self.assertEqual(x, [])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.skip("TODO: RUSTPYTHON crash")
|
||||
@unittest.skip("TODO: RUSTPYTHON; crash")
|
||||
def test_list_resize_overflow(self):
|
||||
# gh-97616: test new_allocated * sizeof(PyObject*) overflow
|
||||
# check in list_resize()
|
||||
@@ -120,8 +118,7 @@ class ListTest(list_tests.CommonTest):
|
||||
with self.assertRaises((MemoryError, OverflowError)):
|
||||
lst *= size
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.skip("TODO: RUSTPYTHON hangs")
|
||||
@unittest.skip("TODO: RUSTPYTHON; hangs")
|
||||
def test_repr_mutate(self):
|
||||
class Obj:
|
||||
@staticmethod
|
||||
@@ -230,7 +227,6 @@ class ListTest(list_tests.CommonTest):
|
||||
with self.assertRaises(TypeError):
|
||||
(3,) + L([1,2])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.skip("TODO: RUSTPYTHON; hang")
|
||||
def test_equal_operator_modifying_operand(self):
|
||||
# test fix for seg fault reported in bpo-38588 part 2.
|
||||
@@ -257,7 +253,6 @@ class ListTest(list_tests.CommonTest):
|
||||
list4 = [1]
|
||||
self.assertFalse(list3 == list4)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.skip("TODO: RUSTPYTHON; hang")
|
||||
def test_lt_operator_modifying_operand(self):
|
||||
# See gh-120298
|
||||
|
||||
Vendored
+60
-129
@@ -13,7 +13,6 @@ import itertools
|
||||
import locale
|
||||
import os
|
||||
import pickle
|
||||
import platform
|
||||
import select
|
||||
import selectors
|
||||
import shutil
|
||||
@@ -188,8 +187,7 @@ class FileTests(unittest.TestCase):
|
||||
os.close(f)
|
||||
self.assertTrue(os.access(os_helper.TESTFN, os.W_OK))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON, BrokenPipeError: (32, 'The process cannot access the file because it is being used by another process. (os error 32)')")
|
||||
@unittest.skipIf(sys.platform == 'win32', "TODO: RUSTPYTHON; , BrokenPipeError: (32, 'The process cannot access the file because it is being used by another process. (os error 32)')")
|
||||
@unittest.skipIf(
|
||||
support.is_emscripten, "Test is unstable under Emscripten."
|
||||
)
|
||||
@@ -716,8 +714,7 @@ class StatAttributeTests(unittest.TestCase):
|
||||
self.assertTrue(isinstance(result.st_file_attributes, int))
|
||||
self.assertTrue(0 <= result.st_file_attributes <= 0xFFFFFFFF)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.stat return value doesnt have st_file_attributes attribute")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.stat return value doesnt have st_file_attributes attribute")
|
||||
@unittest.skipUnless(sys.platform == "win32",
|
||||
"st_file_attributes is Win32 specific")
|
||||
def test_file_attributes(self):
|
||||
@@ -739,8 +736,7 @@ class StatAttributeTests(unittest.TestCase):
|
||||
result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY,
|
||||
stat.FILE_ATTRIBUTE_DIRECTORY)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.stat (PermissionError: [Errno 5] Access is denied.)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.stat (PermissionError: [Errno 5] Access is denied.)")
|
||||
@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
|
||||
def test_access_denied(self):
|
||||
# Default to FindFirstFile WIN32_FIND_DATA when access is
|
||||
@@ -763,8 +759,7 @@ class StatAttributeTests(unittest.TestCase):
|
||||
self.assertNotEqual(result.st_size, 0)
|
||||
self.assertTrue(os.path.isfile(fname))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.stat (PermissionError: [Errno 1] Incorrect function.)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.stat (PermissionError: [Errno 1] Incorrect function.)")
|
||||
@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
|
||||
def test_stat_block_device(self):
|
||||
# bpo-38030: os.stat fails for block devices
|
||||
@@ -822,8 +817,7 @@ class UtimeTests(unittest.TestCase):
|
||||
self.assertEqual(st.st_atime_ns, atime_ns)
|
||||
self.assertEqual(st.st_mtime_ns, mtime_ns)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (AssertionError: 2.002003 != 1.002003 within 1e-06 delta (1.0000000000000002 difference))")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (AssertionError: 2.002003 != 1.002003 within 1e-06 delta (1.0000000000000002 difference))")
|
||||
def test_utime(self):
|
||||
def set_time(filename, ns):
|
||||
# test the ns keyword parameter
|
||||
@@ -889,8 +883,7 @@ class UtimeTests(unittest.TestCase):
|
||||
os.utime(name, dir_fd=dirfd, ns=ns)
|
||||
self._test_utime(set_time)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (AssertionError: 2.002003 != 1.002003 within 1e-06 delta (1.0000000000000002 difference))")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (AssertionError: 2.002003 != 1.002003 within 1e-06 delta (1.0000000000000002 difference))")
|
||||
def test_utime_directory(self):
|
||||
def set_time(filename, ns):
|
||||
# test calling os.utime() on a directory
|
||||
@@ -919,24 +912,21 @@ class UtimeTests(unittest.TestCase):
|
||||
self.assertAlmostEqual(st.st_mtime, current,
|
||||
delta=delta, msg=msg)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (AssertionError: 3359485824.516508 != 1679742912.516503 within 0.05 delta (1679742912.000005 difference) : st_time=3359485824.516508, current=1679742912.516503, dt=1679742912.000005)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (AssertionError: 3359485824.516508 != 1679742912.516503 within 0.05 delta (1679742912.000005 difference) : st_time=3359485824.516508, current=1679742912.516503, dt=1679742912.000005)")
|
||||
def test_utime_current(self):
|
||||
def set_time(filename):
|
||||
# Set to the current time in the new way
|
||||
os.utime(self.fname)
|
||||
self._test_utime_current(set_time)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (AssertionError: 3359485824.5186944 != 1679742912.5186892 within 0.05 delta (1679742912.0000052 difference) : st_time=3359485824.5186944, current=1679742912.5186892, dt=1679742912.0000052)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (AssertionError: 3359485824.5186944 != 1679742912.5186892 within 0.05 delta (1679742912.0000052 difference) : st_time=3359485824.5186944, current=1679742912.5186892, dt=1679742912.0000052)")
|
||||
def test_utime_current_old(self):
|
||||
def set_time(filename):
|
||||
# Set to the current time in the old explicit way.
|
||||
os.utime(self.fname, None)
|
||||
self._test_utime_current(set_time)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_utime_nonexistent(self):
|
||||
now = time.time()
|
||||
filename = 'nonexistent'
|
||||
@@ -957,8 +947,7 @@ class UtimeTests(unittest.TestCase):
|
||||
return buf.value
|
||||
# return None if the filesystem is unknown
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (ModuleNotFoundError: No module named '_ctypes')")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (ModuleNotFoundError: No module named '_ctypes')")
|
||||
def test_large_time(self):
|
||||
# Many filesystems are limited to the year 2038. At least, the test
|
||||
# pass with NTFS filesystem.
|
||||
@@ -969,7 +958,7 @@ class UtimeTests(unittest.TestCase):
|
||||
os.utime(self.fname, (large, large))
|
||||
self.assertEqual(os.stat(self.fname).st_mtime, large)
|
||||
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (AssertionError: NotImplementedError not raised)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (AssertionError: NotImplementedError not raised)")
|
||||
def test_utime_invalid_arguments(self):
|
||||
# seconds and nanoseconds parameters are mutually exclusive
|
||||
with self.assertRaises(ValueError):
|
||||
@@ -1171,9 +1160,8 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol):
|
||||
stdout=subprocess.PIPE, text=True)
|
||||
self.assertEqual(proc.stdout.rstrip(), repr(None))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (AssertionError: ValueError not raised by putenv)")
|
||||
# On OS X < 10.6, unsetenv() doesn't return a value (bpo-13415).
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (AssertionError: ValueError not raised by putenv)")
|
||||
@support.requires_mac_ver(10, 6)
|
||||
def test_putenv_unsetenv_error(self):
|
||||
# Empty variable name is invalid.
|
||||
@@ -1748,18 +1736,15 @@ class BytesWalkTests(WalkTests):
|
||||
bdirs[:] = list(map(os.fsencode, dirs))
|
||||
bfiles[:] = list(map(os.fsencode, files))
|
||||
|
||||
# TODO: RUSTPYTHON (TypeError: Can't mix strings and bytes in path components)
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; (TypeError: Can't mix strings and bytes in path components)
|
||||
def test_compare_to_walk(self):
|
||||
return super().test_compare_to_walk()
|
||||
|
||||
# TODO: RUSTPYTHON (TypeError: Can't mix strings and bytes in path components)
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; (TypeError: Can't mix strings and bytes in path components)
|
||||
def test_dir_fd(self):
|
||||
return super().test_dir_fd()
|
||||
|
||||
# TODO: RUSTPYTHON (TypeError: Can't mix strings and bytes in path components)
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; (TypeError: Can't mix strings and bytes in path components)
|
||||
def test_yields_correct_dir_fd(self):
|
||||
return super().test_yields_correct_dir_fd()
|
||||
|
||||
@@ -1811,8 +1796,7 @@ class MakedirTests(unittest.TestCase):
|
||||
self.assertEqual(os.stat(path).st_mode & 0o777, 0o555)
|
||||
self.assertEqual(os.stat(parent).st_mode & 0o777, 0o775)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.umask not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.umask not implemented yet for all platforms")
|
||||
@unittest.skipIf(
|
||||
support.is_emscripten or support.is_wasi,
|
||||
"Emscripten's/WASI's umask is a stub."
|
||||
@@ -1831,8 +1815,7 @@ class MakedirTests(unittest.TestCase):
|
||||
# Issue #25583: A drive root could raise PermissionError on Windows
|
||||
os.makedirs(os.path.abspath('/'), exist_ok=True)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.umask not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.umask not implemented yet for all platforms")
|
||||
@unittest.skipIf(
|
||||
support.is_emscripten or support.is_wasi,
|
||||
"Emscripten's/WASI's umask is a stub."
|
||||
@@ -2119,8 +2102,7 @@ class URandomFDTests(unittest.TestCase):
|
||||
"""
|
||||
assert_python_ok('-c', code)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON on Windows (ModuleNotFoundError: No module named 'os')")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; on Windows (ModuleNotFoundError: No module named 'os')")
|
||||
def test_urandom_fd_closed(self):
|
||||
# Issue #21207: urandom() should reopen its fd to /dev/urandom if
|
||||
# closed.
|
||||
@@ -2135,8 +2117,7 @@ class URandomFDTests(unittest.TestCase):
|
||||
"""
|
||||
rc, out, err = assert_python_ok('-Sc', code)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (ModuleNotFoundError: No module named 'os'")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (ModuleNotFoundError: No module named 'os'")
|
||||
def test_urandom_fd_reopened(self):
|
||||
# Issue #21207: urandom() should detect its fd to /dev/urandom
|
||||
# changed to something else, and reopen it.
|
||||
@@ -2224,8 +2205,7 @@ class ExecTests(unittest.TestCase):
|
||||
self.assertRaises(ValueError, os.execv, 'notepad', ('',))
|
||||
self.assertRaises(ValueError, os.execv, 'notepad', [''])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.execve not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.execve not implemented yet for all platforms")
|
||||
def test_execvpe_with_bad_arglist(self):
|
||||
self.assertRaises(ValueError, os.execvpe, 'notepad', [], None)
|
||||
self.assertRaises(ValueError, os.execvpe, 'notepad', [], {})
|
||||
@@ -2285,8 +2265,7 @@ class ExecTests(unittest.TestCase):
|
||||
if os.name != "nt":
|
||||
self._test_internal_execvpe(bytes)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.execve not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.execve not implemented yet for all platforms")
|
||||
def test_execve_invalid_env(self):
|
||||
args = [sys.executable, '-c', 'pass']
|
||||
|
||||
@@ -2308,8 +2287,7 @@ class ExecTests(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
os.execve(args[0], args, newenv)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.execve not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.execve not implemented yet for all platforms")
|
||||
@unittest.skipUnless(sys.platform == "win32", "Win32-specific test")
|
||||
def test_execve_with_empty_path(self):
|
||||
# bpo-32890: Check GetLastError() misuse
|
||||
@@ -2390,8 +2368,7 @@ class TestInvalidFD(unittest.TestCase):
|
||||
with self.assertRaises(RuntimeWarning):
|
||||
f(fd, *args, **kwargs)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_fdopen(self):
|
||||
self.check(os.fdopen, encoding="utf-8")
|
||||
self.check_bool(os.fdopen, encoding="utf-8")
|
||||
@@ -2448,8 +2425,7 @@ class TestInvalidFD(unittest.TestCase):
|
||||
def test_fchown(self):
|
||||
self.check(os.fchown, -1, -1)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.skipUnless(hasattr(os, 'fpathconf'), 'test needs os.fpathconf()')
|
||||
@unittest.skipIf(
|
||||
support.is_emscripten or support.is_wasi,
|
||||
@@ -2462,22 +2438,19 @@ class TestInvalidFD(unittest.TestCase):
|
||||
self.check_bool(os.pathconf, "PC_NAME_MAX")
|
||||
self.check_bool(os.fpathconf, "PC_NAME_MAX")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.skipUnless(hasattr(os, 'ftruncate'), 'test needs os.ftruncate()')
|
||||
def test_ftruncate(self):
|
||||
self.check(os.truncate, 0)
|
||||
self.check(os.ftruncate, 0)
|
||||
self.check_bool(os.truncate, 0)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (OSError: [Errno 18] There are no more files.)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (OSError: [Errno 18] There are no more files.)")
|
||||
@unittest.skipUnless(hasattr(os, 'lseek'), 'test needs os.lseek()')
|
||||
def test_lseek(self):
|
||||
self.check(os.lseek, 0, 0)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (OSError: [Errno 18] There are no more files.)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (OSError: [Errno 18] There are no more files.)")
|
||||
@unittest.skipUnless(hasattr(os, 'read'), 'test needs os.read()')
|
||||
def test_read(self):
|
||||
self.check(os.read, 1)
|
||||
@@ -2491,8 +2464,7 @@ class TestInvalidFD(unittest.TestCase):
|
||||
def test_tcsetpgrpt(self):
|
||||
self.check(os.tcsetpgrp, 0)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (OSError: [Errno 18] There are no more files.)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (OSError: [Errno 18] There are no more files.)")
|
||||
@unittest.skipUnless(hasattr(os, 'write'), 'test needs os.write()')
|
||||
def test_write(self):
|
||||
self.check(os.write, b" ")
|
||||
@@ -2501,8 +2473,7 @@ class TestInvalidFD(unittest.TestCase):
|
||||
def test_writev(self):
|
||||
self.check(os.writev, [b'abc'])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.get_inheritable not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.get_inheritable not implemented yet for all platforms")
|
||||
@support.requires_subprocess()
|
||||
def test_inheritable(self):
|
||||
self.check(os.get_inheritable)
|
||||
@@ -2514,13 +2485,11 @@ class TestInvalidFD(unittest.TestCase):
|
||||
self.check(os.get_blocking)
|
||||
self.check(os.set_blocking, True)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_fchdir(self):
|
||||
return super().test_fchdir()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_fsync(self):
|
||||
return super().test_fsync()
|
||||
|
||||
@@ -2563,7 +2532,6 @@ class LinkTests(unittest.TestCase):
|
||||
self.file2 = self.file1 + "2"
|
||||
self._test_link(self.file1, self.file2)
|
||||
|
||||
|
||||
@unittest.skipIf(sys.platform == "win32", "Posix specific tests")
|
||||
class PosixUidGidTests(unittest.TestCase):
|
||||
# uid_t and gid_t are 32-bit unsigned integers on Linux
|
||||
@@ -2765,14 +2733,12 @@ class Win32KillTests(unittest.TestCase):
|
||||
os.kill(proc.pid, sig)
|
||||
self.assertEqual(proc.wait(), sig)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (ModuleNotFoundError: No module named '_ctypes')")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (ModuleNotFoundError: No module named '_ctypes')")
|
||||
def test_kill_sigterm(self):
|
||||
# SIGTERM doesn't mean anything special, but make sure it works
|
||||
self._kill(signal.SIGTERM)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (ModuleNotFoundError: No module named '_ctypes')")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (ModuleNotFoundError: No module named '_ctypes')")
|
||||
def test_kill_int(self):
|
||||
# os.kill on Windows can take an int which gets set as the exit code
|
||||
self._kill(100)
|
||||
@@ -2831,8 +2797,7 @@ class Win32KillTests(unittest.TestCase):
|
||||
|
||||
self._kill_with_event(signal.CTRL_C_EVENT, "CTRL_C_EVENT")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@support.requires_subprocess()
|
||||
def test_CTRL_BREAK_EVENT(self):
|
||||
self._kill_with_event(signal.CTRL_BREAK_EVENT, "CTRL_BREAK_EVENT")
|
||||
@@ -3186,8 +3151,7 @@ class Win32JunctionTests(unittest.TestCase):
|
||||
if os.path.lexists(self.junction):
|
||||
os.unlink(self.junction)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (AttributeError: module '_winapi' has no attribute 'CreateJunction')")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (AttributeError: module '_winapi' has no attribute 'CreateJunction')")
|
||||
def test_create_junction(self):
|
||||
_winapi.CreateJunction(self.junction_target, self.junction)
|
||||
self.assertTrue(os.path.lexists(self.junction))
|
||||
@@ -3201,8 +3165,7 @@ class Win32JunctionTests(unittest.TestCase):
|
||||
self.assertEqual(os.path.normcase("\\\\?\\" + self.junction_target),
|
||||
os.path.normcase(os.readlink(self.junction)))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (AttributeError: module '_winapi' has no attribute 'CreateJunction')")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (AttributeError: module '_winapi' has no attribute 'CreateJunction')")
|
||||
def test_unlink_removes_junction(self):
|
||||
_winapi.CreateJunction(self.junction_target, self.junction)
|
||||
self.assertTrue(os.path.exists(self.junction))
|
||||
@@ -3262,8 +3225,7 @@ class Win32NtTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(0, handle_delta)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.stat (PermissionError: [Errno 5] Access is denied.)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.stat (PermissionError: [Errno 5] Access is denied.)")
|
||||
@support.requires_subprocess()
|
||||
def test_stat_unlink_race(self):
|
||||
# bpo-46785: the implementation of os.stat() falls back to reading
|
||||
@@ -3473,8 +3435,7 @@ class PidTests(unittest.TestCase):
|
||||
with self.assertRaises(TypeError):
|
||||
os.waitstatus_to_exitcode(0.0)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.spawnv not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.spawnv not implemented yet for all platforms")
|
||||
@unittest.skipUnless(sys.platform == 'win32', 'win32-specific test')
|
||||
def test_waitpid_windows(self):
|
||||
# bpo-40138: test os.waitpid() and os.waitstatus_to_exitcode()
|
||||
@@ -3483,8 +3444,7 @@ class PidTests(unittest.TestCase):
|
||||
code = f'import _winapi; _winapi.ExitProcess({STATUS_CONTROL_C_EXIT})'
|
||||
self.check_waitpid(code, exitcode=STATUS_CONTROL_C_EXIT)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (OverflowError: Python int too large to convert to Rust i32)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (OverflowError: Python int too large to convert to Rust i32)")
|
||||
@unittest.skipUnless(sys.platform == 'win32', 'win32-specific test')
|
||||
def test_waitstatus_to_exitcode_windows(self):
|
||||
max_exitcode = 2 ** 32 - 1
|
||||
@@ -3608,8 +3568,7 @@ class SpawnTests(unittest.TestCase):
|
||||
pid = os.spawnv(os.P_NOWAIT, program, args)
|
||||
support.wait_process(pid, exitcode=self.exitcode)
|
||||
|
||||
# TODO: RUSTPYTHON fix spawnv bytes
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; fix spawnv bytes
|
||||
@requires_os_func('spawnve')
|
||||
def test_spawnve_bytes(self):
|
||||
# Test bytes handling in parse_arglist and parse_envlist (#28114)
|
||||
@@ -4224,16 +4183,8 @@ class EventfdTests(unittest.TestCase):
|
||||
@unittest.skipIf(sys.platform == "android", "gh-124873: Test is flaky on Android")
|
||||
@support.requires_linux_version(2, 6, 30)
|
||||
class TimerfdTests(unittest.TestCase):
|
||||
# 1 ms accuracy is reliably achievable on every platform except Android
|
||||
# emulators, where we allow 10 ms (gh-108277).
|
||||
|
||||
# XXX: RUSTPYTHON; AttributeError: module 'platform' has no attribute 'android_ver'
|
||||
#if sys.platform == "android" and platform.android_ver().is_emulator:
|
||||
if sys.platform == "android":
|
||||
CLOCK_RES_PLACES = 2
|
||||
else:
|
||||
CLOCK_RES_PLACES = 3
|
||||
|
||||
# gh-126112: Use 10 ms to tolerate slow buildbots
|
||||
CLOCK_RES_PLACES = 2 # 10 ms
|
||||
CLOCK_RES = 10 ** -CLOCK_RES_PLACES
|
||||
CLOCK_RES_NS = 10 ** (9 - CLOCK_RES_PLACES)
|
||||
|
||||
@@ -4577,8 +4528,7 @@ class OSErrorTests(unittest.TestCase):
|
||||
|
||||
self.filenames = self.bytes_filenames + self.unicode_filenames
|
||||
|
||||
# TODO: RUSTPYTHON (AssertionError: b'@test_22106_tmp\xe7w\xf0' is not b'@test_22106_tmp\xe7w\xf0' : <built-in function chdir>)
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; (AssertionError: b'@test_22106_tmp\xe7w\xf0' is not b'@test_22106_tmp\xe7w\xf0' : <built-in function chdir>)
|
||||
def test_oserror_filename(self):
|
||||
funcs = [
|
||||
(self.filenames, os.chdir,),
|
||||
@@ -4671,8 +4621,7 @@ class CPUCountTests(unittest.TestCase):
|
||||
# FD inheritance check is only useful for systems with process support.
|
||||
@support.requires_subprocess()
|
||||
class FDInheritanceTests(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.get_inheritable not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.get_inheritable not implemented yet for all platforms")
|
||||
def test_get_set_inheritable(self):
|
||||
fd = os.open(__file__, os.O_RDONLY)
|
||||
self.addCleanup(os.close, fd)
|
||||
@@ -4717,8 +4666,7 @@ class FDInheritanceTests(unittest.TestCase):
|
||||
os.set_inheritable(fd, False)
|
||||
self.assertEqual(os.get_inheritable(fd), False)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.get_inheritable not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.get_inheritable not implemented yet for all platforms")
|
||||
def test_get_set_inheritable_badf(self):
|
||||
fd = os_helper.make_bad_fd()
|
||||
|
||||
@@ -4734,8 +4682,7 @@ class FDInheritanceTests(unittest.TestCase):
|
||||
os.set_inheritable(fd, False)
|
||||
self.assertEqual(ctx.exception.errno, errno.EBADF)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.get_inheritable not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.get_inheritable not implemented yet for all platforms")
|
||||
def test_open(self):
|
||||
fd = os.open(__file__, os.O_RDONLY)
|
||||
self.addCleanup(os.close, fd)
|
||||
@@ -4749,7 +4696,6 @@ class FDInheritanceTests(unittest.TestCase):
|
||||
self.assertEqual(os.get_inheritable(rfd), False)
|
||||
self.assertEqual(os.get_inheritable(wfd), False)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.skipIf(sys.platform == 'win32', "TODO: RUSTPYTHON; os.dup on windows")
|
||||
def test_dup(self):
|
||||
fd1 = os.open(__file__, os.O_RDONLY)
|
||||
@@ -4759,15 +4705,13 @@ class FDInheritanceTests(unittest.TestCase):
|
||||
self.addCleanup(os.close, fd2)
|
||||
self.assertEqual(os.get_inheritable(fd2), False)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.skipIf(sys.platform == 'win32', "TODO: RUSTPYTHON; os.dup on windows")
|
||||
def test_dup_standard_stream(self):
|
||||
fd = os.dup(1)
|
||||
self.addCleanup(os.close, fd)
|
||||
self.assertGreater(fd, 0)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON os.dup not implemented yet for all platforms")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; os.dup not implemented yet for all platforms")
|
||||
@unittest.skipUnless(sys.platform == 'win32', 'win32-specific test')
|
||||
def test_dup_nul(self):
|
||||
# os.dup() was creating inheritable fds for character files.
|
||||
@@ -4907,8 +4851,7 @@ class PathTConverterTests(unittest.TestCase):
|
||||
('open', False, (os.O_RDONLY,), getattr(os, 'close', None)),
|
||||
]
|
||||
|
||||
# TODO: RUSTPYTHON (AssertionError: TypeError not raised)
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; (AssertionError: TypeError not raised)
|
||||
def test_path_t_converter(self):
|
||||
str_filename = os_helper.TESTFN
|
||||
if os.name == 'nt':
|
||||
@@ -4993,13 +4936,11 @@ class TestDirEntry(unittest.TestCase):
|
||||
self.addCleanup(os_helper.rmtree, self.path)
|
||||
os.mkdir(self.path)
|
||||
|
||||
# TODO: RUSTPYTHON (AssertionError: TypeError not raised by DirEntry)
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; (AssertionError: TypeError not raised by DirEntry)
|
||||
def test_uninstantiable(self):
|
||||
self.assertRaises(TypeError, os.DirEntry)
|
||||
|
||||
# TODO: RUSTPYTHON (pickle.PicklingError: Can't pickle <class '_os.DirEntry'>: it's not found as _os.DirEntry)
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; (pickle.PicklingError: Can't pickle <class '_os.DirEntry'>: it's not found as _os.DirEntry)
|
||||
def test_unpickable(self):
|
||||
filename = create_file(os.path.join(self.path, "file.txt"), b'python')
|
||||
entry = [entry for entry in os.scandir(self.path)].pop()
|
||||
@@ -5044,8 +4985,7 @@ class TestScandir(unittest.TestCase):
|
||||
else:
|
||||
self.assertEqual(stat1, stat2)
|
||||
|
||||
# TODO: RUSTPYTHON (AssertionError: TypeError not raised by ScandirIter)
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; (AssertionError: TypeError not raised by ScandirIter)
|
||||
def test_uninstantiable(self):
|
||||
scandir_iter = os.scandir(self.path)
|
||||
self.assertRaises(TypeError, type(scandir_iter))
|
||||
@@ -5088,8 +5028,7 @@ class TestScandir(unittest.TestCase):
|
||||
entry_lstat,
|
||||
os.name == 'nt')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.skipIf(sys.platform == "linux", "TODO: RUSTPYTHON, flaky test")
|
||||
@unittest.skipIf(sys.platform == 'linux', "TODO: RUSTPYTHON; , flaky test")
|
||||
def test_attributes(self):
|
||||
link = os_helper.can_hardlink()
|
||||
symlink = os_helper.can_symlink()
|
||||
@@ -5189,8 +5128,7 @@ class TestScandir(unittest.TestCase):
|
||||
self.assertEqual(fspath,
|
||||
os.path.join(os.fsencode(self.path),bytes_filename))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON entry.is_dir() is False")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; entry.is_dir() is False")
|
||||
def test_removed_dir(self):
|
||||
path = os.path.join(self.path, 'dir')
|
||||
|
||||
@@ -5213,8 +5151,7 @@ class TestScandir(unittest.TestCase):
|
||||
self.assertRaises(FileNotFoundError, entry.stat)
|
||||
self.assertRaises(FileNotFoundError, entry.stat, follow_symlinks=False)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON entry.is_file() is False")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; entry.is_file() is False")
|
||||
def test_removed_file(self):
|
||||
entry = self.create_file_entry()
|
||||
os.unlink(entry.path)
|
||||
@@ -5275,8 +5212,7 @@ class TestScandir(unittest.TestCase):
|
||||
with self.assertRaises(TypeError):
|
||||
os.scandir(path_bytes)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.skipUnless(os.listdir in os.supports_fd,
|
||||
'fd support for listdir required for this test.')
|
||||
def test_fd(self):
|
||||
@@ -5303,8 +5239,7 @@ class TestScandir(unittest.TestCase):
|
||||
st = os.stat(entry.name, dir_fd=fd, follow_symlinks=False)
|
||||
self.assertEqual(entry.stat(follow_symlinks=False), st)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON (AssertionError: FileNotFoundError not raised by scandir)")
|
||||
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (AssertionError: FileNotFoundError not raised by scandir)")
|
||||
@unittest.skipIf(support.is_wasi, "WASI maps '' to cwd")
|
||||
def test_empty_path(self):
|
||||
self.assertRaises(FileNotFoundError, os.scandir, '')
|
||||
@@ -5360,8 +5295,7 @@ class TestScandir(unittest.TestCase):
|
||||
with self.check_no_resource_warning():
|
||||
del iterator
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_resource_warning(self):
|
||||
self.create_file("file.txt")
|
||||
self.create_file("file2.txt")
|
||||
@@ -5434,8 +5368,7 @@ class TestPEP519(unittest.TestCase):
|
||||
def test_pathlike_class_getitem(self):
|
||||
self.assertIsInstance(os.PathLike[bytes], types.GenericAlias)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_pathlike_subclass_slots(self):
|
||||
class A(os.PathLike):
|
||||
__slots__ = ()
|
||||
@@ -5443,8 +5376,7 @@ class TestPEP519(unittest.TestCase):
|
||||
return ''
|
||||
self.assertFalse(hasattr(A(), '__dict__'))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_fspath_set_to_None(self):
|
||||
class Foo:
|
||||
__fspath__ = None
|
||||
@@ -5547,8 +5479,7 @@ class ForkTests(unittest.TestCase):
|
||||
self.assertEqual(err.decode("utf-8"), "")
|
||||
self.assertEqual(out.decode("utf-8"), "")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_fork_at_finalization(self):
|
||||
code = """if 1:
|
||||
import atexit
|
||||
|
||||
Executable
+333
@@ -0,0 +1,333 @@
|
||||
#!/usr/bin/env python
|
||||
__doc__ = """
|
||||
This tool helps with updating test files from CPython.
|
||||
|
||||
Examples
|
||||
--------
|
||||
To move the patches found in `Lib/test/foo.py` to ` ~/cpython/Lib/test/foo.py` then write the contents back to `Lib/test/foo.py`
|
||||
|
||||
>>> ./{fname} --from Lib/test/foo.py --to ~/cpython/Lib/test/foo.py -o Lib/test/foo.py
|
||||
|
||||
You can run the same command without `-o` to override the `--from` path:
|
||||
|
||||
>>> ./{fname} --from Lib/test/foo.py --to ~/cpython/Lib/test/foo.py
|
||||
|
||||
To get a baseline of patches, you can alter the patches file with your favorite tool/script/etc and then reapply it with:
|
||||
|
||||
>>> ./{fname} --from Lib/test/foo.py --show-patches -o my_patches.json
|
||||
|
||||
(By default the output is set to print to stdout).
|
||||
|
||||
When you want to apply your own patches:
|
||||
|
||||
>>> ./{fname} -p my_patches.json --to Lib/test/foo.py
|
||||
""".format(fname=__import__("os").path.basename(__file__))
|
||||
|
||||
|
||||
import argparse
|
||||
import ast
|
||||
import collections
|
||||
import enum
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
import typing
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from collections.abc import Iterator
|
||||
|
||||
type Patches = dict[str, dict[str, list["PatchSpec"]]]
|
||||
|
||||
COL_OFFSET = 4
|
||||
INDENT1 = " " * COL_OFFSET
|
||||
INDENT2 = INDENT1 * 2
|
||||
COMMENT = "TODO: RUSTPYTHON"
|
||||
|
||||
|
||||
@enum.unique
|
||||
class UtMethod(enum.StrEnum):
|
||||
"""
|
||||
UnitTest Method.
|
||||
"""
|
||||
|
||||
def _generate_next_value_(name, start, count, last_values) -> str:
|
||||
return name[0].lower() + name[1:]
|
||||
|
||||
def has_cond(self) -> bool:
|
||||
return self.endswith(("If", "Unless"))
|
||||
|
||||
ExpectedFailure = enum.auto()
|
||||
ExpectedFailureIf = enum.auto()
|
||||
ExpectedFailureIfWindows = enum.auto()
|
||||
Skip = enum.auto()
|
||||
SkipIf = enum.auto()
|
||||
SkipUnless = enum.auto()
|
||||
|
||||
|
||||
class PatchSpec(typing.NamedTuple):
|
||||
"""
|
||||
Attributes
|
||||
----------
|
||||
ut_method : UtMethod
|
||||
unittest method.
|
||||
cond : str, optional
|
||||
`ut_method` condition. Relevant only for some of `ut_method` types.
|
||||
reason : str, optional
|
||||
Reason for why the test is patched in this way.
|
||||
"""
|
||||
|
||||
ut_method: UtMethod
|
||||
cond: str | None = None
|
||||
reason: str = ""
|
||||
|
||||
def fmt(self) -> str:
|
||||
prefix = f"@unittest.{self.ut_method}"
|
||||
match self.ut_method:
|
||||
case UtMethod.ExpectedFailure:
|
||||
line = f"{prefix} # {COMMENT}; {self.reason}"
|
||||
case UtMethod.ExpectedFailureIfWindows | UtMethod.Skip:
|
||||
line = f'{prefix}("{COMMENT}; {self.reason}")'
|
||||
case UtMethod.SkipIf | UtMethod.SkipUnless | UtMethod.ExpectedFailureIf:
|
||||
line = f'{prefix}({self.cond}, "{COMMENT}; {self.reason}")'
|
||||
|
||||
return line.strip().rstrip(";").strip()
|
||||
|
||||
|
||||
class PatchEntry(typing.NamedTuple):
|
||||
"""
|
||||
Stores patch metadata.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
parent_class : str
|
||||
Parent class of test.
|
||||
test_name : str
|
||||
Test name.
|
||||
spec : PatchSpec
|
||||
Patch spec.
|
||||
"""
|
||||
|
||||
parent_class: str
|
||||
test_name: str
|
||||
spec: PatchSpec
|
||||
|
||||
@classmethod
|
||||
def iter_patch_entires(
|
||||
cls, tree: ast.Module, lines: list[str]
|
||||
) -> "Iterator[typing.Self]":
|
||||
for cls_node, fn_node in iter_tests(tree):
|
||||
parent_class = cls_node.name
|
||||
for dec_node in fn_node.decorator_list:
|
||||
if not isinstance(dec_node, (ast.Attribute, ast.Call)):
|
||||
continue
|
||||
|
||||
attr_node = (
|
||||
dec_node if isinstance(dec_node, ast.Attribute) else dec_node.func
|
||||
)
|
||||
|
||||
if (
|
||||
isinstance(attr_node, ast.Name)
|
||||
or getattr(attr_node.value, "id", None) != "unittest"
|
||||
):
|
||||
continue
|
||||
|
||||
cond = None
|
||||
try:
|
||||
ut_method = UtMethod(attr_node.attr)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
match ut_method:
|
||||
case UtMethod.ExpectedFailure:
|
||||
# Search first on decorator line, then in the line before
|
||||
for line in lines[
|
||||
dec_node.lineno - 1 : dec_node.lineno - 3 : -1
|
||||
]:
|
||||
if COMMENT not in line:
|
||||
continue
|
||||
reason = "".join(re.findall(rf"{COMMENT}.?(.*)", line))
|
||||
break
|
||||
else:
|
||||
continue
|
||||
case _:
|
||||
reason = next(
|
||||
(
|
||||
node.value
|
||||
for node in ast.walk(dec_node)
|
||||
if isinstance(node, ast.Constant)
|
||||
and isinstance(node.value, str)
|
||||
and COMMENT in node.value
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
# If we didn't find a constant containing <COMMENT>,
|
||||
# then we didn't put this decorator
|
||||
if not reason:
|
||||
continue
|
||||
|
||||
if ut_method.has_cond():
|
||||
cond = ast.unparse(dec_node.args[0])
|
||||
|
||||
reason = (
|
||||
reason.replace(COMMENT, "").strip().lstrip(";").lstrip(":").strip()
|
||||
)
|
||||
spec = PatchSpec(ut_method, cond, reason)
|
||||
yield cls(parent_class, fn_node.name, spec)
|
||||
|
||||
|
||||
def iter_tests(
|
||||
tree: ast.Module,
|
||||
) -> "Iterator[tuple[ast.ClassDef, ast.FunctionDef | ast.AsyncFunctionDef]]":
|
||||
for key, nodes in ast.iter_fields(tree):
|
||||
if key != "body":
|
||||
continue
|
||||
|
||||
for cls_node in nodes:
|
||||
if not isinstance(cls_node, ast.ClassDef):
|
||||
continue
|
||||
|
||||
for fn_node in cls_node.body:
|
||||
if not isinstance(fn_node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
continue
|
||||
|
||||
yield (cls_node, fn_node)
|
||||
|
||||
|
||||
def iter_patches(contents: str) -> "Iterator[PatchEntry]":
|
||||
lines = contents.splitlines()
|
||||
tree = ast.parse(contents)
|
||||
yield from PatchEntry.iter_patch_entires(tree, lines)
|
||||
|
||||
|
||||
def build_patch_dict(it: "Iterator[PatchEntry]") -> Patches:
|
||||
patches = collections.defaultdict(lambda: collections.defaultdict(list))
|
||||
for entry in it:
|
||||
patches[entry.parent_class][entry.test_name].append(entry.spec)
|
||||
|
||||
return {k: dict(v) for k, v in patches.items()}
|
||||
|
||||
|
||||
def iter_patch_lines(tree: ast.Module, patches: Patches) -> "Iterator[tuple[int, str]]":
|
||||
cache = {} # Used in phase 2
|
||||
|
||||
# Phase 1: Iterate and mark existing tests
|
||||
for cls_node, fn_node in iter_tests(tree):
|
||||
cache[cls_node.name] = cls_node.end_lineno
|
||||
specs = patches.get(cls_node.name, {}).pop(fn_node.name, None)
|
||||
if not specs:
|
||||
continue
|
||||
|
||||
lineno = min(
|
||||
(dec_node.lineno for dec_node in fn_node.decorator_list),
|
||||
default=fn_node.lineno,
|
||||
)
|
||||
indent = " " * fn_node.col_offset
|
||||
yield (lineno - 1, "\n".join(f"{indent}{spec.fmt()}" for spec in specs))
|
||||
|
||||
# Phase 2: Iterate and mark inhereted tests
|
||||
for cls_name, tests in patches.items():
|
||||
lineno = cache.get(cls_name)
|
||||
if not lineno:
|
||||
print(f"WARNING: {cls_name} does not exist in remote file", file=sys.stderr)
|
||||
continue
|
||||
for test_name, specs in tests.items():
|
||||
patch_lines = "\n".join(f"{INDENT1}{spec.fmt()}" for spec in specs)
|
||||
yield (
|
||||
lineno,
|
||||
f"""
|
||||
{patch_lines}
|
||||
{INDENT1}def {test_name}(self):
|
||||
{INDENT2}return super().{test_name}()
|
||||
""".rstrip(),
|
||||
)
|
||||
|
||||
|
||||
def apply_patches(contents: str, patches: Patches) -> str:
|
||||
tree = ast.parse(contents)
|
||||
lines = contents.splitlines()
|
||||
|
||||
modifications = list(iter_patch_lines(tree, patches))
|
||||
# Going in reverse to not distrupt the line offset
|
||||
for lineno, patch in sorted(modifications, reverse=True):
|
||||
lines.insert(lineno, patch)
|
||||
|
||||
joined = "\n".join(lines)
|
||||
return f"{joined}\n"
|
||||
|
||||
|
||||
def write_output(data: str, dest: str) -> None:
|
||||
if dest == "-":
|
||||
print(data, end="")
|
||||
return
|
||||
|
||||
with open(dest, "w") as fd:
|
||||
fd.write(data)
|
||||
|
||||
|
||||
def build_argparse() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
patches_group = parser.add_mutually_exclusive_group(required=True)
|
||||
patches_group.add_argument(
|
||||
"-p",
|
||||
"--patches",
|
||||
help="File path to file containing patches in a JSON format",
|
||||
type=pathlib.Path,
|
||||
)
|
||||
patches_group.add_argument(
|
||||
"--from",
|
||||
help="File to gather patches from",
|
||||
dest="gather_from",
|
||||
type=pathlib.Path,
|
||||
)
|
||||
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument(
|
||||
"--to",
|
||||
help="File to apply patches to",
|
||||
type=pathlib.Path,
|
||||
)
|
||||
group.add_argument(
|
||||
"--show-patches", action="store_true", help="Show the patches and exit"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-o", "--output", default="-", help="Output file. Set to '-' for stdout"
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = build_argparse()
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.patches:
|
||||
patches = {
|
||||
cls_name: {
|
||||
test_name: [PatchSpec(**spec) for spec in specs]
|
||||
for test_name, specs in tests.items()
|
||||
}
|
||||
for cls_name, tests in json.loads(args.patches.read_text()).items()
|
||||
}
|
||||
else:
|
||||
patches = build_patch_dict(iter_patches(args.gather_from.read_text()))
|
||||
|
||||
if args.show_patches:
|
||||
patches = {
|
||||
cls_name: {
|
||||
test_name: [spec._asdict() for spec in specs]
|
||||
for test_name, specs in tests.items()
|
||||
}
|
||||
for cls_name, tests in patches.items()
|
||||
}
|
||||
output = json.dumps(patches, indent=4) + "\n"
|
||||
write_output(output, args.output)
|
||||
sys.exit(0)
|
||||
|
||||
patched = apply_patches(args.to.read_text(), patches)
|
||||
write_output(patched, args.output)
|
||||
Reference in new issue
Block a user