diff-cover 9.6.0__tar.gz → 9.7.0__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 (157) hide show
  1. {diff_cover-9.6.0 → diff_cover-9.7.0}/PKG-INFO +1 -1
  2. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/command_runner.py +9 -9
  3. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/config_parser.py +2 -5
  4. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/diff_quality_tool.py +15 -16
  5. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/diff_reporter.py +7 -4
  6. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/git_diff.py +1 -1
  7. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/git_path.py +2 -1
  8. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/report_generator.py +5 -7
  9. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/snippets.py +6 -11
  10. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/util.py +4 -0
  11. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/violationsreporters/base.py +12 -6
  12. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/violationsreporters/java_violations_reporter.py +10 -11
  13. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/violationsreporters/violations_reporter.py +149 -134
  14. {diff_cover-9.6.0 → diff_cover-9.7.0}/pyproject.toml +56 -31
  15. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_diff_reporter.py +59 -68
  16. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_git_diff.py +4 -1
  17. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_git_path.py +4 -1
  18. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_integration.py +20 -1
  19. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_java_violations_reporter.py +4 -1
  20. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_report_generator.py +1 -2
  21. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_util.py +4 -0
  22. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_violations_reporter.py +69 -16
  23. diff_cover-9.6.0/tests/fixtures/dotnet_coverage_console_report.txt +0 -10
  24. {diff_cover-9.6.0 → diff_cover-9.7.0}/LICENSE +0 -0
  25. {diff_cover-9.6.0 → diff_cover-9.7.0}/NOTICE +0 -0
  26. {diff_cover-9.6.0 → diff_cover-9.7.0}/README.rst +0 -0
  27. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/__init__.py +0 -0
  28. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/diff_cover_tool.py +0 -0
  29. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/hook.py +0 -0
  30. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/hookspecs.py +0 -0
  31. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/console_coverage_report.txt +0 -0
  32. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/console_quality_report.txt +0 -0
  33. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/external_style.css +0 -0
  34. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/github_coverage_annotations.txt +0 -0
  35. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/html_coverage_report.html +0 -0
  36. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/html_quality_report.html +0 -0
  37. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/markdown_coverage_report.md +0 -0
  38. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/markdown_quality_report.md +0 -0
  39. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/snippet_content.html +0 -0
  40. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/snippet_content.md +0 -0
  41. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/snippet_content.txt +0 -0
  42. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/templates/snippet_style.html +0 -0
  43. {diff_cover-9.6.0 → diff_cover-9.7.0}/diff_cover/violationsreporters/__init__.py +0 -0
  44. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/__init__.py +0 -0
  45. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/add_console_report.txt +0 -0
  46. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/add_html_report.html +0 -0
  47. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/add_json_report.json +0 -0
  48. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/add_markdown_report.md +0 -0
  49. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/changed_console_report.txt +0 -0
  50. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/changed_html_report.html +0 -0
  51. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/coverage.xml +0 -0
  52. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/coverage1.xml +0 -0
  53. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/coverage2.xml +0 -0
  54. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/coverage_missing_lines.xml +0 -0
  55. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/cpp_functions_console_report.txt +0 -0
  56. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/cpp_functions_coverage.lcov +0 -0
  57. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/delete_console_report.txt +0 -0
  58. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/delete_html_report.html +0 -0
  59. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/dotnet_coverage.xml +0 -0
  60. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/empty.txt +0 -0
  61. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/empty_pycodestyle_violations.txt +0 -0
  62. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/expand_console_report.txt +0 -0
  63. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/external_css_html_report.html +0 -0
  64. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/external_style.css +0 -0
  65. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_add.txt +0 -0
  66. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_changed.txt +0 -0
  67. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_code_dupe.txt +0 -0
  68. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_cpp.txt +0 -0
  69. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_cpp_functions.txt +0 -0
  70. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_delete.txt +0 -0
  71. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_dotnet.txt +0 -0
  72. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_external_css.txt +0 -0
  73. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_lua.txt +0 -0
  74. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_moved.txt +0 -0
  75. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_mult.txt +0 -0
  76. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_python.txt +0 -0
  77. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_subdir.txt +0 -0
  78. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_typescript.txt +0 -0
  79. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_typescript_branches.txt +0 -0
  80. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_unicode.txt +0 -0
  81. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_violations.txt +0 -0
  82. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/git_diff_violations_two_files.txt +0 -0
  83. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/hello.py +0 -0
  84. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/hi.py +0 -0
  85. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/html_report.html +0 -0
  86. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/html_report_empty.html +0 -0
  87. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/html_report_one_snippet.html +0 -0
  88. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/html_report_two_snippets.html +0 -0
  89. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/lcov.info +0 -0
  90. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/lua_console_report.txt +0 -0
  91. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/luacoverage.xml +0 -0
  92. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/markdown_report_one_snippet.md +0 -0
  93. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/markdown_report_two_snippets.md +0 -0
  94. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/moved_console_report.txt +0 -0
  95. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/moved_coverage.xml +0 -0
  96. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/moved_html_report.html +0 -0
  97. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/mult_inputs_console_report.txt +0 -0
  98. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/mult_inputs_html_report.html +0 -0
  99. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pycodestyle_report.txt +0 -0
  100. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pycodestyle_violations_report.html +0 -0
  101. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pycodestyle_violations_report.json +0 -0
  102. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pycodestyle_violations_report.md +0 -0
  103. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pycodestyle_violations_report.txt +0 -0
  104. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pycodestyle_violations_report_external_css.html +0 -0
  105. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pyflakes_two_files.txt +0 -0
  106. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pyflakes_violations_report.html +0 -0
  107. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pyflakes_violations_report.txt +0 -0
  108. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pylint_dupe.txt +0 -0
  109. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pylint_dupe_violations_report.txt +0 -0
  110. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pylint_report.txt +0 -0
  111. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pylint_violations_console_report.txt +0 -0
  112. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pylint_violations_report.html +0 -0
  113. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pylint_violations_report.txt +0 -0
  114. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/pylintrc +0 -0
  115. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/real_cpp_console_report.txt +0 -0
  116. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/real_cpp_coverage.lcov +0 -0
  117. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/real_python_console_report.txt +0 -0
  118. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/real_python_coverage.lcov +0 -0
  119. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/real_typescript_console_report.txt +0 -0
  120. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/real_typescript_coverage.lcov +0 -0
  121. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/show_uncovered_lines_console.txt +0 -0
  122. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet.css +0 -0
  123. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_8859.py +0 -0
  124. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_arabic_output.html +0 -0
  125. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_default.html +0 -0
  126. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_invalid_violations.html +0 -0
  127. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_list.html +0 -0
  128. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_list.md +0 -0
  129. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_list2.md +0 -0
  130. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_list3.md +0 -0
  131. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_no_filename_ext.html +0 -0
  132. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_src.py +0 -0
  133. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_src2.cpp +0 -0
  134. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_src3.cpp +0 -0
  135. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_unicode.html +0 -0
  136. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_unicode.py +0 -0
  137. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/snippet_unicode_html_output.html +0 -0
  138. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/subdir_coverage_console_report.txt +0 -0
  139. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/subdir_coverage_html_report.html +0 -0
  140. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/test_src.txt +0 -0
  141. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/typescript_branches_console_report.txt +0 -0
  142. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/typescript_branches_coverage.lcov +0 -0
  143. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/unicode_console_report.txt +0 -0
  144. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/unicode_coverage.xml +0 -0
  145. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/unicode_html_report.html +0 -0
  146. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/unicode_test_src.txt +0 -0
  147. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/fixtures/violations_test_file.py +0 -0
  148. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/helpers.py +0 -0
  149. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/snippet_list_unicode.html +0 -0
  150. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_clover_violations_reporter/test.xml +0 -0
  151. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_clover_violations_reporter.py +0 -0
  152. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_config_parser.py +0 -0
  153. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_diff_cover_main.py +0 -0
  154. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_diff_cover_tool.py +0 -0
  155. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_diff_quality_main.py +0 -0
  156. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_git_diff_file.py +0 -0
  157. {diff_cover-9.6.0 → diff_cover-9.7.0}/tests/test_snippets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: diff_cover
