diff-cover 9.2.4__tar.gz → 9.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. {diff_cover-9.2.4 → diff_cover-9.3.1}/PKG-INFO +12 -11
  2. {diff_cover-9.2.4 → diff_cover-9.3.1}/README.rst +9 -7
  3. diff_cover-9.3.1/diff_cover/__init__.py +5 -0
  4. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/command_runner.py +1 -12
  5. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/diff_cover_tool.py +78 -31
  6. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/diff_quality_tool.py +28 -36
  7. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/diff_reporter.py +2 -2
  8. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/git_diff.py +16 -11
  9. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/git_path.py +1 -4
  10. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/violationsreporters/java_violations_reporter.py +1 -7
  11. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/violationsreporters/violations_reporter.py +14 -0
  12. {diff_cover-9.2.4 → diff_cover-9.3.1}/pyproject.toml +28 -5
  13. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/add_html_report.html +21 -21
  14. diff_cover-9.3.1/tests/fixtures/add_json_report.json +49 -0
  15. diff_cover-9.3.1/tests/fixtures/add_markdown_report.md +32 -0
  16. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/moved_html_report.html +21 -21
  17. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/mult_inputs_html_report.html +21 -21
  18. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pycodestyle_violations_report.html +25 -25
  19. diff_cover-9.3.1/tests/fixtures/pycodestyle_violations_report.json +40 -0
  20. diff_cover-9.3.1/tests/fixtures/pycodestyle_violations_report.md +12 -0
  21. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pyflakes_violations_report.html +14 -14
  22. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pylint_violations_report.html +30 -34
  23. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/subdir_coverage_html_report.html +21 -21
  24. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/unicode_html_report.html +21 -21
  25. diff_cover-9.3.1/tests/test_diff_cover_tool.py +126 -0
  26. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_diff_quality_main.py +2 -2
  27. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_git_diff.py +10 -0
  28. diff_cover-9.3.1/tests/test_integration.py +630 -0
  29. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_report_generator.py +2 -2
  30. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_violations_reporter.py +79 -24
  31. diff_cover-9.2.4/diff_cover/__init__.py +0 -13
  32. diff_cover-9.2.4/tests/test_diff_cover_tool.py +0 -82
  33. diff_cover-9.2.4/tests/test_integration.py +0 -646
  34. {diff_cover-9.2.4 → diff_cover-9.3.1}/LICENSE +0 -0
  35. {diff_cover-9.2.4 → diff_cover-9.3.1}/NOTICE +0 -0
  36. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/config_parser.py +0 -0
  37. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/hook.py +0 -0
  38. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/hookspecs.py +0 -0
  39. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/report_generator.py +0 -0
  40. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/snippets.py +0 -0
  41. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/console_coverage_report.txt +0 -0
  42. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/console_quality_report.txt +0 -0
  43. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/external_style.css +0 -0
  44. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/html_coverage_report.html +0 -0
  45. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/html_quality_report.html +0 -0
  46. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/markdown_coverage_report.md +0 -0
  47. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/markdown_quality_report.md +0 -0
  48. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/snippet_content.html +0 -0
  49. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/snippet_content.md +0 -0
  50. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/snippet_content.txt +0 -0
  51. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/templates/snippet_style.html +0 -0
  52. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/util.py +0 -0
  53. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/violationsreporters/__init__.py +0 -0
  54. {diff_cover-9.2.4 → diff_cover-9.3.1}/diff_cover/violationsreporters/base.py +0 -0
  55. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/__init__.py +0 -0
  56. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/add_console_report.txt +0 -0
  57. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/changed_console_report.txt +0 -0
  58. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/changed_html_report.html +0 -0
  59. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/coverage.xml +0 -0
  60. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/coverage1.xml +0 -0
  61. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/coverage2.xml +0 -0
  62. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/coverage_missing_lines.xml +0 -0
  63. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/delete_console_report.txt +0 -0
  64. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/delete_html_report.html +0 -0
  65. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/dotnet_coverage.xml +0 -0
  66. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/dotnet_coverage_console_report.txt +0 -0
  67. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/empty.txt +0 -0
  68. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/empty_pycodestyle_violations.txt +0 -0
  69. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/expand_console_report.txt +0 -0
  70. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/external_css_html_report.html +0 -0
  71. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/external_style.css +0 -0
  72. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_add.txt +0 -0
  73. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_changed.txt +0 -0
  74. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_code_dupe.txt +0 -0
  75. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_delete.txt +0 -0
  76. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_dotnet.txt +0 -0
  77. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_external_css.txt +0 -0
  78. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_lua.txt +0 -0
  79. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_moved.txt +0 -0
  80. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_mult.txt +0 -0
  81. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_subdir.txt +0 -0
  82. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_unicode.txt +0 -0
  83. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_violations.txt +0 -0
  84. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/git_diff_violations_two_files.txt +0 -0
  85. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/hello.py +0 -0
  86. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/hi.py +0 -0
  87. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/html_report.html +0 -0
  88. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/html_report_empty.html +0 -0
  89. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/html_report_one_snippet.html +0 -0
  90. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/html_report_two_snippets.html +0 -0
  91. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/lcov.info +0 -0
  92. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/lua_console_report.txt +0 -0
  93. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/luacoverage.xml +0 -0
  94. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/markdown_report_one_snippet.md +0 -0
  95. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/markdown_report_two_snippets.md +0 -0
  96. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/moved_console_report.txt +0 -0
  97. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/moved_coverage.xml +0 -0
  98. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/mult_inputs_console_report.txt +0 -0
  99. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pycodestyle_report.txt +0 -0
  100. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pycodestyle_violations_report.txt +0 -0
  101. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pycodestyle_violations_report_external_css.html +0 -0
  102. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pyflakes_two_files.txt +0 -0
  103. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pyflakes_violations_report.txt +0 -0
  104. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pylint_dupe.txt +0 -0
  105. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pylint_dupe_violations_report.txt +0 -0
  106. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pylint_report.txt +0 -0
  107. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pylint_violations_console_report.txt +0 -0
  108. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pylint_violations_report.txt +0 -0
  109. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/pylintrc +0 -0
  110. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/show_uncovered_lines_console.txt +0 -0
  111. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet.css +0 -0
  112. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_8859.py +0 -0
  113. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_arabic_output.html +0 -0
  114. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_default.html +0 -0
  115. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_invalid_violations.html +0 -0
  116. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_list.html +0 -0
  117. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_list.md +0 -0
  118. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_list2.md +0 -0
  119. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_list3.md +0 -0
  120. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_no_filename_ext.html +0 -0
  121. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_src.py +0 -0
  122. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_src2.cpp +0 -0
  123. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_src3.cpp +0 -0
  124. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_unicode.html +0 -0
  125. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_unicode.py +0 -0
  126. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/snippet_unicode_html_output.html +0 -0
  127. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/subdir_coverage_console_report.txt +0 -0
  128. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/test_src.txt +0 -0
  129. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/unicode_console_report.txt +0 -0
  130. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/unicode_coverage.xml +0 -0
  131. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/unicode_test_src.txt +0 -0
  132. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/fixtures/violations_test_file.py +0 -0
  133. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/helpers.py +0 -0
  134. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/snippet_list_unicode.html +0 -0
  135. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_clover_violations_reporter/test.xml +0 -0
  136. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_clover_violations_reporter.py +0 -0
  137. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_config_parser.py +0 -0
  138. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_diff_cover_main.py +0 -0
  139. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_diff_reporter.py +0 -0
  140. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_git_diff_file.py +0 -0
  141. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_git_path.py +0 -0
  142. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_java_violations_reporter.py +0 -0
  143. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_snippets.py +0 -0
  144. {diff_cover-9.2.4 → diff_cover-9.3.1}/tests/test_util.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: diff_cover
