• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

netromdk / vermin / 18395028078

10 Oct 2025 02:53AM UTC coverage: 99.372% (-0.3%) from 99.686%
18395028078

push

github

web-flow
Merge pull request #307 from netromdk/dev

Python 3.13 support and various fixes and features

588 of 635 new or added lines in 26 files covered. (92.6%)

3 existing lines in 1 file now uncovered.

13777 of 13864 relevant lines covered (99.37%)

7.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.67
/vermin/formats/github_format.py
1
import os
8✔
2
import re
8✔
3

4
from ..utility import bounded_str_hash, version_strings
8✔
5
from .parsable_format import ParsableFormat
8✔
6

7
def escape(value):
8✔
8
  """Escape special characters in GitHub Actions annotations. Not to be used for messages."""
9
  return (
8✔
10
    str(value)
11
    .replace('%', '%25')  # must be first
12
    .replace(',', '%2C')
13
    .replace(':', '%3A')
14
  )
15

16
class GitHubFormat(ParsableFormat):
8✔
17
  """Variant of ParsableFormat which outputs as Github Actions annotations."""
18

19
  def __init__(self, name="github"):
8✔
20
    super().__init__(name)
8✔
21
    self.order = {}
8✔
22
    """cache sort order of lines; SourceVisitor maintains an internal list of outputs from
6✔
23
    format_output_line; these are later deduplicated in a set, then passed to sort_output_lines;
24
    so caching their order uses minimal extra memory and avoids a fragile parse of the already
25
    serialized data to extract its sort order
26
    """
27
    self.cwd = os.getcwd()
8✔
28
    """current working directory, to relativize paths"""
8✔
29

30
  def format_output_line(self, msg, path=None, line=None, col=None,
8✔
31
                         versions=None, plural=None, violation=False):
32
    # default title is for generic analysis errors/notices
33
    title = "Python version requirement analysis"
8✔
34
    level = "error" if violation else "notice"
8✔
35
    if versions is not None:
8✔
36
      versions = version_strings(versions)
8✔
37
      title = "Requires Python {}".format(versions)
8✔
38
    if msg is None:
8✔
NEW
39
      if versions is None:
×
NEW
40
        msg = "user-defined symbols being ignored"
×
41
      # msg is None when providing a summary
NEW
42
      elif path is None:
×
NEW
43
        msg = "Minimum Python version required across all files: {}".format(versions)
×
44
      else:
NEW
45
        msg = "Minimum Python version required for this file: {}".format(versions)
×
46
    # github actions can't display multiline messages
47
    if msg is not None:
8✔
48
      msg = re.sub(r'(\r\n|\r|\n)', ' | ', str(msg))
8✔
49
    # relativize path so github can link to it in a PR
50
    if path is not None:
8✔
51
      try:
8✔
52
        path = os.path.abspath(path)
8✔
53
        path = os.path.relpath(path, self.cwd)
8✔
NEW
54
      except ValueError:
×
NEW
55
        pass
×
56
    vals = {
8✔
57
      "file": path,
58
      "line": line,
59
      "col": col,
60
      "title": title,
61
    }
62
    args = ",".join("{}={}".format(k, escape(v)) for k, v in vals.items() if v is not None)
8✔
63
    out = "::{} {}::{}".format(level, args, msg)
8✔
64

65
    # pre-calculate sort order
66
    order = (line or 0) + (float(col or 0) + bounded_str_hash(title + "\n" + msg)) / 1000
8✔
67
    self.order[out] = order
8✔
68
    return out
8✔
69

70
  def _sort_key(self, line):
8✔
71
    """Uses cached order, falling back to plain hash if not found."""
72
    if line not in self.order:
8✔
NEW
73
      return bounded_str_hash(line)
×
74
    return self.order[line]
8✔
75

76
  def sort_output_lines(self, lines):
8✔
77
    lines.sort(key=self._sort_key)
8✔
78
    # SourceVisitor, which calls this, overwrites its internal list of outputs; so the input
79
    # lines will never be reused; safe to clear the cache here. Only deleting the lines we've
80
    # seen, to handle depth-first AST traversals.
81
    for line in lines:
8✔
82
      if line in self.order:
8✔
83
        del self.order[line]
8✔
84
    return lines
8✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc