8000 Add dircmp shallow option. · python/cpython@6224a0f · GitHub
[go: up one dir, main page]

Skip to content

Commit 6224a0f

Browse files
aunzatTobias Rautenkranz
authored andcommitted
Add dircmp shallow option.
1 parent e57ecf6 commit 6224a0f

File tree

4 files changed

+90
-32
lines changed

4 files changed

+90
-32
lines changed

Doc/library/filecmp.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ The :mod:`filecmp` module defines the following functions:
7070
The :class:`dircmp` class
7171
-------------------------
7272

73-
.. class:: dircmp(a, b, ignore=None, hide=None)
73+
.. class:: dircmp(a, b, ignore=None, hide=None, shallow=True)
7474

7575
Construct a new directory comparison object, to compare the directories *a*
7676
and *b*. *ignore* is a list of names to ignore, and defaults to
7777
:const:`filecmp.DEFAULT_IGNORES`. *hide* is a list of names to hide, and
7878
defaults to ``[os.curdir, os.pardir]``.
7979

80-
The :class:`dircmp` class compares files by doing *shallow* comparisons
81-
as described for :func:`filecmp.cmp`.
80+
The *shallow* parameter has the same meaning and default value as for
81+
:func:`filecmp.cmp`.
8282

8383
The :class:`dircmp` class provides the following methods:
8484

Lib/filecmp.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,15 @@ def _do_cmp(f1, f2):
8888
class dircmp:
8989
"""A class that manages the comparison of 2 directories.
9090
91-
dircmp(a, b, ignore=None, hide=None)
91+
dircmp(a, b, ignore=None, hide=None, shallow=True)
9292
A and B are directories.
9393
IGNORE is a list of names to ignore,
9494
defaults to DEFAULT_IGNORES.
9595
HIDE is a list of names to hide,
9696
defaults to [os.curdir, os.pardir].
97+
SHALLOW specifies whether to just check the stat signature (do not read
98+
the files).
99+
defaults to True.
97100
98101
High level usage:
99102
x = dircmp(dir1, dir2)
@@ -121,7 +124,7 @@ class dircmp:
121124
in common_dirs.
122125
"""
123126

124-
def __init__(self, a, b, ignore=None, hide=None): # Initialize
127+
def __init__(self, a, b, ignore=None, hide=None, shallow=True): # Initialize
125128
self.left = a
126129
self.right = b
127130
if hide is None:
@@ -132,6 +135,7 @@ def __init__(self, a, b, ignore=None, hide=None): # Initialize
132135
self.ignore = DEFAULT_IGNORES
133136
else:
134137
self.ignore = ignore
138+
self.shallow = shallow
135139