3
- Version: 9.2.4
3
+ Version: 9.3.1
4
4
  Summary: Run coverage and linting reports on diffs
5
5
  License: Apache-2.0
6
6
  Author: See Contributors
7
- Requires-Python: >=3.9.17,<4.0.0
7
+ Requires-Python: >=3.9
8
8
  Classifier: Development Status :: 5 - Production/Stable
9
9
  Classifier: Environment :: Console
10
10
  Classifier: Intended Audience :: Developers
@@ -12,11 +12,11 @@ Classifier: License :: OSI Approved :: Apache Software License
12
12
  Classifier: Operating System :: OS Independent
13
13
  Classifier: Programming Language :: Python
14
14
  Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
15
16
  Classifier: Programming Language :: Python :: 3.10
16
17
  Classifier: Programming Language :: Python :: 3.11
17
18
  Classifier: Programming Language :: Python :: 3.12
18
19
  Classifier: Programming Language :: Python :: 3.13
19
- Classifier: Programming Language :: Python :: 3.9
20
20
  Classifier: Programming Language :: Python :: Implementation :: CPython
21
21
  Classifier: Topic :: Software Development :: Quality Assurance
22
22
  Classifier: Topic :: Software Development :: Testing
@@ -25,7 +25,6 @@ Requires-Dist: Jinja2 (>=2.7.1)
25
25
  Requires-Dist: Pygments (>=2.19.1,<3.0.0)
26
26
  Requires-Dist: chardet (>=3.0.0)
