mirror of
https://github.com/python/cpython.git
synced 2025-12-22 16:39:14 +00:00
gh-133722: Add Difflib theme to _colorize and 'color' option to difflib.unified_diff (#133725)
This commit is contained in:
parent
64ee1babfb
commit
34d7351ac7
7 changed files with 73 additions and 13 deletions
|
|
@ -278,7 +278,7 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module.
|
|||
emu
|
||||
|
||||
|
||||
.. function:: unified_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n')
|
||||
.. function:: unified_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n', *, color=False)
|
||||
|
||||
Compare *a* and *b* (lists of strings); return a delta (a :term:`generator`
|
||||
generating the delta lines) in unified diff format.
|
||||
|
|
@ -297,6 +297,10 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module.
|
|||
For inputs that do not have trailing newlines, set the *lineterm* argument to
|
||||
``""`` so that the output will be uniformly newline free.
|
||||
|
||||
Set *color* to ``True`` to enable output in color, similar to
|
||||
:program:`git diff --color`. Even if enabled, it can be
|
||||
:ref:`controlled using environment variables <using-on-controlling-color>`.
|
||||
|
||||
The unified diff format normally has a header for filenames and modification
|
||||
times. Any or all of these may be specified using strings for *fromfile*,
|
||||
*tofile*, *fromfiledate*, and *tofiledate*. The modification times are normally
|
||||
|
|
@ -319,6 +323,10 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module.
|
|||
|
||||
See :ref:`difflib-interface` for a more detailed example.
|
||||
|
||||
.. versionchanged:: next
|
||||
Added the *color* parameter.
|
||||
|
||||
|
||||
.. function:: diff_bytes(dfunc, a, b, fromfile=b'', tofile=b'', fromfiledate=b'', tofiledate=b'', n=3, lineterm=b'\n')
|
||||
|
||||
Compare *a* and *b* (lists of bytes objects) using *dfunc*; yield a
|
||||
|
|
|
|||
|
|
@ -229,6 +229,14 @@ dbm
|
|||
difflib
|
||||
-------
|
||||
|
||||
.. _whatsnew315-color-difflib:
|
||||
|
||||
* Introduced the optional *color* parameter to :func:`difflib.unified_diff`,
|
||||
enabling color output similar to :program:`git diff`.
|
||||
This can be controlled by :ref:`environment variables
|
||||
<using-on-controlling-color>`.
|
||||
(Contributed by Douglas Thor in :gh:`133725`.)
|
||||
|
||||
* Improved the styling of HTML diff pages generated by the :class:`difflib.HtmlDiff`
|
||||
class, and migrated the output to the HTML5 standard.
|
||||
(Contributed by Jiahao Li in :gh:`134580`.)
|
||||
|
|
|
|||
|
|
@ -172,7 +172,18 @@ class Argparse(ThemeSection):
|
|||
reset: str = ANSIColors.RESET
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class Difflib(ThemeSection):
|
||||
"""A 'git diff'-like theme for `difflib.unified_diff`."""
|
||||
added: str = ANSIColors.GREEN
|
||||
context: str = ANSIColors.RESET # context lines
|
||||
header: str = ANSIColors.BOLD # eg "---" and "+++" lines
|
||||
hunk: str = ANSIColors.CYAN # the "@@" lines
|
||||
removed: str = ANSIColors.RED
|
||||
reset: str = ANSIColors.RESET
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class Syntax(ThemeSection):
|
||||
prompt: str = ANSIColors.BOLD_MAGENTA
|
||||
keyword: str = ANSIColors.BOLD_BLUE
|
||||
|
|
@ -186,7 +197,7 @@ class Syntax(ThemeSection):
|
|||
reset: str = ANSIColors.RESET
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class Traceback(ThemeSection):
|
||||
type: str = ANSIColors.BOLD_MAGENTA
|
||||
message: str = ANSIColors.MAGENTA
|
||||
|
|
@ -198,7 +209,7 @@ class Traceback(ThemeSection):
|
|||
reset: str = ANSIColors.RESET
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class Unittest(ThemeSection):
|
||||
passed: str = ANSIColors.GREEN
|
||||
warn: str = ANSIColors.YELLOW
|
||||
|
|
@ -207,7 +218,7 @@ class Unittest(ThemeSection):
|
|||
reset: str = ANSIColors.RESET
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class Theme:
|
||||
"""A suite of themes for all sections of Python.
|
||||
|
||||
|
|
@ -215,6 +226,7 @@ class Theme:
|
|||
below.
|
||||
"""
|
||||
argparse: Argparse = field(default_factory=Argparse)
|
||||
difflib: Difflib = field(default_factory=Difflib)
|
||||
syntax: Syntax = field(default_factory=Syntax)
|
||||
traceback: Traceback = field(default_factory=Traceback)
|
||||
unittest: Unittest = field(default_factory=Unittest)
|
||||
|
|
@ -223,6 +235,7 @@ class Theme:
|
|||
self,
|
||||
*,
|
||||
argparse: Argparse | None = None,
|
||||
difflib: Difflib | None = None,
|
||||
syntax: Syntax | None = None,
|
||||
traceback: Traceback | None = None,
|
||||
unittest: Unittest | None = None,
|
||||
|
|
@ -234,6 +247,7 @@ class Theme:
|
|||
"""
|
||||
return type(self)(
|
||||
argparse=argparse or self.argparse,
|
||||
difflib=difflib or self.difflib,
|
||||
syntax=syntax or self.syntax,
|
||||
traceback=traceback or self.traceback,
|
||||
unittest=unittest or self.unittest,
|
||||
|
|
@ -249,6 +263,7 @@ class Theme:
|
|||
"""
|
||||
return cls(
|
||||
argparse=Argparse.no_colors(),
|
||||
difflib=Difflib.no_colors(),
|
||||
syntax=Syntax.no_colors(),
|
||||
traceback=Traceback.no_colors(),
|
||||
unittest=Unittest.no_colors(),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ __all__ = ['get_close_matches', 'ndiff', 'restore', 'SequenceMatcher',
|
|||
'Differ','IS_CHARACTER_JUNK', 'IS_LINE_JUNK', 'context_diff',
|
||||
'unified_diff', 'diff_bytes', 'HtmlDiff', 'Match']
|
||||
|
||||
from _colorize import can_colorize, get_theme
|
||||
from heapq import nlargest as _nlargest
|
||||
from collections import namedtuple as _namedtuple
|
||||
from types import GenericAlias
|
||||
|
|
@ -1094,7 +1095,7 @@ def _format_range_unified(start, stop):
|
|||
return '{},{}'.format(beginning, length)
|
||||
|
||||
def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
|
||||
tofiledate='', n=3, lineterm='\n'):
|
||||
tofiledate='', n=3, lineterm='\n', *, color=False):
|
||||
r"""
|
||||
Compare two sequences of lines; generate the delta as a unified diff.
|
||||
|
||||
|
|
@ -1111,6 +1112,10 @@ def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
|
|||
For inputs that do not have trailing newlines, set the lineterm
|
||||
argument to "" so that the output will be uniformly newline free.
|
||||
|
||||
Set 'color' to True to enable output in color, similar to
|
||||
'git diff --color'. Even if enabled, it can be
|
||||
controlled using environment variables such as 'NO_COLOR'.
|
||||
|
||||
The unidiff format normally has a header for filenames and modification
|
||||
times. Any or all of these may be specified using strings for
|
||||
'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
|
||||
|
|
@ -1134,6 +1139,11 @@ def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
|
|||
four
|
||||
"""
|
||||
|
||||
if color and can_colorize():
|
||||
t = get_theme(force_color=True).difflib
|
||||
else:
|
||||
t = get_theme(force_no_color=True).difflib
|
||||
|
||||
_check_types(a, b, fromfile, tofile, fromfiledate, tofiledate, lineterm)
|
||||
started = False
|
||||
for group in SequenceMatcher(None,a,b).get_grouped_opcodes(n):
|
||||
|
|
@ -1141,25 +1151,25 @@ def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
|
|||
started = True
|
||||
fromdate = '\t{}'.format(fromfiledate) if fromfiledate else ''
|
||||
todate = '\t{}'.format(tofiledate) if tofiledate else ''
|
||||
yield '--- {}{}{}'.format(fromfile, fromdate, lineterm)
|
||||
yield '+++ {}{}{}'.format(tofile, todate, lineterm)
|
||||
yield f'{t.header}--- {fromfile}{fromdate}{lineterm}{t.reset}'
|
||||
yield f'{t.header}+++ {tofile}{todate}{lineterm}{t.reset}'
|
||||
|
||||
first, last = group[0], group[-1]
|
||||
file1_range = _format_range_unified(first[1], last[2])
|
||||
file2_range = _format_range_unified(first[3], last[4])
|
||||
yield '@@ -{} +{} @@{}'.format(file1_range, file2_range, lineterm)
|
||||
yield f'{t.hunk}@@ -{file1_range} +{file2_range} @@{lineterm}{t.reset}'
|
||||
|
||||
for tag, i1, i2, j1, j2 in group:
|
||||
if tag == 'equal':
|
||||
for line in a[i1:i2]:
|
||||
yield ' ' + line
|
||||
yield f'{t.context} {line}{t.reset}'
|
||||
continue
|
||||
if tag in {'replace', 'delete'}:
|
||||
for line in a[i1:i2]:
|
||||
yield '-' + line
|
||||
yield f'{t.removed}-{line}{t.reset}'
|
||||
if tag in {'replace', 'insert'}:
|
||||
for line in b[j1:j2]:
|
||||
yield '+' + line
|
||||
yield f'{t.added}+{line}{t.reset}'
|
||||
|
||||
|
||||
########################################################################
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import difflib
|
||||
from test.support import findfile
|
||||
from test.support import findfile, force_colorized
|
||||
import unittest
|
||||
import doctest
|
||||
import sys
|
||||
|
|
@ -355,6 +355,22 @@ class TestOutputFormat(unittest.TestCase):
|
|||
self.assertEqual(fmt(3,6), '4,6')
|
||||
self.assertEqual(fmt(0,0), '0')
|
||||
|
||||
@force_colorized
|
||||
def test_unified_diff_colored_output(self):
|
||||
args = [['one', 'three'], ['two', 'three'], 'Original', 'Current',
|
||||
'2005-01-26 23:30:50', '2010-04-02 10:20:52']
|
||||
actual = list(difflib.unified_diff(*args, lineterm='', color=True))
|
||||
|
||||
expect = [
|
||||
"\033[1m--- Original\t2005-01-26 23:30:50\033[0m",
|
||||
"\033[1m+++ Current\t2010-04-02 10:20:52\033[0m",
|
||||
"\033[36m@@ -1,2 +1,2 @@\033[0m",
|
||||
"\033[31m-one\033[0m",
|
||||
"\033[32m+two\033[0m",
|
||||
"\033[0m three\033[0m",
|
||||
]
|
||||
self.assertEqual(expect, actual)
|
||||
|
||||
|
||||
class TestBytes(unittest.TestCase):
|
||||
# don't really care about the content of the output, just the fact
|
||||
|
|
|
|||
|
|
@ -1902,6 +1902,7 @@ Nicolas M. Thiéry
|
|||
James Thomas
|
||||
Reuben Thomas
|
||||
Robin Thomas
|
||||
Douglas Thor
|
||||
Brian Thorne
|
||||
Christopher Thorne
|
||||
Stephen Thorne
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Added a *color* option to :func:`difflib.unified_diff` that colors output
|
||||
similar to :program:`git diff`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue