1# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). 2# http://www.logilab.fr/ -- mailto:contact@logilab.fr 3# 4# This program is free software; you can redistribute it and/or modify it under 5# the terms of the GNU General Public License as published by the Free Software 6# Foundation; either version 2 of the License, or (at your option) any later 7# version. 8# 9# This program is distributed in the hope that it will be useful, but WITHOUT 10# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License along with 14# this program; if not, write to the Free Software Foundation, Inc., 15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16""" Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). 17 http://www.logilab.fr/ -- mailto:contact@logilab.fr 18 19Raw metrics checker 20""" 21 22import tokenize 23 24# pylint now requires pylint >= 2.2, so this is no longer necessary 25#if not hasattr(tokenize, 'NL'): 26# raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") 27 28from logilab.common.ureports import Table 29 30from pylint.interfaces import ITokenChecker 31from pylint.utils import EmptyReport 32from pylint.checkers import BaseTokenChecker 33from pylint.reporters import diff_string 34 35def report_raw_stats(sect, stats, old_stats): 36 """calculate percentage of code / doc / comment / empty 37 """ 38 total_lines = stats['total_lines'] 39 if not total_lines: 40 raise EmptyReport() 41 sect.description = '%s lines have been analyzed' % total_lines 42 lines = ('type', 'number', '%', 'previous', 'difference') 43 for node_type in ('code', 'docstring', 'comment', 'empty'): 44 key = node_type + '_lines' 45 total = stats[key] 46 percent = float(total * 100) / total_lines 47 old = old_stats.get(key, None) 48 if old is not None: 49 diff_str = diff_string(old, total) 50 else: 51 old, diff_str = 'NC', 'NC' 52 lines += (node_type, str(total), '%.2f' % percent, 53 str(old), diff_str) 54 sect.append(Table(children=lines, cols=5, rheaders=1)) 55 56 57class RawMetricsChecker(BaseTokenChecker): 58 """does not check anything but gives some raw metrics : 59 * total number of lines 60 * total number of code lines 61 * total number of docstring lines 62 * total number of comments lines 63 * total number of empty lines 64 """ 65 66 __implements__ = (ITokenChecker,) 67 68 # configuration section name 69 name = 'metrics' 70 # configuration options 71 options = () 72 # messages 73 msgs = {} 74 # reports 75 reports = (('RP0701', 'Raw metrics', report_raw_stats),) 76 77 def __init__(self, linter): 78 BaseTokenChecker.__init__(self, linter) 79 self.stats = None 80 81 def open(self): 82 """init statistics""" 83 self.stats = self.linter.add_stats(total_lines=0, code_lines=0, 84 empty_lines=0, docstring_lines=0, 85 comment_lines=0) 86 87 def process_tokens(self, tokens): 88 """update stats""" 89 i = 0 90 tokens = list(tokens) 91 while i < len(tokens): 92 i, lines_number, line_type = get_type(tokens, i) 93 self.stats['total_lines'] += lines_number 94 self.stats[line_type] += lines_number 95 96 97JUNK = (tokenize.NL, tokenize.INDENT, tokenize.NEWLINE, tokenize.ENDMARKER) 98 99def get_type(tokens, start_index): 100 """return the line type : docstring, comment, code, empty""" 101 i = start_index 102 tok_type = tokens[i][0] 103 start = tokens[i][2] 104 pos = start 105 line_type = None 106 while i < len(tokens) and tokens[i][2][0] == start[0]: 107 tok_type = tokens[i][0] 108 pos = tokens[i][3] 109 if line_type is None: 110 if tok_type == tokenize.STRING: 111 line_type = 'docstring_lines' 112 elif tok_type == tokenize.COMMENT: 113 line_type = 'comment_lines' 114 elif tok_type in JUNK: 115 pass 116 else: 117 line_type = 'code_lines' 118 i += 1 119 if line_type is None: 120 line_type = 'empty_lines' 121 elif i < len(tokens) and tok_type == tokenize.NEWLINE: 122 i += 1 123 return i, pos[0] - start[0] + 1, line_type 124 125 126def register(linter): 127 """ required method to auto register this checker """ 128 linter.register_checker(RawMetricsChecker(linter)) 129 130