27
27
  Requires-Dist: pluggy (>=0.13.1,<2)
28
- Requires-Dist: setuptools (>=17.0.0) ; python_version < "3.8"
29
28
  Requires-Dist: tomli (>=1.2.1) ; extra == "toml"
30
29
  Project-URL: Homepage, https://github.com/Bachmann1234/diff-cover
31
30
  Project-URL: Repository, https://github.com/Bachmann1234/diff-cover
@@ -134,9 +133,10 @@ You can also generate an HTML, JSON or Markdown version of the report:
134
133
 
135
134
  .. code:: bash
136
135
 
137
- diff-cover coverage.xml --html-report report.html
138
- diff-cover coverage.xml --json-report report.json
139
- diff-cover coverage.xml --markdown-report report.md
136
+ diff-cover coverage.xml --format html:report.html
137
+ diff-cover coverage.xml --format json:report.json
138
+ diff-cover coverage.xml --format markdown:report.md
139
+ diff-cover coverage.xml --format html:report.html,markdown:report.md
140
140
 
141
141
  Multiple XML Coverage Reports
142
142
  -------------------------------
@@ -161,7 +161,7 @@ You can use diff-cover to see quality reports on the diff as well by running
161
161
  diff-quality --violations=<tool>
162
162
 
163
163
  Where ``tool`` is the quality checker to use. Currently ``pycodestyle``, ``pyflakes``,
164
- ``flake8``, ``pylint``, ``checkstyle``, ``checkstylexml`` are supported, but more
164
+ ``flake8``, ``pylint``, ``checkstyle``, ``checkstylexml``, ``ruff check`` are supported, but more
165
165
  checkers can (and should!) be supported. See the section "Adding `diff-quality``
166
166
  Support for a New Quality Checker".
167
167
 
@@ -173,9 +173,10 @@ Like ``diff-cover``, HTML, JSON or Markdown reports can be generated with
173
173
 
174
174
  .. code:: bash
175
175
 
176
- diff-quality --violations=<tool> --html-report report.html
177
- diff-quality --violations=<tool> --json-report report.json
178
- diff-quality --violations=<tool> --markdown-report report.md
176
+ diff-quality --violations=<tool> --format html:report.html
177
+ diff-quality --violations=<tool> --format json:report.json
178
+ diff-quality --violations=<tool> --format markdown:report.md
179
+ diff-quality --violations=<tool> --format html:report.html,markdown:report.md
179
180
 
180
181
  If you have already generated a report using ``pycodestyle``, ``pyflakes``, ``flake8``,
181
182
  ``pylint``, ``checkstyle``, ``checkstylexml``, or ``findbugs`` you can pass the report
@@ -101,9 +101,10 @@ You can also generate an HTML, JSON or Markdown version of the report:
101
101
 
102
102
  .. code:: bash
103
103
 
104
- diff-cover coverage.xml --html-report report.html
105
- diff-cover coverage.xml --json-report report.json
106
- diff-cover coverage.xml --markdown-report report.md
104
+ diff-cover coverage.xml --format html:report.html
105
+ diff-cover coverage.xml --format json:report.json
106
+ diff-cover coverage.xml --format markdown:report.md
107
+ diff-cover coverage.xml --format html:report.html,markdown:report.md
107
108
 
108
109
  Multiple XML Coverage Reports
109
110
  -------------------------------
@@ -128,7 +129,7 @@ You can use diff-cover to see quality reports on the diff as well by running
128
129
  diff-quality --violations=<tool>
129
130
 
130
131
  Where ``tool`` is the quality checker to use. Currently ``pycodestyle``, ``pyflakes``,
131
- ``flake8``, ``pylint``, ``checkstyle``, ``checkstylexml`` are supported, but more
132
+ ``flake8``, ``pylint``, ``checkstyle``, ``checkstylexml``, ``ruff check`` are supported, but more
132
133
  checkers can (and should!) be supported. See the section "Adding `diff-quality``
133
134
  Support for a New Quality Checker".
134
135
 
@@ -140,9 +141,10 @@ Like ``diff-cover``, HTML, JSON or Markdown reports can be generated with
140
141
 
141
142
  .. code:: bash
142
143
 
143
- diff-quality --violations=<tool> --html-report report.html
144
- diff-quality --violations=<tool> --json-report report.json
145
- diff-quality --violations=<tool> --markdown-report report.md
144
+ diff-quality --violations=<tool> --format html:report.html
145
+ diff-quality --violations=<tool> --format json:report.json
146
+ diff-quality --violations=<tool> --format markdown:report.md
147
+ diff-quality --violations=<tool> --format html:report.html,markdown:report.md
146
148
 
