# -*- coding: utf-8 -*- # $Id$ import os import sys import zlib import token import parser import cc import pep8 def _compseq(seq, level): c = zlib.compressobj(level) return ''.join([c.compress(chunk) for chunk in seq] + [c.flush()]) def _maxstrlen(seq, iferr=0): try: return max(len(l) for l in seq) except: return iferr class Scorer: def __init__(self, lines): self._code = ''.join(lines) self._ccode = _compseq(lines, 9) self._lines = lines self._nlines = [l for l in lines if l.strip() and not l.strip().startswith('#')] self._astpl = parser.suite(self.code()).totuple() self._token_cache = {} self.complexity() self.style_errors() def tupletree(self): return self._astpl def code(self): return self._code def lines(self): return self._lines def uncommented_lines(self): return self._nlines def bytes(self): return len(self.code()) or 1 def uncommented_bytes(self): return sum(len(l) for l in self.uncommented_lines()) or 1 def rows(self): return len(self.lines()) or 1 def uncommented_rows(self): return len(self.uncommented_lines()) or 1 def compressibility(self, level=6): return float(len(self._ccode)) / self.bytes() def token_bytes(self, types): def _bytes(stp, bytes=0): if not stp: return bytes if stp[0] in types: bytes += len(stp[1]) else: for p in stp[1:]: bytes = _bytes(p, bytes) return bytes types = tuple(types) try: ret = self._token_cache[types] except KeyError: ret = self._token_cache[types] = _bytes(self.tupletree()) return ret def complexity(self, item='all', iferr=(256, 1)): try: stat = cc.measure_complexity(self._code, item) ccpp = cc.PrettyPrinter(None, True) cplx = zip(*ccpp.flatten_stats(stat))[-1] self._ccsum = sum(cplx) or 1 self._cclen = len(cplx) or 1 except: self._ccsum, self._cclen = iferr return self._ccsum, self._cclen def style_errors(self, name='', iferr=256): pep8.process_options([name]) p8ck = pep8.Checker(None) p8ck.lines = self._lines p8ck.filename = name try: tmpfp = sys.stdout sys.stdout = open(os.devnull, 'w') self._estyle = p8ck.check_all() or 1 except: self._estyle = iferr finally: sys.stdout = tmpfp return self._estyle def complex_score(self, coefficient=48): maxlen = _maxstrlen(self.lines(), 256) score = self._cclen * coefficient score /= (maxlen / (coefficient * 2)) or 1 score /= (self._ccsum / ((coefficient / 3) or 1)) or 1 score *= self.compressibility() return int(score) or 1 def style_score(self, coefficient=64): data_bytes = self.token_bytes([token.NUMBER, token.STRING, token.NAME, token.COLON]) score = self.uncommented_bytes() - data_bytes score /= (self.uncommented_rows() / coefficient) or 1 score /= self._estyle score *= (coefficient / 12) or 1 score *= self.compressibility() return int(score) or 1 def lines_score(self, coefficient=2.5): maxlen = _maxstrlen(self.lines(), 256) data_bytes = self.token_bytes([token.NUMBER, token.STRING, token.NAME, token.COLON]) score = self.uncommented_bytes() - data_bytes score /= (maxlen / self.uncommented_rows()) or 1 score /= self.uncommented_rows() score *= coefficient score *= self.compressibility() return int(score) or 1 def const_score(self, coefficient=2): const_bytes = self.token_bytes([token.NUMBER, token.STRING]) or 1 score = self.uncommented_bytes() / const_bytes score *= coefficient score *= self.compressibility() return int(score) or 1 def colon_score(self, coefficient=2): colon_bytes = self.token_bytes([token.COLON]) or 1 score = self.uncommented_bytes() score /= (score / colon_bytes) or 1 score *= coefficient score *= self.compressibility() return int(score) or 1 def name_score(self, coefficient=36): name_bytes = self.token_bytes([token.NAME]) or 1 score = self.uncommented_bytes() score /= (name_bytes / coefficient) or 1 score *= self.compressibility() return int(score) or 1