3
- Version: 9.6.0
3
+ Version: 9.7.0
4
4
  Summary: Run coverage and linting reports on diffs
5
5
  License: Apache-2.0
6
6
  Author: See Contributors
@@ -24,12 +24,12 @@ def execute(command, exit_codes=None):
24
24
  exit_codes = [0]
25
25
 
26
26
  stdout_pipe = subprocess.PIPE
27
- process = subprocess.Popen(command, stdout=stdout_pipe, stderr=stdout_pipe)
28
- try:
29
- stdout, stderr = process.communicate()
30
- except OSError:
31
- sys.stderr.write(" ".join(_ensure_unicode(cmd) for cmd in command))
32
- raise
27
+ with subprocess.Popen(command, stdout=stdout_pipe, stderr=stdout_pipe) as process:
28
+ try:
29
+ stdout, stderr = process.communicate()
30
+ except OSError:
31
+ sys.stderr.write(" ".join(_ensure_unicode(cmd) for cmd in command))
32
+ raise
33
33
 
34
34
  stderr = _ensure_unicode(stderr)
35
35
  if process.returncode not in exit_codes:
@@ -43,10 +43,10 @@ def run_command_for_code(command):
43
43
  Returns command's exit code.
44
44
  """
45
45
  try:
46
- process = subprocess.Popen(
46
+ with subprocess.Popen(
47
47
  command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
48
- )
49
- process.communicate()
48
+ ) as process:
49
+ process.communicate()
50
50
  except FileNotFoundError:
51
51
  return 1
52
52
  return process.returncode
@@ -6,15 +6,12 @@ try:
6
6
 
7
7
  _HAS_TOML = True
8
8
  except ImportError: # pragma: no cover
9
- _HAS_TOML = False
10
-
11
- if not _HAS_TOML:
12
9
  try:
13
10
  import tomllib as toml
14
11
 
15
12
  _HAS_TOML = True
16
- except ImportError: # pragma: no cover
17
- pass
13
+ except ImportError:
14
+ _HAS_TOML = False
18
15
 
19
16
 
20
17
  class Tool(enum.Enum):
@@ -3,6 +3,7 @@ Implement the command-line tool interface for diff_quality.
3
3
  """