147
149
  If you have already generated a report using ``pycodestyle``, ``pyflakes``, ``flake8``,
148
150
  ``pylint``, ``checkstyle``, ``checkstylexml``, or ``findbugs`` you can pass the report
@@ -0,0 +1,5 @@
1
+ from importlib.metadata import version
2
+
3
+ VERSION = version("diff_cover")
4
+ DESCRIPTION = "Automatically find diff lines that need test coverage."
5
+ QUALITY_DESCRIPTION = "Automatically find diff lines with quality violations."
@@ -28,18 +28,7 @@ def execute(command, exit_codes=None):
28
28
  try:
29
29
  stdout, stderr = process.communicate()
30
30
  except OSError:
31
- sys.stderr.write(
32
- " ".join(
33
- [
34
- (
35
- cmd.decode(sys.getfilesystemencoding())
36
- if isinstance(cmd, bytes)
37
- else cmd
38
- )
39
- for cmd in command
40
- ]
41
- )
42
- )
31
+ sys.stderr.write(" ".join(_ensure_unicode(cmd) for cmd in command))
43
32
  raise
44
33
 
45
34
  stderr = _ensure_unicode(stderr)
@@ -3,6 +3,7 @@ import io
3
3
  import logging
4
4
  import os
5
5
  import sys
6
+ import warnings
6
7
  import xml.etree.ElementTree as etree
7
8
 
8
9
  from diff_cover import DESCRIPTION, VERSION
@@ -21,9 +22,10 @@ from diff_cover.violationsreporters.violations_reporter import (
21
22
  XmlCoverageReporter,
22
23
  )
23
24
 
24
- HTML_REPORT_HELP = "Diff coverage HTML output"
25
- JSON_REPORT_HELP = "Diff coverage JSON output"
26
- MARKDOWN_REPORT_HELP = "Diff coverage Markdown output"
25
+ FORMAT_HELP = "Format to use"
26
+ HTML_REPORT_DEFAULT_PATH = "diff-cover.html"
27
+ JSON_REPORT_DEFAULT_PATH = "diff-cover.json"
28
+ MARKDOWN_REPORT_DEFAULT_PATH = "diff-cover.md"
27
29
  COMPARE_BRANCH_HELP = "Branch to compare"
28
30
  CSS_FILE_HELP = "Write CSS into an external file"
