diff --git a/Lib/os.py b/Lib/os.py index 8cc70a11e9bc89..75b64e129818d0 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -461,7 +461,7 @@ def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd= dirs.remove('CVS') # don't visit CVS directories """ sys.audit("os.fwalk", top, topdown, onerror, follow_symlinks, dir_fd) - if not isinstance(top, int) or not hasattr(top, '__index__'): + if not isinstance(top, int) and not hasattr(top, '__index__'): top = fspath(top) # Note: To guard against symlink races, we use the standard # lstat()/open()/fstat() trick. diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 00e738ecf9a1c3..30d5f4d978a14b 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1527,6 +1527,25 @@ def test_fd_leak(self): self.addCleanup(os.close, newfd) self.assertEqual(newfd, minfd) + def test_fwalk(self): + class CustomBaseClass(): + def __init__(self, val): + self.val = val + def __index__(self): + return os.open(self.val, os.O_RDONLY) + class CustomStrClass(CustomBaseClass): + def __fspath__(self): + return str(self.val) + + top = CustomBaseClass('.') + with self.assertRaisesRegex(TypeError, f'open: path should be string, bytes or os.PathLike'): + next(os.fwalk(top=top, follow_symlinks=True)) + + top = CustomStrClass('.') + root, dirs, files, rootfd = next(os.fwalk(top=top, follow_symlinks=True)) + self.assertIsInstance(root, CustomStrClass) + self.assertNotIsInstance(root, str) + # fwalk() keeps file descriptors open test_walk_many_open_files = None diff --git a/Misc/NEWS.d/next/Library/2021-08-04-16-45-58.bpo-42053.WzOl39.rst b/Misc/NEWS.d/next/Library/2021-08-04-16-45-58.bpo-42053.WzOl39.rst new file mode 100644 index 00000000000000..878825d5b6f0be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-08-04-16-45-58.bpo-42053.WzOl39.rst @@ -0,0 +1,2 @@ +fixed incorrect boolean test for non-fd arguments in os.fwalk. +Added test with custom class and __index__ method \ No newline at end of file