136140
def phase0(self): # Compare everything except common subdirectories
137141
self.left_list = _filter(os.listdir(self.left),
@@ -184,7 +188,7 @@ def phase2(self): # Distinguish files, directories, funnies
184188
self.common_funny.append(x)
185189

186190
def phase3(self): # Find out differences between common files
187-
xx = cmpfiles(self.left, self.right, self.common_files)
191+
xx = cmpfiles(self.left, self.right, self.common_files, self.shallow)
188192
self.same_files, self.diff_files, self.funny_files = xx
189193

190194
def phase4(self): # Find out differences between common subdirectories
@@ -196,7 +200,8 @@ def phase4(self): # Find out differences between common subdirectories
196200
for x in self.common_dirs:
197201
a_x = os.path.join(self.left, x)
198202
b_x = os.path.join(self.right, x)
199-
self.subdirs[x] = self.__class__(a_x, b_x, self.ignore, self.hide)
203+
self.subdirs[x] = self.__class__(a_x, b_x, self.ignore, self.hide,
204+
self.shallow)
200205

201206
def phase4_closure(self): # Recursively call phase4() on subdirectories
202207
self.phase4()

Lib/test/test_filecmp.py

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,24 @@ def setUp(self):
1313
self.name = os_helper.TESTFN
1414
self.name_same = os_helper.TESTFN + '-same'
1515
self.name_diff = os_helper.TESTFN + '-diff'
16+
self.name_same_shallow = os_helper.TESTFN + '-same-shallow'
1617
data = 'Contents of file go here.\n'
1718
for name in [self.name, self.name_same, self.name_diff]:
1819
with open(name, 'w', encoding="utf-8") as output:
1920
output.write(data)
2021

2122
with open(self.name_diff, 'a+', encoding="utf-8") as output:
2223
output.write('An extra line.\n')
24+
with open(self.name_same_shallow , 'wb') as output:
25+
# same file size; but different content (i.e. all zero)
26+
output.write(bytes(len(data.encode())))
2327
self.dir = tempfile.gettempdir()
2428

2529
def tearDown(self):
2630
os.unlink(self.name)
2731
os.unlink(self.name_same)
2832
os.unlink(self.name_diff)
33+
os.unlink(self.name_same_shallow)
2934

3035
def test_matching(self):
3136
self.assertTrue(filecmp.cmp(self.name, self.name),
@@ -36,12 +41,17 @@ def test_matching(self):
3641
"Comparing file to identical file fails")
3742
self.assertTrue(filecmp.cmp(self.name, self.name_same, shallow=False),
3843
"Comparing file to identical file fails")
44+
self.assertTrue(filecmp.cmp(self.name, self.name_same_shallow),
45+
"Shallow identical files should be considered equal")
3946

4047
def test_different(self):
4148
self.assertFalse(filecmp.cmp(self.name, self.name_diff),
4249
"Mismatched files compare as equal")
4350
self.assertFalse(filecmp.cmp(self.name, self.dir),
4451
"File and directory compare as equal")
52+
self.assertFalse(filecmp.cmp(self.name, self.name_same_shallow,
53+
shallow=False),
54+
"Mismatched file to shallow identical file compares as equal")
4555

4656
def test_cache_clear(self):
4757
first_compare = filecmp.cmp(self.name, self.name_same, shallow=False)
@@ -56,14 +66,17 @@ def setUp(self):
5666
self.dir = os.path.join(tmpdir, 'dir')
5767
self.dir_same = os.path.join(tmpdir, 'dir-same')
5868
self.dir_diff = os.path.join(tmpdir, 'dir-diff')
69+
self.dir_diff_file = os.path.join(tmpdir, 'dir-diff-file')
70+
self.dir_same_shallow = os.path.join(tmpdir, 'dir-same-shallow')
5971

6072
# Another dir is created under dir_same, but it has a name from the
6173
# ignored list so it should not affect testing results.
6274
self.dir_ignored = os.path.join(self.dir_same, '.hg')
6375

6476
self.caseinsensitive = os.path.normcase('A') == os.path.normcase('a')
6577
data = 'Contents of file go here.\n'
66-
for dir in (self.dir, self.dir_same, self.dir_diff, self.dir_ignored):
78+
for dir in (self.dir, self.dir_same, self.dir_same_shallow,
79+
self.dir_diff, self.dir_ignored, self.dir_diff_file):
6780
shutil.rmtree(dir, True)
6881
os.mkdir(dir)
6982
subdir_path = os.path.join(dir, 'subdir')
@@ -72,14 +85,24 @@ def setUp(self):
7285
fn = 'FiLe' # Verify case-insensitive comparison
7386
else:
7487
fn = 'file'
75-
with open(os.path.join(dir, fn), 'w', encoding="utf-8") as output:
76-
output.write(data)
88+
if dir is self.dir_same_shallow:
89+
with open(os.path.join(dir, fn) , 'wb') as output:
90+
# same file size; but different content (i.e. all zero)
91+
output.write(bytes(len(data.encode())))
92+
else:
93+
with open(os.path.join(dir, fn), 'w', encoding="utf-8") as output:
94+
output.write(data)
7795

7896
with open(os.path.join(self.dir_diff, 'file2'), 'w', encoding="utf-8") as output:
7997
output.write('An extra file.\n')
8098

99+
# Add different file2
100+
with open(os.path.join(self.dir_diff_file, 'file2'), 'w', encoding="utf-8") as output:
101+
output.write('Different contents.\n')
102+
81103
def tearDown(self):
82-
for dir in (self.dir, self.dir_same, self.dir_diff):
104+
for dir in (self.dir, self.dir_same, self.dir_diff,
105+
self.dir_same_shallow, self.dir_diff_file):
83106
shutil.rmtree(dir)
84107

85108
def test_default_ignores(self):
@@ -102,11 +125,7 @@ def test_cmpfiles(self):
102125
shallow=False),
103126
"Comparing directory to same fails")
104127

105-
# Add different file2
106-
with open(os.path.join(self.dir, 'file2'), 'w', encoding="utf-8") as output:
107-
output.write('Different contents.\n')
108-
109-
self.assertFalse(filecmp.cmpfiles(self.dir, self.dir_same,
128+
self.assertFalse(filecmp.cmpfiles(self.dir, self.dir_diff_file,
110129
['file', 'file2']) ==
111130
(['file'], ['file2'], []),
112131
"Comparing mismatched directories fails")
@@ -116,11 +135,22 @@ def _assert_lists(self, actual, expected):
116135
"""Assert that two lists are equal, up to ordering."""
117136
self.assertEqual(sorted(actual), sorted(expected))
118137

138+
def test_dircmp_identical_directories(self):
139+
self._assert_dircmp_identical_directories()
140+
self._assert_dircmp_identical_directories(shallow=False)
141+
142+
def test_dircmp_different_file(self):
143+
self._assert_dircmp_different_file()
144+
self._assert_dircmp_different_file(shallow=False)
119145

120-
def test_dircmp(self):
146+
def test_dircmp_different_directories(self):
147+
self._assert_dircmp_different_directories()
148+
self._assert_dircmp_different_directories(shallow=False)
149+
150+
def _assert_dircmp_identical_directories(self, **options):
121151
# Check attributes for comparison of two identical directories
122152
left_dir, right_dir = self.dir, self.dir_same
123-
d = filecmp.dircmp(left_dir, right_dir)
153+
d = filecmp.dircmp(left_dir, right_dir, **options)
124154
self.assertEqual(d.left, left_dir)
125155
self.assertEqual(d.right, right_dir)
126156
if self.caseinsensitive:
@@ -142,9 +172,10 @@ def test_dircmp(self):
142172
]
143173
self._assert_report(d.report, expected_report)
144174

175+
def _assert_dircmp_different_directories(self, **options):
145176
# Check attributes for comparison of two different directories (right)
146177
left_dir, right_dir = self.dir, self.dir_diff
147-
d = filecmp.dircmp(left_dir, right_dir)
178+
d = filecmp.dircmp(left_dir, right_dir, **options)
148179
self.assertEqual(d.left, left_dir)
149180
self.assertEqual(d.right, right_dir)
150181
self._assert_lists(d.left_list, ['file', 'subdir'])
@@ -164,12 +195,8 @@ def test_dircmp(self):
164195
self._assert_report(d.report, expected_report)
165196

166197
# Check attributes for comparison of two different directories (left)
167-
left_dir, right_dir = self.dir, self.dir_diff
168-
shutil.move(
169-
os.path.join(self.dir_diff, 'file2'),
170-
os.path.join(self.dir, 'file2')
171-
)
172-
d = filecmp.dircmp(left_dir, right_dir)
198+
left_dir, right_dir = self.dir_diff, self.dir
199+
d = filecmp.dircmp(left_dir, right_dir, **options)
173200
self.assertEqual(d.left, left_dir)
174201
self.assertEqual(d.right, right_dir)
175202
self._assert_lists(d.left_list, ['file', 'file2', 'subdir'])
@@ -180,27 +207,51 @@ def test_dircmp(self):
180207
self.assertEqual(d.same_files, ['file'])
181208
self.assertEqual(d.diff_files, [])
182209
expected_report = [
183-
"diff {} {}".format(self.dir, self.dir_diff),
184-
"Only in {} : ['file2']".format(self.dir),
210+
"diff {} {}".format(self.dir_diff, self.dir),
211+
"Only in {} : ['file2']".format(self.dir_diff),
185212
"Identical files : ['file']",
186213
"Common subdirectories : ['subdir']",
187214
]
188215
self._assert_report(d.report, expected_report)
189216

190-
# Add different file2
191-
with open(os.path.join(self.dir_diff, 'file2'), 'w', encoding="utf-8") as output:
192-
output.write('Different contents.\n')
193-
d = filecmp.dircmp(self.dir, self.dir_diff)
217+
218+
def _assert_dircmp_different_file(self, **options):
219+
# A different file2
220+
d = filecmp.dircmp(self.dir_diff, self.dir_diff_file, **options)
194221
self.assertEqual(d.same_files, ['file'])
195222
self.assertEqual(d.diff_files, ['file2'])
196223
expected_report = [
197-
"diff {} {}".format(self.dir, self.dir_diff),
224+
"diff {} {}".format(self.dir_diff, self.dir_diff_file),
198225
"Identical files : ['file']",
199226
"Differing files : ['file2']",
200227
"Common subdirectories : ['subdir']",
201228
]
202229
self._assert_report(d.report, expected_report)
203230

231+
def test_dircmp_no_shallow_different_file(self):
232+
# A non shallow different file2
233+
d = filecmp.dircmp(self.dir, self.dir_same_shallow, shallow=False)
234+
self.assertEqual(d.same_files, [])
235+
self.assertEqual(d.diff_files, ['file'])
236+
expected_report = [
237+
"diff {} {}".format(self.dir, self.dir_same_shallow),
238+
"Differing files : ['file']",
239+
"Common subdirectories : ['subdir']",
240+
]
241+
self._assert_report(d.report, expected_report)
242+
243+
def test_dircmp_shallow_same_file(self):
244+
# A non shallow different file2
245+
d = filecmp.dircmp(self.dir, self.dir_same_shallow)
246+
self.assertEqual(d.same_files, ['file'])
247+
self.assertEqual(d.diff_files, [])
248+
expected_report = [
249+
"diff {} {}".format(self.dir, self.dir_same_shallow),
250+
"Identical files : ['file']",
251+
"Common subdirectories : ['subdir']",
252+
]
253+
self._assert_report(d.report, expected_report)
254+
204255
def test_dircmp_subdirs_type(self):
205256
"""Check that dircmp.subdirs respects subclassing."""
206257
class MyDirCmp(filecmp.dircmp):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add option for *non-shallow* comparisons to :class:`filecmp.dircmp` like
2+
:func:`filecmp.cmp`.

0 commit comments

Comments
 (0)
0