29
31
  FAIL_UNDER_HELP = (
@@ -51,6 +53,19 @@ DIFF_FILE_HELP = "The diff file to use"
51
53
  LOGGER = logging.getLogger(__name__)
52
54
 
53
55
 
56
+ def format_type(value):
57
+ """
58
+ Accepts:
59
+ --format html
60
+ --format json
61
+ --format json,html
62
+ --format html,json:path/to/file.json
63
+
64
+ return: dict of strings to paths
65
+ """
66
+ return dict((item.split(":") for item in value.split(",")) if value else {})
67
+
68
+
54
69
  def parse_coverage_args(argv):
55
70
  """
56
71
  Parse command line arguments, returning a dict of
@@ -72,24 +87,10 @@ def parse_coverage_args(argv):
72
87
  parser.add_argument("coverage_file", type=str, help=COVERAGE_FILE_HELP, nargs="+")
73
88
 
74
89
  parser.add_argument(
75
- "--html-report",
76
- metavar="FILENAME",
77
- type=str,
78
- help=HTML_REPORT_HELP,
79
- )
80
-
81
- parser.add_argument(
82
- "--json-report",
83
- metavar="FILENAME",
84
- type=str,
85
- help=JSON_REPORT_HELP,
86
- )
87
-
88
- parser.add_argument(
89
- "--markdown-report",
90
- metavar="FILENAME",
91
- type=str,
92
- help=MARKDOWN_REPORT_HELP,
90
+ "--format",
91
+ type=format_type,
92
+ default="",
93
+ help=FORMAT_HELP,
93
94
  )
94
95
 
95
96
  parser.add_argument(
@@ -203,10 +204,8 @@ def generate_coverage_report(
203
204
  coverage_files,
204
205
  compare_branch,
205
206
  diff_tool,
206
- html_report=None,
207
+ report_formats=None,
207
208
  css_file=None,
208
- json_report=None,
209
- markdown_report=None,
210
209
  ignore_staged=False,
211
210
  ignore_unstaged=False,
212
211
  include_untracked=False,
@@ -248,7 +247,8 @@ def generate_coverage_report(
248
247
  coverage = LcovCoverageReporter(lcov_roots, src_roots)
249
248
 
250
249
  # Build a report generator
251
- if html_report is not None:
250
+ if "html" in report_formats:
251
+ html_report = report_formats["html"] or HTML_REPORT_DEFAULT_PATH
252
252
  css_url = css_file
253
253
  if css_url is not None:
254
254
  css_url = os.path.relpath(css_file, os.path.dirname(html_report))
@@ -259,12 +259,14 @@ def generate_coverage_report(
259
259
  with open(css_file, "wb") as output_file:
260
260
  reporter.generate_css(output_file)
261
261
 
262
- if json_report is not None:
262
+ if "json" in report_formats:
263
+ json_report = report_formats["json"] or JSON_REPORT_DEFAULT_PATH
263
264
  reporter = JsonReportGenerator(coverage, diff)
264
265
  with open(json_report, "wb") as output_file:
265
266
  reporter.generate_report(output_file)
266
267
 
267
- if markdown_report is not None:
268
+ if "markdown" in report_formats:
269
+ markdown_report = report_formats["markdown"] or MARKDOWN_REPORT_DEFAULT_PATH
268
270
  reporter = MarkdownReportGenerator(coverage, diff)
269
271
  with open(markdown_report, "wb") as output_file:
270
272
  reporter.generate_report(output_file)
@@ -278,6 +280,53 @@ def generate_coverage_report(
278
280
  return reporter.total_percent_covered()
279
281
 
280
282
 
283
+ def handle_old_format(description, argv):
284
+ parser = argparse.ArgumentParser(description=description)
285
+ arg_html = parser.add_argument("--html-report", type=str)
286
+ arg_json = parser.add_argument("--json-report", type=str)
287
+ arg_markdown = parser.add_argument("--markdown-report", type=str)
288
+ parser.add_argument("--format", type=str)
289
+
290
+ known_args, unknown_args = parser.parse_known_args(argv)
291
+ format_ = format_type(known_args.format)
292
+ if known_args.html_report:
293
+ if "html" in format_:
294
+ raise argparse.ArgumentError(
295
+ arg_html, "Cannot use along with --format html."
296
+ )
297
+ warnings.warn(
298
+ "The --html-report option is deprecated. "
299
+ f"Use --format html:{known_args.html_report} instead."
300
+ )
301
+ format_["html"] = known_args.html_report
302
+ if known_args.json_report:
303
+ if "json" in format_:
304
+ raise argparse.ArgumentError(
305
+ arg_json, "Cannot use along with --format json."
306
+ )
307
+ warnings.warn(
308
+ "The --json-report option is deprecated. "
309
+ f"Use --format json:{known_args.json_report} instead."
310
+ )
311
+ format_["json"] = known_args.json_report
312
+ if known_args.markdown_report:
313
+ if "markdown" in format_:
314
+ raise argparse.ArgumentError(
315
+ arg_markdown, "Cannot use along with --format markdown."
316
+ )
317
+ warnings.warn(
318
+ "The --markdown-report option is deprecated. "
319
+ f"Use --format markdown:{known_args.markdown_report} instead."
320
+ )
321
+ format_["markdown"] = known_args.markdown_report
322
+ if format_:
323
+ unknown_args += [
324
+ "--format",
325
+ ",".join(f"{k}:{v}" for k, v in format_.items()), # noqa: E231
326
+ ]
327
+ return unknown_args
328
+
329
+
281
330
  def main(argv=None, directory=None):
282
331
  """
283
332
  Main entry point for the tool, script installed via pyproject.toml
@@ -287,7 +336,7 @@ def main(argv=None, directory=None):
287
336
  0 is successful run
288
337
  """
289
338
  argv = argv or sys.argv
290
- arg_dict = parse_coverage_args(argv[1:])
339
+ arg_dict = parse_coverage_args(handle_old_format(DESCRIPTION, argv[1:]))
291
340
 
292
341
  quiet = arg_dict["quiet"]
293
342
  level = logging.ERROR if quiet else logging.WARNING
@@ -308,9 +357,7 @@ def main(argv=None, directory=None):
308
357
  arg_dict["coverage_file"],
309
358
  arg_dict["compare_branch"],
310
359
  diff_tool,
311
- html_report=arg_dict["html_report"],
312
- json_report=arg_dict["json_report"],
313
- markdown_report=arg_dict["markdown_report"],
360
+ report_formats=arg_dict["format"],
314
361
  css_file=arg_dict["external_css_file"],
315
362
  ignore_staged=arg_dict["ignore_staged"],
316
363
  ignore_unstaged=arg_dict["ignore_unstaged"],
@@ -20,14 +20,17 @@ from diff_cover.diff_cover_tool import (
20
20
  DIFF_RANGE_NOTATION_HELP,
21
21
  EXCLUDE_HELP,
22
22
  FAIL_UNDER_HELP,
23
- HTML_REPORT_HELP,
23
+ FORMAT_HELP,
24
+ HTML_REPORT_DEFAULT_PATH,
24
25
  IGNORE_STAGED_HELP,
25
26
  IGNORE_UNSTAGED_HELP,
26
27
  IGNORE_WHITESPACE,
27
28
  INCLUDE_UNTRACKED_HELP,
28
- JSON_REPORT_HELP,
29
- MARKDOWN_REPORT_HELP,
29
+ JSON_REPORT_DEFAULT_PATH,
30
+ MARKDOWN_REPORT_DEFAULT_PATH,
30
31
  QUIET_HELP,
32
+ format_type,
33
+ handle_old_format,
31
34
  )
32
35
  from diff_cover.diff_reporter import GitDiffReporter
33
36
  from diff_cover.git_diff import GitDiffTool
@@ -54,6 +57,7 @@ from diff_cover.violationsreporters.violations_reporter import (
54
57
  pycodestyle_driver,
55
58
  pydocstyle_driver,
56
59
  pyflakes_driver,
60
+ ruff_check_driver,
57
61
  shellcheck_driver,
58
62
  )
59
63
 
@@ -62,6 +66,7 @@ QUALITY_DRIVERS = {
62
66
  "pycodestyle": pycodestyle_driver,
63
67
  "pyflakes": pyflakes_driver,
64
68
  "pylint": PylintDriver(),
69
+ "ruff.check": ruff_check_driver,
65
70
  "flake8": flake8_driver,
66
71
  "jshint": jshint_driver,
67
72
  "eslint": EslintDriver(),
@@ -105,24 +110,10 @@ def parse_quality_args(argv):
105
110
  )
106
111
 
107
112
  parser.add_argument(
108
- "--html-report",
109
- metavar="FILENAME",
110
- type=str,
111
- help=HTML_REPORT_HELP,
112
- )
113
-
114
- parser.add_argument(
115
- "--json-report",
116
- metavar="FILENAME",
117
- type=str,
118
- help=JSON_REPORT_HELP,
119
- )
120
-
121
- parser.add_argument(
122
- "--markdown-report",
123
- metavar="FILENAME",
124
- type=str,
125
- help=MARKDOWN_REPORT_HELP,
113
+ "--format",
114
+ type=format_type,
115
+ default="",
116
+ help=FORMAT_HELP,
126
117
  )
127
118
 
128
119
  parser.add_argument(
@@ -224,17 +215,14 @@ def parse_quality_args(argv):
224
215
  def generate_quality_report(
225
216
  tool,
226
217
  compare_branch,
227
- html_report=None,
228
- json_report=None,
229
- markdown_report=None,
218
+ diff_tool,
219
+ report_formats=None,
230
220
  css_file=None,
231
221
  ignore_staged=False,
232
222
  ignore_unstaged=False,
233
223
  include_untracked=False,
234
224
  exclude=None,
235
225
  include=None,
236
- diff_range_notation=None,
237
- ignore_whitespace=False,
238
226
  quiet=False,
239
227
  ):
240
228
  """
@@ -245,7 +233,7 @@ def generate_quality_report(
245
233
  )
246
234
  diff = GitDiffReporter(
247
235
  compare_branch,
248
- git_diff=GitDiffTool(diff_range_notation, ignore_whitespace),
236
+ git_diff=diff_tool,
249
237
  ignore_staged=ignore_staged,
250
238
  ignore_unstaged=ignore_unstaged,
251
239
  include_untracked=include_untracked,
@@ -254,7 +242,8 @@ def generate_quality_report(
254
242
  include=include,
255
243
  )
256
244
 
257
- if html_report is not None:
245
+ if "html" in report_formats:
246
+ html_report = report_formats["html"] or HTML_REPORT_DEFAULT_PATH
258
247
  css_url = css_file
259
248
  if css_url is not None:
260
249
  css_url = os.path.relpath(css_file, os.path.dirname(html_report))
@@ -265,12 +254,14 @@ def generate_quality_report(
265
254
  with open(css_file, "wb") as output_file:
266
255
  reporter.generate_css(output_file)
267
256
 
268
- if json_report is not None:
257
+ if "json" in report_formats:
258
+ json_report = report_formats["json"] or JSON_REPORT_DEFAULT_PATH
269
259
  reporter = JsonReportGenerator(tool, diff)
270
260
  with open(json_report, "wb") as output_file:
271
261
  reporter.generate_report(output_file)
272
262
 
273
- if markdown_report is not None:
263
+ if "markdown" in report_formats:
264
+ markdown_report = report_formats["markdown"] or MARKDOWN_REPORT_DEFAULT_PATH
274
265
  reporter = MarkdownQualityReportGenerator(tool, diff)
275
266
  with open(markdown_report, "wb") as output_file:
276
267
  reporter.generate_report(output_file)
@@ -293,7 +284,9 @@ def main(argv=None, directory=None):
293
284
  """
294
285
 
295
286
  argv = argv or sys.argv
296
- arg_dict = parse_quality_args(argv[1:])
287
+ arg_dict = parse_quality_args(
288
+ handle_old_format(diff_cover.QUALITY_DESCRIPTION, argv[1:])
289
+ )
297
290
 
298
291
  quiet = arg_dict["quiet"]
299
292
  level = logging.ERROR if quiet else logging.WARNING
@@ -353,17 +346,16 @@ def main(argv=None, directory=None):
353
346
  percent_passing = generate_quality_report(
354
347
  reporter,
355
348
  arg_dict["compare_branch"],
356
- html_report=arg_dict["html_report"],
357
- json_report=arg_dict["json_report"],
358
- markdown_report=arg_dict["markdown_report"],
349
+ GitDiffTool(
350
+ arg_dict["diff_range_notation"], arg_dict["ignore_whitespace"]
351
+ ),
352
+ report_formats=arg_dict["format"],
359
353
  css_file=arg_dict["external_css_file"],
360
354
  ignore_staged=arg_dict["ignore_staged"],
361
355
  ignore_unstaged=arg_dict["ignore_unstaged"],
362
356
  include_untracked=arg_dict["include_untracked"],
363
357
  exclude=arg_dict["exclude"],
364
358
  include=arg_dict["include"],
365
- diff_range_notation=arg_dict["diff_range_notation"],
366
- ignore_whitespace=arg_dict["ignore_whitespace"],
367
359
  quiet=quiet,
368
360
  )
369
361
  if percent_passing >= fail_under:
@@ -484,9 +484,9 @@ class GitDiffReporter(BaseDiffReporter):
484
484
  try:
485
485
  return int(groups[0])
486
486
 
487
- except ValueError:
487
+ except ValueError as e:
488
488
  msg = f"Could not parse '{groups[0]}' as a line number"
489
- raise GitDiffError(msg)
489
+ raise GitDiffError(msg) from e
490
490
 
491
491
  else:
492
492
  msg = f"Could not find start of hunk in line '{line}'"
@@ -41,6 +41,7 @@ class GitDiffTool:
41
41
  :param bool ignore_whitespace:
42
42
  Perform a diff but ignore any and all whitespace.
43
43
  """
44
+ self._untracked_cache = None
44
45
  self.range_notation = range_notation
45
46
  self._default_git_args = [
46
47
  "git",
@@ -80,7 +81,7 @@ class GitDiffTool:
80
81
  the `--compare-branch` argument allows you to set a different branch.
81
82
  """
82
83
  )
83
- )
84
+ ) from e
84
85
  raise
85
86
 
86
87
  def diff_unstaged(self):
@@ -107,10 +108,16 @@ class GitDiffTool:
107
108
 
108
109
  def untracked(self):
109
110
  """Return the untracked files."""
111
+ if self._untracked_cache is not None:
112
+ return self._untracked_cache
113
+
110
114
  output = execute(["git", "ls-files", "--exclude-standard", "--others"])[0]
111
- if not output:
112
- return []
113
- return [to_unescaped_filename(line) for line in output.splitlines() if line]
115
+ self._untracked_cache = []
116
+ if output:
117
+ self._untracked_cache = [
118
+ to_unescaped_filename(line) for line in output.splitlines() if line
119
+ ]
120
+ return self._untracked_cache
114
121
 
115
122
 
116
123
  class GitDiffFileTool(GitDiffTool):
@@ -129,14 +136,12 @@ class GitDiffFileTool(GitDiffTool):
129
136
  try:
130
137
  with open(self.diff_file_path, "r") as file:
131
138
  return file.read()
132
- except IOError as e:
133
- raise ValueError(
134
- dedent(
135
- f"""
136
- Could not read the diff file. Make sure '{self.diff_file_path}' exists?
137
- """
138
- )
139
+ except OSError as e:
140
+ error_message = (
141
+ "Could not read the diff file. "
142
+ f"Make sure '{self.diff_file_path}' exists?"
139
143
  )
144
+ raise ValueError(error_message) from e
140
145
 
141
146
  def diff_unstaged(self):
142
147
  return ""
@@ -24,10 +24,7 @@ class GitPathTool:
24
24
  Set the cwd that is used to manipulate paths.
25
25
  """
26
26
  if not cwd:
27
- try:
28
- cwd = os.getcwdu()
29
- except AttributeError:
30
- cwd = os.getcwd()
27
+ cwd = os.getcwd()
31
28
  if isinstance(cwd, bytes):
32
29
  cwd = cwd.decode(sys.getdefaultencoding())
33
30
  cls._cwd = cwd
@@ -3,15 +3,9 @@ Classes for querying the information in a test coverage report.
3
3
  """
4
4
 
5
5
  import os
6
+ import xml.etree.ElementTree as etree
6
7
  from collections import defaultdict
7
8
 
8
- try:
9
- # Needed for Python < 3.3, works up to 3.8
10
- import xml.etree.ElementTree as etree
11
- except ImportError:
12
- # Python 3.9 onwards
13
- import xml.etree.ElementTree as etree
14
-
15
9
  from diff_cover.command_runner import run_command_for_code
16
10
  from diff_cover.git_path import GitPathTool
17
11
  from diff_cover.violationsreporters.base import (
@@ -476,6 +476,20 @@ pyflakes_driver = RegexBasedDriver(
476
476
  exit_codes=[0, 1],
477
477
  )
478
478
 
479
+ ruff_check_driver = RegexBasedDriver(
480
+ name="ruff.check",
481
+ supported_extensions=["py"],
482
+ command=["ruff", "check"],
483
+ # Match lines of the form:
484
+ # path/to/file.py:328:27 F541 [*] f-string without any placeholders
485
+ # path/to/file.py:418:26 F841 [*] Local variable `e` is assigned to but never used
486
+ expression=r"^([^:]+):(\d+):\d*:? (.*)$",
487
+ command_to_check_install=["ruff", "--version"],
488
+ # ruff exit code is 1 if there are violations
489
+ # https://docs.astral.sh/ruff/linter/#exit-codes
490
+ exit_codes=[0, 1],
491
+ )
492
+
479
493
  """
480
494
  Report Flake8 violations.
481
495
  """
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "diff_cover"
3
- version = "9.2.4"
3
+ version = "9.3.1"
4
4
  description = "Run coverage and linting reports on diffs"
5
5
  authors = ["See Contributors"]
6
6
  homepage = "https://github.com/Bachmann1234/diff-cover"
@@ -40,18 +40,18 @@ diff-cover = 'diff_cover.diff_cover_tool:main'
40
40
  diff-quality = 'diff_cover.diff_quality_tool:main'
41
41
 
42
42
  [tool.poetry.dependencies]
43
- python = "^3.9.17"
43
+ python = ">=3.9"
44
44
  Pygments = "^2.19.1"
45
45
  Jinja2 = ">=2.7.1"
46
46
  pluggy = ">=0.13.1,<2"
47
47
  chardet = ">=3.0.0"
48
48
  tomli = {version = ">=1.2.1", optional = true}
49
- setuptools = { version = ">=17.0.0", python = "<3.8" }
50
49
 
51
- [tool.poetry.dev-dependencies]
52
- pytest-cov = "^6.0.0"
50
+ [tool.poetry.group.dev.dependencies]
51
+ pytest-cov = "^6.1.1"
53
52
  pytest-datadir = "^1.5.0"
54
53
  pytest-mock = "^3.14.0"
54
+ pytest-xdist = "^3.6.1"
55
55
  pycodestyle = ">=2.9.1"
56
56
  flake8 = "^5.0.4"
57
57
  pyflakes = "^2.5.0"
@@ -61,6 +61,7 @@ pydocstyle = "^6.1.1"
61
61
  black = "^25.1.0"
62
62
  isort = "^6.0.1"
63
63
  doc8 = "1.1.2"
64
+ ruff = "^0.11.10"
64
65
 
65
66
  [tool.poetry.extras]
66
67
  toml = ["tomli"]
@@ -85,6 +86,28 @@ load-plugins = [
85
86
  "pylint_pytest",
86
87
  ]
87
88
 
89
+ [tool.coverage.run]
90
+ branch = true
91
+ relative_files = true
92
+ parallel = true
93
+ concurrency = ["multiprocessing"]
94
+ source = ["diff_cover"]
95
+ omit = ["./tests/*"]
96
+
97
+ [tool.coverage.report]
98
+ show_missing = true
99
+ exclude_also = [
100
+ "if typing.TYPE_CHECKING:",
101
+ "if TYPE_CHECKING:",
102
+ "raise NotImplementedError",
103
+ "raise AssertionError",
104
+ "pass",
105
+ ]
106
+
107
+ [tool.coverage.html]
108
+ show_contexts = true
109
+ skip_covered = false
110
+
88
111
  [tool.pylint."messages control"]
89
112
  enable = ["all"]
90
113
  disable = [