diff --git a/Doc/library/filecmp.rst b/Doc/library/filecmp.rst --- a/Doc/library/filecmp.rst +++ b/Doc/library/filecmp.rst @@ -152,5 +152,6 @@ .. attribute:: subdirs A dictionary mapping names in :attr:`common_dirs` to :class:`dircmp` - objects. + instances (or to instances of the object's class if :class:`dircmp` + was subclassed). diff --git a/Lib/filecmp.py b/Lib/filecmp.py --- a/Lib/filecmp.py +++ b/Lib/filecmp.py @@ -182,7 +182,7 @@ for x in self.common_dirs: a_x = os.path.join(self.left, x) b_x = os.path.join(self.right, x) - self.subdirs[x] = dircmp(a_x, b_x, self.ignore, self.hide) + self.subdirs[x] = self.__class__(a_x, b_x, self.ignore, self.hide) def phase4_closure(self): # Recursively call phase4() on subdirectories self.phase4() diff --git a/Lib/test/test_filecmp.py b/Lib/test/test_filecmp.py --- a/Lib/test/test_filecmp.py +++ b/Lib/test/test_filecmp.py @@ -51,6 +51,8 @@ for dir in [self.dir, self.dir_same, self.dir_diff]: shutil.rmtree(dir, True) os.mkdir(dir) + subdir_path = os.path.join(dir, 'subdir') + os.mkdir(subdir_path) if self.caseinsensitive and dir is self.dir_same: fn = 'FiLe' # Verify case-insensitive comparison else: @@ -95,24 +97,31 @@ (['file'], ['file2'], []), "Comparing mismatched directories fails") + def _assert_lists(self, actual, expected): + """Assert that two lists are equal, up to ordering.""" + self.assertEqual(sorted(actual), sorted(expected)) def test_dircmp(self): # Check attributes for comparison of two identical directories d = filecmp.dircmp(self.dir, self.dir_same) if self.caseinsensitive: - self.assertEqual([d.left_list, d.right_list],[['file'], ['FiLe']]) + self._assert_lists(d.left_list, ['file', 'subdir']) + self._assert_lists(d.right_list, ['file', 'subdir']) else: - self.assertEqual([d.left_list, d.right_list],[['file'], ['file']]) - self.assertEqual(d.common, ['file']) + self._assert_lists(d.left_list, ['file', 'subdir']) + self._assert_lists(d.right_list, ['file', 'subdir']) + self._assert_lists(d.common, ['file', 'subdir']) + self.assertEqual(d.common_dirs, ['subdir']) self.assertTrue(d.left_only == d.right_only == []) self.assertEqual(d.same_files, ['file']) self.assertEqual(d.diff_files, []) # Check attributes for comparison of two different directories d = filecmp.dircmp(self.dir, self.dir_diff) - self.assertEqual(d.left_list, ['file']) - self.assertTrue(d.right_list == ['file', 'file2']) - self.assertEqual(d.common, ['file']) + self._assert_lists(d.left_list, ['file', 'subdir']) + self._assert_lists(d.right_list, ['file', 'file2', 'subdir']) + self._assert_lists(d.common, ['file', 'subdir']) + self.assertEqual(d.common_dirs, ['subdir']) self.assertEqual(d.left_only, []) self.assertEqual(d.right_only, ['file2']) self.assertEqual(d.same_files, ['file']) @@ -126,6 +135,16 @@ self.assertEqual(d.same_files, ['file']) self.assertEqual(d.diff_files, ['file2']) + def test_dircmp_subdirs_type(self): + """Check that dircmp.subdirs respects subclassing.""" + class MyDirCmp(filecmp.dircmp): + pass + d = MyDirCmp(self.dir, self.dir_diff) + sub_dirs = d.subdirs + self.assertEqual(list(sub_dirs.keys()), ['subdir']) + sub_dcmp = sub_dirs['subdir'] + self.assertEqual(type(sub_dcmp), MyDirCmp) + def test_main(): support.run_unittest(FileCompareTestCase, DirCompareTestCase) diff --git a/Misc/NEWS b/Misc/NEWS --- a/Misc/NEWS +++ b/Misc/NEWS @@ -55,6 +55,9 @@ Library ------- +- Issue #15450: Ensure that filecmp's dircmp.subdirs behaves as expected + when subclassing dircmp. Patch by Chris Jerdonek. + - Issue #6056: Make multiprocessing use setblocking(True) on the sockets it uses. Original patch by J Derek Wilson.