raw_metrics.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. # Copyright (c) 2007, 2010, 2013, 2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
  2. # Copyright (c) 2013 Google, Inc.
  3. # Copyright (c) 2014 Arun Persaud <arun@nubati.net>
  4. # Copyright (c) 2015-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
  5. # Copyright (c) 2015 Mike Frysinger <vapier@gentoo.org>
  6. # Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
  7. # Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
  8. # Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
  9. # Copyright (c) 2019-2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
  10. # Copyright (c) 2020-2021 hippo91 <guillaume.peillex@gmail.com>
  11. # Copyright (c) 2020 谭九鼎 <109224573@qq.com>
  12. # Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  13. # Copyright (c) 2021 bot <bot@noreply.github.com>
  14. # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
  15. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  16. # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
  17. import sys
  18. import tokenize
  19. from typing import Any, Optional, cast
  20. from pylint.checkers import BaseTokenChecker
  21. from pylint.interfaces import ITokenChecker
  22. from pylint.reporters.ureports.nodes import Table
  23. from pylint.utils import LinterStats, diff_string
  24. if sys.version_info >= (3, 8):
  25. from typing import Literal
  26. else:
  27. from typing_extensions import Literal
  28. def report_raw_stats(
  29. sect,
  30. stats: LinterStats,
  31. old_stats: Optional[LinterStats],
  32. ) -> None:
  33. """calculate percentage of code / doc / comment / empty"""
  34. total_lines = stats.code_type_count["total"]
  35. sect.description = f"{total_lines} lines have been analyzed"
  36. lines = ["type", "number", "%", "previous", "difference"]
  37. for node_type in ("code", "docstring", "comment", "empty"):
  38. node_type = cast(Literal["code", "docstring", "comment", "empty"], node_type)
  39. total = stats.code_type_count[node_type]
  40. percent = float(total * 100) / total_lines if total_lines else None
  41. old = old_stats.code_type_count[node_type] if old_stats else None
  42. diff_str = diff_string(old, total) if old else None
  43. lines += [
  44. node_type,
  45. str(total),
  46. f"{percent:.2f}" if percent is not None else "NC",
  47. str(old) if old else "NC",
  48. diff_str if diff_str else "NC",
  49. ]
  50. sect.append(Table(children=lines, cols=5, rheaders=1))
  51. class RawMetricsChecker(BaseTokenChecker):
  52. """does not check anything but gives some raw metrics :
  53. * total number of lines
  54. * total number of code lines
  55. * total number of docstring lines
  56. * total number of comments lines
  57. * total number of empty lines
  58. """
  59. __implements__ = (ITokenChecker,)
  60. # configuration section name
  61. name = "metrics"
  62. # configuration options
  63. options = ()
  64. # messages
  65. msgs: Any = {}
  66. # reports
  67. reports = (("RP0701", "Raw metrics", report_raw_stats),)
  68. def __init__(self, linter):
  69. super().__init__(linter)
  70. def open(self):
  71. """init statistics"""
  72. self.linter.stats.reset_code_count()
  73. def process_tokens(self, tokens):
  74. """update stats"""
  75. i = 0
  76. tokens = list(tokens)
  77. while i < len(tokens):
  78. i, lines_number, line_type = get_type(tokens, i)
  79. self.linter.stats.code_type_count["total"] += lines_number
  80. self.linter.stats.code_type_count[line_type] += lines_number
  81. JUNK = (tokenize.NL, tokenize.INDENT, tokenize.NEWLINE, tokenize.ENDMARKER)
  82. def get_type(tokens, start_index):
  83. """return the line type : docstring, comment, code, empty"""
  84. i = start_index
  85. start = tokens[i][2]
  86. pos = start
  87. line_type = None
  88. while i < len(tokens) and tokens[i][2][0] == start[0]:
  89. tok_type = tokens[i][0]
  90. pos = tokens[i][3]
  91. if line_type is None:
  92. if tok_type == tokenize.STRING:
  93. line_type = "docstring"
  94. elif tok_type == tokenize.COMMENT:
  95. line_type = "comment"
  96. elif tok_type in JUNK:
  97. pass
  98. else:
  99. line_type = "code"
  100. i += 1
  101. if line_type is None:
  102. line_type = "empty"
  103. elif i < len(tokens) and tokens[i][0] == tokenize.NEWLINE:
  104. i += 1
  105. return i, pos[0] - start[0] + 1, line_type
  106. def register(linter):
  107. """required method to auto register this checker"""
  108. linter.register_checker(RawMetricsChecker(linter))