Skip to content

Commit c3b6dbf

Browse files
authored
gh-115801: Only allow sequence of strings as input for difflib.unified_diff (GH-118333)
1 parent b90bd3e commit c3b6dbf

File tree

3 files changed

+31
-6
lines changed

3 files changed

+31
-6
lines changed

Lib/difflib.py

+6
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,12 @@ def _check_types(a, b, *args):
12641264
if b and not isinstance(b[0], str):
12651265
raise TypeError('lines to compare must be str, not %s (%r)' %
12661266
(type(b[0]).__name__, b[0]))
1267+
if isinstance(a, str):
1268+
raise TypeError('input must be a sequence of strings, not %s' %
1269+
type(a).__name__)
1270+
if isinstance(b, str):
1271+
raise TypeError('input must be a sequence of strings, not %s' %
1272+
type(b).__name__)
12671273
for arg in args:
12681274
if not isinstance(arg, str):
12691275
raise TypeError('all arguments must be str, not: %r' % (arg,))

Lib/test/test_difflib.py

+24-6
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def test_close_matches_aligned(self):
295295

296296
class TestOutputFormat(unittest.TestCase):
297297
def test_tab_delimiter(self):
298-
args = ['one', 'two', 'Original', 'Current',
298+
args = [['one'], ['two'], 'Original', 'Current',
299299
'2005-01-26 23:30:50', '2010-04-02 10:20:52']
300300
ud = difflib.unified_diff(*args, lineterm='')
301301
self.assertEqual(list(ud)[0:2], [
@@ -307,7 +307,7 @@ def test_tab_delimiter(self):
307307
"--- Current\t2010-04-02 10:20:52"])
308308

309309
def test_no_trailing_tab_on_empty_filedate(self):
310-
args = ['one', 'two', 'Original', 'Current']
310+
args = [['one'], ['two'], 'Original', 'Current']
311311
ud = difflib.unified_diff(*args, lineterm='')
312312
self.assertEqual(list(ud)[0:2], ["--- Original", "+++ Current"])
313313

@@ -447,6 +447,28 @@ def assertDiff(expect, actual):
447447
lineterm=b'')
448448
assertDiff(expect, actual)
449449

450+
451+
class TestInputTypes(unittest.TestCase):
452+
def _assert_type_error(self, msg, generator, *args):
453+
with self.assertRaises(TypeError) as ctx:
454+
list(generator(*args))
455+
self.assertEqual(msg, str(ctx.exception))
456+
457+
def test_input_type_checks(self):
458+
unified = difflib.unified_diff
459+
context = difflib.context_diff
460+
461+
expect = "input must be a sequence of strings, not str"
462+
self._assert_type_error(expect, unified, 'a', ['b'])
463+
self._assert_type_error(expect, context, 'a', ['b'])
464+
465+
self._assert_type_error(expect, unified, ['a'], 'b')
466+
self._assert_type_error(expect, context, ['a'], 'b')
467+
468+
expect = "lines to compare must be str, not NoneType (None)"
469+
self._assert_type_error(expect, unified, ['a'], [None])
470+
self._assert_type_error(expect, context, ['a'], [None])
471+
450472
def test_mixed_types_content(self):
451473
# type of input content must be consistent: all str or all bytes
452474
a = [b'hello']
@@ -495,10 +517,6 @@ def test_mixed_types_dates(self):
495517
b = ['bar\n']
496518
list(difflib.unified_diff(a, b, 'a', 'b', datea, dateb))
497519

498-
def _assert_type_error(self, msg, generator, *args):
499-
with self.assertRaises(TypeError) as ctx:
500-
list(generator(*args))
501-
self.assertEqual(msg, str(ctx.exception))
502520

503521
class TestJunkAPIs(unittest.TestCase):
504522
def test_is_line_junk_true(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Raise ``TypeError`` when passing a string to :func:`difflib.unified_diff` and :func:`difflib.context_diff`.

0 commit comments

Comments
 (0)