]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-122400: Handle ValueError in filecmp (GH-122401)
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>
Tue, 30 Jul 2024 08:50:30 +0000 (10:50 +0200)
committerGitHub <noreply@github.com>
Tue, 30 Jul 2024 08:50:30 +0000 (08:50 +0000)
Lib/filecmp.py
Lib/test/test_filecmp.py
Misc/NEWS.d/next/Library/2024-07-29-16-47-08.gh-issue-122400.fM0YSv.rst [new file with mode: 0644]

index 020ea694ca63e93d5d7daff628af780e4f5ae704..c5b8d854d77de3dfecb058994b3436f69f5dfad5 100644 (file)
@@ -164,12 +164,14 @@ class dircmp:
             ok = True
             try:
                 a_stat = os.stat(a_path)
-            except OSError:
+            except (OSError, ValueError):
+                # See https://github.com/python/cpython/issues/122400
+                # for the rationale for protecting against ValueError.
                 # print('Can\'t stat', a_path, ':', why.args[1])
                 ok = False
             try:
                 b_stat = os.stat(b_path)
-            except OSError:
+            except (OSError, ValueError):
                 # print('Can\'t stat', b_path, ':', why.args[1])
                 ok = False
 
@@ -285,12 +287,12 @@ def cmpfiles(a, b, common, shallow=True):
 # Return:
 #       0 for equal
 #       1 for different
-#       2 for funny cases (can't stat, etc.)
+#       2 for funny cases (can't stat, NUL bytes, etc.)
 #
 def _cmp(a, b, sh, abs=abs, cmp=cmp):
     try:
         return not abs(cmp(a, b, sh))
-    except OSError:
+    except (OSError, ValueError):
         return 2
 
 
index 1fb47163719ede40086b06867ac8bf0d924e117d..2c83667b22feb4a9b0e2dc4521aad21bd888621d 100644 (file)
@@ -156,6 +156,39 @@ class DirCompareTestCase(unittest.TestCase):
                     (['file'], ['file2'], []),
                     "Comparing mismatched directories fails")
 
+    def test_cmpfiles_invalid_names(self):
+        # See https://github.com/python/cpython/issues/122400.
+        for file, desc in [
+            ('\x00', 'NUL bytes filename'),
+            (__file__ + '\x00', 'filename with embedded NUL bytes'),
+            ("\uD834\uDD1E.py", 'surrogate codes (MUSICAL SYMBOL G CLEF)'),
+            ('a' * 1_000_000, 'very long filename'),
+        ]:
+            for other_dir in [self.dir, self.dir_same, self.dir_diff]:
+                with self.subTest(f'cmpfiles: {desc}', other_dir=other_dir):
+                    res = filecmp.cmpfiles(self.dir, other_dir, [file])
+                    self.assertTupleEqual(res, ([], [], [file]))
+
+    def test_dircmp_invalid_names(self):
+        for bad_dir, desc in [
+            ('\x00', 'NUL bytes dirname'),
+            (f'Top{os.sep}Mid\x00', 'dirname with embedded NUL bytes'),
+            ("\uD834\uDD1E", 'surrogate codes (MUSICAL SYMBOL G CLEF)'),
+            ('a' * 1_000_000, 'very long dirname'),
+        ]:
+            d1 = filecmp.dircmp(self.dir, bad_dir)
+            d2 = filecmp.dircmp(bad_dir, self.dir)
+            for target in [
+                # attributes where os.listdir() raises OSError or ValueError
+                'left_list', 'right_list',
+                'left_only', 'right_only', 'common',
+            ]:
+                with self.subTest(f'dircmp(ok, bad): {desc}', target=target):
+                    with self.assertRaises((OSError, ValueError)):
+                        getattr(d1, target)
+                with self.subTest(f'dircmp(bad, ok): {desc}', target=target):
+                    with self.assertRaises((OSError, ValueError)):
+                        getattr(d2, target)
 
     def _assert_lists(self, actual, expected):
         """Assert that two lists are equal, up to ordering."""
diff --git a/Misc/NEWS.d/next/Library/2024-07-29-16-47-08.gh-issue-122400.fM0YSv.rst b/Misc/NEWS.d/next/Library/2024-07-29-16-47-08.gh-issue-122400.fM0YSv.rst
new file mode 100644 (file)
index 0000000..8c47e94
--- /dev/null
@@ -0,0 +1,3 @@
+Handle :exc:`ValueError`\s raised by :func:`os.stat` in
+:class:`filecmp.dircmp` and :func:`filecmp.cmpfiles`.
+Patch by Bénédikt Tran.