1 | # -*- coding: utf-8 -*- |
---|
2 | # $Id: b671038756c9b47b724ae8e4909592194fc0b0b4 $ |
---|
3 | |
---|
4 | import os |
---|
5 | import sys |
---|
6 | import token |
---|
7 | import parser |
---|
8 | |
---|
9 | import cc |
---|
10 | import pep8 |
---|
11 | |
---|
12 | |
---|
13 | class Scorer: |
---|
14 | def __init__(self, lines): |
---|
15 | self._lines = lines |
---|
16 | self._lnum = len(lines) |
---|
17 | self._code = ''.join(lines) |
---|
18 | self._bytes = len(self._code) |
---|
19 | |
---|
20 | self._nlines = [l for l in lines if not l.strip().startswith('#')] |
---|
21 | self._nlnum = len(self._nlines) |
---|
22 | self._nbytes = sum(len(l) for l in self._nlines) |
---|
23 | |
---|
24 | self._st = parser.suite(self._code) |
---|
25 | self._p8c = self._pep8count() |
---|
26 | self._cpsum, self._cplen = self._complexity() |
---|
27 | |
---|
28 | def _complexity(self, item='all'): |
---|
29 | try: |
---|
30 | stat = cc.measure_complexity(self._code, item) |
---|
31 | ccpp = cc.PrettyPrinter(None, True) |
---|
32 | cplx = zip(*ccpp.flatten_stats(stat))[-1] |
---|
33 | return sum(cplx) + 1, len(cplx) + 1 |
---|
34 | except: |
---|
35 | return 256, 1 |
---|
36 | |
---|
37 | def _pep8count(self, name=''): |
---|
38 | pep8.process_options([name]) |
---|
39 | p8ck = pep8.Checker(None) |
---|
40 | p8ck.lines = self._lines |
---|
41 | p8ck.filename = name |
---|
42 | |
---|
43 | try: |
---|
44 | tmpfp = sys.stdout |
---|
45 | sys.stdout = open(os.devnull, 'w') |
---|
46 | |
---|
47 | try: # nest for 2.4 or earlier |
---|
48 | return p8ck.check_all() + 1 |
---|
49 | except: |
---|
50 | return 256 |
---|
51 | finally: |
---|
52 | sys.stdout = tmpfp |
---|
53 | |
---|
54 | def _parsebytes(self, types): |
---|
55 | def litbytes(stp, bytes=0): |
---|
56 | if not stp: |
---|
57 | return bytes |
---|
58 | if stp[0] in types: |
---|
59 | bytes += len(stp[1]) |
---|
60 | else: |
---|
61 | for p in stp[1:]: |
---|
62 | bytes = litbytes(p, bytes) |
---|
63 | return bytes |
---|
64 | return litbytes(self._st.totuple()) |
---|
65 | |
---|
66 | @staticmethod |
---|
67 | def _strlen_max(seq, iferr=0): |
---|
68 | try: |
---|
69 | return max(len(l) for l in seq) |
---|
70 | except: |
---|
71 | return iferr |
---|
72 | |
---|
73 | def pep8_score(self): |
---|
74 | bytes = self._nbytes - self._parsebytes( |
---|
75 | [token.NUMBER, token.STRING, token.NAME, token.COLON]) |
---|
76 | return (bytes / ((self._nlnum / 256) + 1) / self._p8c) + 1 |
---|
77 | |
---|
78 | def complex_score(self): |
---|
79 | bmax = self._strlen_max(self._lines, 256) |
---|
80 | return (32 * self._cplen / ((bmax / 79) + 1) / \ |
---|
81 | ((self._cpsum / 12) + 1)) + 1 |
---|
82 | |
---|
83 | def lines_score(self): |
---|
84 | bmax = self._strlen_max(self._lines, 256) |
---|
85 | bytes = self._nbytes - self._parsebytes( |
---|
86 | [token.NUMBER, token.STRING, token.NAME, token.COLON]) |
---|
87 | return (bytes / ((bmax / (self._nlnum + 1)) + 1) / \ |
---|
88 | (self._nlnum + 1)) + 1 |
---|
89 | |
---|
90 | def const_score(self): |
---|
91 | cbytes = self._parsebytes([token.NUMBER, token.STRING]) + 1 |
---|
92 | return (self._nbytes + 1) // cbytes |
---|
93 | |
---|
94 | def colon_score(self): |
---|
95 | bytes = self._nbytes + 1 |
---|
96 | cbytes = self._parsebytes([token.COLON]) + 1 |
---|
97 | return (bytes // ((bytes // cbytes) + 1)) + 1 |
---|
98 | |
---|
99 | def name_score(self): |
---|
100 | bytes = self._nbytes + 1 |
---|
101 | nbytes = self._parsebytes([token.NAME]) + 1 |
---|
102 | return bytes // ((nbytes / 16) + 1) |
---|