4
4
 
5
5
  import argparse
6
+ import contextlib
6
7
  import io
7
8
  import logging
8
9
  import os
@@ -56,6 +57,7 @@ from diff_cover.violationsreporters.violations_reporter import (
56
57
  PylintDriver,
57
58
  flake8_driver,
58
59
  jshint_driver,
60
+ mypy_driver,
59
61
  pycodestyle_driver,
60
62
  pydocstyle_driver,
61
63
  pyflakes_driver,
@@ -66,6 +68,7 @@ from diff_cover.violationsreporters.violations_reporter import (
66
68
  QUALITY_DRIVERS = {
67
69
  "clang": ClangFormatDriver(),
68
70
  "cppcheck": CppcheckDriver(),
71
+ "mypy": mypy_driver,
69
72
  "pycodestyle": pycodestyle_driver,
70
73
  "pyflakes": pyflakes_driver,
71
74
  "pylint": PylintDriver(),
@@ -81,8 +84,8 @@ QUALITY_DRIVERS = {
81
84
  "shellcheck": shellcheck_driver,
82
85
  }
83
86
 
84
- VIOLATION_CMD_HELP = "Which code quality tool to use (%s)" % "/".join(
85
- sorted(QUALITY_DRIVERS)
87
+ VIOLATION_CMD_HELP = (
88
+ f"Which code quality tool to use ({'/'.join(sorted(QUALITY_DRIVERS))})"
86
89
  )
87
90
  INPUT_REPORTS_HELP = "Which violations reports to use"
88
91
  OPTIONS_HELP = "Options to be passed to the violations tool"
@@ -315,20 +318,24 @@ def main(argv=None, directory=None):
315
318
  plugin_manager.add_hookspecs(hookspecs)
316
319
  plugin_manager.load_setuptools_entrypoints("diff_cover")
317
320
 
318
- hooks = (
319
- plugin_manager.hook.diff_cover_report_quality # pylint: disable=no-member
320
- )
321
+ hooks = plugin_manager.hook.diff_cover_report_quality
321
322
  for hookimpl in hooks.get_hookimpls():
322
323
  if hookimpl.plugin_name == tool:
323
324
  reporter_factory_fn = hookimpl.function
324
325
  break
325
326
 
326
- if reporter or driver or reporter_factory_fn:
327
- input_reports = []
327
+ # If none of the reporter, driver, or reporter_factory_fn are set
328
+ if not any((reporter, driver, reporter_factory_fn)):
329
+ LOGGER.error("Quality tool not recognized: '%s'", tool)
330
+ return 1
331
+
332
+ with contextlib.ExitStack() as stack:
328
333
  try:
334
+ input_reports = []
329
335
  for path in arg_dict["input_reports"]:
330
336
  try:
331
- input_reports.append(open(path, "rb"))
337
+ file_handle = stack.enter_context(open(path, "rb"))
338
+ input_reports.append(file_handle)
332
339
  except OSError:
333
340
  LOGGER.error("Could not load report '%s'", path)
334
341
  return 1
@@ -373,14 +380,6 @@ def main(argv=None, directory=None):
373
380
  except OSError as exc:
374
381
  LOGGER.error("Failure: '%s'", str(exc))
375
382
  return 1
376
- # Close any reports we opened
377
- finally:
378
- for file_handle in input_reports:
379
- file_handle.close()
380
-
381
- else:
382
- LOGGER.error("Quality tool not recognized: '%s'", tool)
383
- return 1
384
383
 
385
384
 
386
385
  if __name__ == "__main__":
@@ -9,6 +9,7 @@ import re
9
9
  from abc import ABC, abstractmethod
10
10
 
11
11
  from diff_cover.git_diff import GitDiffError
12
+ from diff_cover.util import to_unix_path, to_unix_paths
12
13
 
13
14
 
14
15
  class BaseDiffReporter(ABC):
@@ -85,7 +86,7 @@ class BaseDiffReporter(ABC):
85
86
  include = self._include
86
87
  if include:
87
88
  for pattern in include:
88
- if path in glob.glob(pattern, recursive=True):
89
+ if path in to_unix_paths(glob.glob(pattern, recursive=True)):
89
90
  break # file is included
90
91
  else:
91
92
  return True
@@ -200,7 +201,7 @@ class GitDiffReporter(BaseDiffReporter):
200
201
 
201
202
  # Look up the modified lines for the source file
202
203
  # If no lines modified, return an empty list
203
- return diff_dict.get(src_path, [])
204
+ return diff_dict.get(to_unix_path(src_path), [])
204
205
 
205
206
  def _get_included_diff_results(self):
206
207
  """
@@ -237,12 +238,13 @@ class GitDiffReporter(BaseDiffReporter):
237
238
  diff_dict = self._parse_diff_str(diff_str)
238
239
 
239
240
  for src_path, (added_lines, deleted_lines) in diff_dict.items():
241
+ src_path = to_unix_path(src_path)
240
242
  if not self._validate_path_to_diff(src_path):
241
243
  continue
242
244
 
243
245
  # Remove any lines from the dict that have been deleted
244
246
  # Include any lines that have been added
245
- result_dict[src_path] = [
247
+ result_dict[to_unix_path(src_path)] = [
246
248
  line
247
249
  for line in result_dict.get(src_path, [])
248
250
  if line not in deleted_lines
@@ -332,6 +334,7 @@ class GitDiffReporter(BaseDiffReporter):
332
334
 
333
335
  # Parse the diff string into sections by source file
334
336
  for line in diff_str.split("\n"):
337
+ line = line.rstrip()
335
338
  # If the line starts with "diff --git"
336
339
  # or "diff --cc" (in the case of a merge conflict)
337
340
  # then it is the start of a new source file
@@ -501,7 +504,7 @@ class GitDiffReporter(BaseDiffReporter):
501
504
  number is included once and the lines are ordered sequentially.
502
505
  """
503
506
 
504
- if len(line_numbers) == 0:
507
+ if not line_numbers:
505
508
  return []
506
509
 
507
510
  # Ensure lines are unique by putting them in a set
@@ -132,7 +132,7 @@ class GitDiffFileTool(GitDiffTool):
132
132
  Raises a `GitDiffError` if the file cannot be read.
133
133
  """
134
134
  try:
135
- with open(self.diff_file_path, "r") as file:
135
+ with open(self.diff_file_path, "r", encoding="utf-8") as file:
136
136
  return file.read()
137
137
  except OSError as e:
138
138
  error_message = (
@@ -6,6 +6,7 @@ import os
6
6
  import sys
7
7
 
8
8
  from diff_cover.command_runner import execute
9
+ from diff_cover.util import to_unix_path
9
10
 
10
11
 
11
12
  class GitPathTool:
@@ -51,7 +52,7 @@ class GitPathTool:
51
52
  # and src_path is `other_package/some_file.py`
52
53
  # search for `/home/user/work/diff-cover/other_package/some_file.py`
53
54
 
54
- return os.path.join(cls._root, src_path)
55
+ return to_unix_path(os.path.join(cls._root, src_path))
55
56
 
56
57
  @classmethod
57
58
  def _git_root(cls):
@@ -4,13 +4,13 @@ Classes for generating diff coverage reports.
4
4
 
5
5
  import contextlib
6
6
  import json
7
- import os
8
7
  from abc import ABC, abstractmethod
9
8
  from gettext import gettext, ngettext
10
9
 
11
10
  from jinja2 import Environment, PackageLoader, select_autoescape
12
11
 
13
12
  from diff_cover.snippets import Snippet
13
+ from diff_cover.util import to_unix_path
14
14
 
15
15
 
16
16
  class DiffViolations:
@@ -149,10 +149,7 @@ class BaseReportGenerator(ABC):
149
149
  """
150
150
 
151
151
  return sum(
152
- [
153
- len(summary.measured_lines)
154
- for summary in self._diff_violations().values()
155
- ]
152
+ len(summary.measured_lines) for summary in self._diff_violations().values()
156
153
  )
157
154
 
158
155
  def total_num_violations(self):
@@ -193,13 +190,14 @@ class BaseReportGenerator(ABC):
193
190
 
194
191
  To make this efficient, we cache and reuse the result.
195
192
  """
193
+
196
194
  src_paths_changed = self._diff.src_paths_changed()
197
195
  if not self._diff_violations_dict:
198
196
  try:
199
197
  violations = self._violations.violations_batch(src_paths_changed)
200
198
  self._diff_violations_dict = {
201
- os.path.normpath(src_path): DiffViolations(
202
- violations.get(os.path.normpath(src_path), []),
199
+ to_unix_path(src_path): DiffViolations(
200
+ violations.get(to_unix_path(src_path), []),
203
201
  self._violations.measured_lines(src_path),
204
202
  self._diff.lines_changed(src_path),
205
203
  )
@@ -128,21 +128,16 @@ class Snippet:
128
128
  if i in self._violation_lines:
129
129
  notice = "!"
130
130
 
131
- format_string = "{} {:>" + str(line_number_length) + "} {}"
132
- text += format_string.format(notice, i, line)
131
+ text += f"{notice} {i:>{line_number_length}} {line}"
133
132
 
134
- header = "Lines %d-%d\n\n" % (self._start_line, self._last_line)
133
+ header = f"Lines {self._start_line}-{self._last_line}\n\n"
135
134
  if self._lexer_name in self.LEXER_TO_MARKDOWN_CODE_HINT:
136
- return header + (
137
- "```"
138
- + self.LEXER_TO_MARKDOWN_CODE_HINT[self._lexer_name]
139
- + "\n"
140
- + text
141
- + "\n```\n"
142
- )
135
+ code_hint = self.LEXER_TO_MARKDOWN_CODE_HINT[self._lexer_name]
136
+ code_block = f"""```{code_hint}\n{text}\n```\n"""
137
+ return header + code_block
143
138
 
144
139
  # unknown programming language, return a non-decorated fenced code block:
145
- return "```\n" + text + "\n```\n"
140
+ return f"""```\n{text}\n```\n"""
146
141
 
147
142
  def terminal(self):
148
143
  """
@@ -47,6 +47,10 @@ def to_unix_path(path):
47
47
  return posixpath.normpath(os.path.normcase(path).replace("\\", "/"))
48
48
 
49
49
 
50
+ def to_unix_paths(paths):
51
+ return [to_unix_path(path) for path in paths]
52
+
53
+
50
54
  def to_unescaped_filename(filename: str) -> str:
51
55
  """Try to unescape the given filename.
52
56
 
@@ -6,6 +6,7 @@ from abc import ABC, abstractmethod
6
6
  from collections import defaultdict, namedtuple
7
7
 
8
8
  from diff_cover.command_runner import execute, run_command_for_code
9
+ from diff_cover.util import to_unix_path
9
10
 
10
11
  Violation = namedtuple("Violation", "line, message")
11
12
 
@@ -60,7 +61,7 @@ class BaseViolationReporter(ABC):
60
61
  """
61
62
  # An existing quality plugin "sqlfluff" depends on this
62
63
  # being not abstract and returning None
63
- return None
64
+ del src_path
64
65
 
65
66
  def name(self):
66
67
  """
@@ -156,6 +157,10 @@ class QualityReporter(BaseViolationReporter):
156
157
  self.violations_dict = self.driver.parse_reports(self.reports)
157
158
  return self.violations_dict[src_path]
158
159
 
160
+ if not os.path.exists(src_path):
161
+ self.violations_dict[src_path] = []
162
+ return self.violations_dict[src_path]
163
+
159
164
  if self.driver_tool_installed is None:
160
165
  self.driver_tool_installed = self.driver.installed()
161
166
  if not self.driver_tool_installed:
@@ -165,8 +170,7 @@ class QualityReporter(BaseViolationReporter):
165
170
  if self.options:
166
171
  for arg in self.options.split():
167
172
  command.append(arg)
168
- if os.path.exists(src_path):
169
- command.append(src_path.encode(sys.getfilesystemencoding()))
173
+ command.append(src_path.encode(sys.getfilesystemencoding()))
170
174
 
171
175
  stdout, stderr = execute(command, self.driver.exit_codes)
172
176
  output = stderr if self.driver.output_stderr else stdout
@@ -229,13 +233,15 @@ class RegexBasedDriver(QualityDriver):
229
233
  if self.expression.flags & re.MULTILINE:
230
234
  matches = (match for match in re.finditer(self.expression, report))
231
235
  else:
232
- matches = (self.expression.match(line) for line in report.split("\n"))
236
+ matches = (
237
+ self.expression.match(line.rstrip()) for line in report.split("\n")
238
+ )
233
239
  for match in matches:
234
240
  if match is not None:
235
241
  src, line_number, message = match.groups()
236
242
  # Transform src to a relative path, if it isn't already
237
- src = os.path.relpath(src)
238
- violation = Violation(int(line_number), message)
243
+ src = to_unix_path(os.path.relpath(src))
244
+ violation = Violation(int(line_number), message.rstrip())
239
245
  violations_dict[src].append(violation)
240
246
  return violations_dict
241
247
 
@@ -2,12 +2,12 @@
2
2
  Classes for querying the information in a test coverage report.
3
3
  """
4
4
 
5
- import os
6
5
  import xml.etree.ElementTree as etree
7
6
  from collections import defaultdict
8
7
 
9
8
  from diff_cover.command_runner import run_command_for_code
10
9
  from diff_cover.git_path import GitPathTool
10
+ from diff_cover.util import to_unix_path
11
11
  from diff_cover.violationsreporters.base import (
12
12
  QualityDriver,
13
13
  RegexBasedDriver,
@@ -66,12 +66,11 @@ class CheckstyleXmlDriver(QualityDriver):
66
66
  for file_tree in files:
67
67
  for error in file_tree.findall("error"):
68
68
  line_number = error.get("line")
69
- error_str = "{}: {}".format(
70
- error.get("severity"), error.get("message")
71
- )
72
- violation = Violation(int(line_number), error_str)
69
+ severity = error.get("severity")
70
+ message = error.get("message")
71
+ violation = Violation(int(line_number), f"{severity}: {message}")
73
72
  filename = GitPathTool.relative_path(file_tree.get("name"))
74
- violations_dict[filename].append(violation)
73
+ violations_dict[to_unix_path(filename)].append(violation)
75
74
  return violations_dict
76
75
 
77
76
  def installed(self):
@@ -113,7 +112,7 @@ class FindbugsXmlDriver(QualityDriver):
113
112
  error_str = f"{category}: {short_message}"
114
113
  violation = Violation(line_number, error_str)
115
114
  filename = GitPathTool.relative_path(line.get("sourcepath"))
116
- violations_dict[filename].append(violation)
115
+ violations_dict[to_unix_path(filename)].append(violation)
117
116
 
118
117
  return violations_dict
119
118
 
@@ -149,11 +148,11 @@ class PmdXmlDriver(QualityDriver):
149
148
  for node_file in node_files:
150
149
  for error in node_file.findall("violation"):
151
150
  line_number = error.get("beginline")
152
- error_str = "{}: {}".format(error.get("rule"), error.text.strip())
153
- violation = Violation(int(line_number), error_str)
151
+ rule = error.get("rule")
152
+ message = error.text.strip()
153
+ violation = Violation(int(line_number), f"{rule}: {message}")
154
154
  filename = GitPathTool.relative_path(node_file.get("name"))
155
- filename = filename.replace(os.sep, "/")
156
- violations_dict[filename].append(violation)
155
+ violations_dict[to_unix_path(filename)].append(violation)
157
156
 
158
157
  return violations_dict
159
158