Skip to content

Commit f0e4c06

Browse files
committed
feat: --diff to show what failed --check
1 parent a863dfb commit f0e4c06

File tree

4 files changed

+69
-1
lines changed

4 files changed

+69
-1
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ These are changes to Cog over time.
2121
Unreleased
2222
----------
2323

24+
- Added a ``--diff`` option to show the diff of what changed to fail a
25+
``--check`` run.
26+
2427
- Embedded code can change the current directory, cog will change back to the
2528
original directory when the code is done.
2629

cogapp/cogapp.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Cog content generation tool."""
22

33
import copy
4+
import difflib
45
import getopt
56
import glob
67
import io
@@ -50,6 +51,7 @@
5051
-z The end-output marker can be omitted, and is assumed at eof.
5152
-v Print the version of cog and exit.
5253
--check Check that the files would not change if run again.
54+
--diff With --check, show a diff of what failed the check.
5355
--markers='START END END-OUTPUT'
5456
The patterns surrounding cog inline instructions. Should
5557
include three values separated by spaces, the start, end,
@@ -240,6 +242,7 @@ def __init__(self):
240242
self.prologue = ""
241243
self.print_output = False
242244
self.check = False
245+
self.diff = False
243246

244247
def __eq__(self, other):
245248
"""Comparison operator for tests to use."""
@@ -262,6 +265,7 @@ def parse_args(self, argv):
262265
"cdD:eI:n:o:rs:p:PUvw:xz",
263266
[
264267
"check",
268+
"diff",
265269
"markers=",
266270
"verbosity=",
267271
],
@@ -308,6 +312,8 @@ def parse_args(self, argv):
308312
self.eof_can_be_end = True
309313
elif o == "--check":
310314
self.check = True
315+
elif o == "--diff":
316+
self.diff = True
311317
elif o == "--markers":
312318
self._parse_markers(a)
313319
elif o == "--verbosity":
@@ -335,6 +341,9 @@ def validate(self):
335341
if self.replace and self.output_name:
336342
raise CogUsageError("Can't use -o with -r (they are opposites)")
337343

344+
if self.diff and not self.check:
345+
raise CogUsageError("Can't use --diff without --check")
346+
338347

339348
class Cog(Redirectable):
340349
"""The Cog engine."""
@@ -692,6 +701,18 @@ def process_one_file(self, fname):
692701
else:
693702
assert self.options.check
694703
self.check_failed = True
704+
if self.options.diff:
705+
old_lines = old_text.splitlines()
706+
new_lines = new_text.splitlines()
707+
diff = difflib.unified_diff(
708+
old_lines,
709+
new_lines,
710+
fromfile=f"current {fname}",
711+
tofile=f"changed {fname}",
712+
lineterm="",
713+
)
714+
for diff_line in diff:
715+
self.prout(diff_line)
695716
finally:
696717
# The try-finally block is so we can print a partial line
697718
# with the name of the file, and print (changed) on the

cogapp/test_cogapp.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,12 @@ def test_no_dash_o_and_amp_file(self):
824824
with self.assertRaisesRegex(CogUsageError, r"^Can't use -o with &file$"):
825825
self.cog.callable_main(["argv0", "-o", "foo", "&cogfiles.txt"])
826826

827+
def test_no_diff_without_check(self):
828+
with self.assertRaisesRegex(
829+
CogUsageError, r"^Can't use --diff without --check$"
830+
):
831+
self.cog.callable_main(["argv0", "--diff"])
832+
827833
def test_dash_v(self):
828834
self.assertEqual(self.cog.main(["argv0", "-v"]), 0)
829835
output = self.output.getvalue()
@@ -2167,6 +2173,40 @@ def test_check_bad(self):
21672173
)
21682174
self.assert_made_files_unchanged(d)
21692175

2176+
def test_check_bad_with_diff(self):
2177+
d = {
2178+
"skittering.cog": """\
2179+
//[[[cog
2180+
for i in range(5): cog.outl(f"number {i}")
2181+
cog.outl("goodbye world")
2182+
//]]]
2183+
number 0
2184+
number 1
2185+
number 2
2186+
number 3
2187+
number 4
2188+
hello world
2189+
//[[[end]]]
2190+
""",
2191+
}
2192+
make_files(d)
2193+
self.run_check(["--diff", "skittering.cog"], status=5)
2194+
output = """\
2195+
Checking skittering.cog (changed)
2196+
--- current skittering.cog
2197+
+++ changed skittering.cog
2198+
@@ -7,5 +7,5 @@
2199+
number 2
2200+
number 3
2201+
number 4
2202+
-hello world
2203+
+goodbye world
2204+
//[[[end]]]
2205+
Check failed
2206+
"""
2207+
self.assertEqual(self.output.getvalue(), reindent_block(output))
2208+
self.assert_made_files_unchanged(d)
2209+
21702210
def test_check_mixed(self):
21712211
d = {
21722212
"unchanged.cog": """\

docs/running.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Cog is a command-line utility which takes arguments in standard form.
5656
-z The end-output marker can be omitted, and is assumed at eof.
5757
-v Print the version of cog and exit.
5858
--check Check that the files would not change if run again.
59+
--diff With --check, show a diff of what failed the check.
5960
--markers='START END END-OUTPUT'
6061
The patterns surrounding cog inline instructions. Should
6162
include three values separated by spaces, the start, end,
@@ -65,7 +66,7 @@ Cog is a command-line utility which takes arguments in standard form.
6566
1 lists only changed files, 0 lists no files.
6667
-h Print this help.
6768
68-
.. {{{end}}} (checksum: 159e7d7aebb9dcc98f250d47879703dd)
69+
.. {{{end}}} (checksum: d9737a8e9fc9c2fc78a5200f2fa8440f)
6970
7071
In addition to running cog as a command on the command line, you can also
7172
invoke it as a module with the Python interpreter:
@@ -203,6 +204,9 @@ You can use the ``--check`` option to run cog just to check that the files
203204
would not change if run again. This is useful in continuous integration to
204205
check that your files have been updated properly.
205206

207+
The ``--diff`` option will show a unified diff of the change that caused
208+
``--check`` to fail.
209+
206210

207211
Output line suffixes
208212
--------------------

0 commit comments

Comments
 (0)