timeit

Програмиране с Python

Курс във Факултета по Математика и Информатика към СУ

Решение на Статичен анализ на python код от Николай Желязков

Обратно към всички решения

Към профила на Николай Желязков

Резултати

  • 9 точки от тестове
  • 0 бонус точки
  • 9 точки общо
  • 10 успешни тест(а)
  • 1 неуспешни тест(а)

Код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import ast
import re
from collections import defaultdict
from math import floor


def check_arity(node, max_arity, result):
    count_args = len(node.args.args)
    if node.args.vararg:
        count_args = count_args + 1
    if node.args.kwarg:
        count_args = count_args + 1
    if count_args > max_arity:
        msg = 'too many arguments({} > {})'.format(count_args, max_arity)
        result[node.lineno].append(msg)


def check_class_methods(cls, limit, result):
    cnt_methods = 0
    for node in ast.walk(cls):
        if isinstance(node, ast.FunctionDef):
            cnt_methods = cnt_methods + 1
    if cnt_methods > limit:
        msg = 'too many methods in class({} > {})'.format(cnt_methods, limit)
        result[cls.lineno].append(msg)


def get_lines(node):
    max_line = node.lineno
    for x in ast.walk(node):
        if hasattr(x, 'lineno') and x.lineno > max_line:
            max_line = x.lineno
    return max_line - node.lineno


def num_lines(func, max_lines):
    last_line = func.lineno
    mult_lines = 0
    for node in ast.walk(func):
        if hasattr(node, 'lineno') and node.lineno > last_line:
            last_line = node.lineno
        if hasattr(node, 'elts') or hasattr(node, 'keys'):
            mult_lines = mult_lines + get_lines(node)
        if isinstance(node, ast.Str):
            mult_lines = mult_lines + len(re.findall(r'\n', node.s))
    return last_line - func.lineno - mult_lines


def check_lines(func, max_lines, result):
    lines = num_lines(func, max_lines)
    if max_lines < lines:
        msg = 'method with too many lines ({} > {})'.format(lines, max_lines)
        result[func.lineno].append(msg)


def get_nesting(func, nesting):
    max_nesting = [nesting, 0]
    if hasattr(func, 'body'):
        for node in ast.walk(func):
            if hasattr(node, 'lineno'):
                max_nesting[1] = node.lineno
            if node is func:
                continue
            node_nesting = get_nesting(node, nesting + 1)
            if node_nesting[0] > max_nesting[0]:
                max_nesting[0] = node_nesting[0]
    return max_nesting


def check_nesting(func, max_nesting, result):
    nesting = get_nesting(func, 0)
    if nesting[0] > max_nesting:
        msg = 'nesting too deep ({} > {})'.format(nesting[0], max_nesting)
        result[nesting[1]].append(msg)


def ast_critic(code, result, **rules):
    max_arity = rules.get('max_arity', None)
    max_methods = rules.get('methods_per_class', None)
    max_lines = rules.get('max_lines_per_function', None)
    max_nesting = rules.get('max_nesting', None)
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef) or isinstance(node, ast.Lambda):
            if max_arity:
                check_arity(node, max_arity, result)
            if max_lines:
                check_lines(node, max_lines, result)
            if max_nesting:
                check_nesting(node, max_nesting, result)
        if max_methods and isinstance(node, ast.ClassDef):
            check_class_methods(node, max_methods, result)


def check_indent(line, text, indent, result):
    indentation = re.match(r'\s*', text).end()
    if indentation % indent != 0:
        i = indent
        while abs(indentation - i) > indent / 2:
            i = i + indent
        msg = 'indentation is {} instead of {}'.format(indentation, i)
        result[line].append(msg)


def check_line_len(line, text, max_line_len, result):
    if len(text) > max_line_len:
        msg = 'line too long ({} > {})'.format(len(text), max_line_len)
        result[line].append(msg)


def check_mult_exprs(line, text, result):
    if re.sub(r"('[^']*?')|(\"[^\"]*\")", '', text).find('; ') != -1:
        msg = 'multiple expressions on the same line'
        result[line].append(msg)


def check_trailing_whitespaces(line, text, result):
    if re.search(r'\s$', text):
        msg = 'trailing whitespace'
        result[line].append(msg)


def regex_critic(code, result, **rules):
    indent = rules.get('identation', 4)
    max_line_len = rules.get('line_length', 79)
    forbid_tr_whitespace = rules.get('forbid_trailing_whitespace', True)
    forbid_semicolons = rules.get('forbid_semicolons', True)
    for line, text in enumerate(code.splitlines(), 1):
        check_indent(line, text, indent, result)
        check_line_len(line, text, max_line_len, result)
        if forbid_semicolons:
            check_mult_exprs(line, text, result)
        if forbid_tr_whitespace:
            check_trailing_whitespaces(line, text, result)


def critic(code, **rules):
    result = defaultdict(list)
    ast_critic(code, result, **rules)
    regex_critic(code, result, **rules)
    return dict(result)

Лог от изпълнението

......F....
======================================================================
FAIL: test_multiple_issues_all_over_the_place (test.TestCritic)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/data/rails/pyfmi-2016/releases/20160307095126/lib/language/python/runner.py", line 67, in thread
    raise result
AssertionError: Items in the first set but not the second:
'method with too many lines (14 > 5)'

----------------------------------------------------------------------
Ran 11 tests in 0.116s

FAILED (failures=1)

История (3 версии и 0 коментара)

Николай обнови решението на 14.05.2016 20:45 (преди над 1 година)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import ast
import re
from collections import defaultdict
from math import floor


def check_arity(node, max_arity, result):
    count_args = len(node.args.args)
    if count_args > max_arity:
        msg = 'too many arguments({} > {})'.format(count_args, max_arity)
        result[node.lineno].append(msg)


def check_class_methods(cls, limit, result):
    cnt_methods = 0
    for node in ast.walk(cls):
        if isinstance(node, ast.FunctionDef):
            cnt_methods = cnt_methods + 1
    if cnt_methods > limit:
        msg = 'too many methods in class({} > {})'.format(cnt_methods, limit)
        result[cls.lineno].append(msg)


def check_lines(func, max_lines, result):
    last_line = func.lineno
    for node in ast.walk(func):
        if hasattr(node, 'lineno') and node.lineno > last_line:
            last_line = node.lineno
    lines = last_line - func.lineno
    if max_lines < lines:
        msg = 'method with too many lines ({} > {})'.format(lines, max_lines)
        result[func.lineno].append(msg)


def ast_critic(code, result, **rules):
    max_arity = rules.get('max_arity', None)
    max_methods = rules.get('methods_per_class', None)
    max_lines = rules.get('max_lines_per_function', None)
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef):
            if max_arity:
                check_arity(node, max_arity, result)
            if max_lines:
                check_lines(node, max_lines, result)
        if max_methods and isinstance(node, ast.ClassDef):
            check_class_methods(node, max_methods, result)


def check_indent(line, text, indent, result):
    indentation = re.match(r'\s*', text).end()
    if indentation % indent != 0:
        msg = 'indentation is {} instead of {}'.format(indentation, indent)
        result[line].append(msg)


def check_line_len(line, text, max_line_len, result):
    if len(text) > max_line_len:
        msg = 'line too long ({} > {})'.format(len(text), max_line_len)
        result[line].append(msg)


def check_mult_exprs(line, text, result):
    if re.sub(r"('[^']*?')|(\"[^\"]*\")", '', text).find('; ') != -1:
        msg = 'multiple expressions on the same line'
        result[line].append(msg)


def check_trailing_whitespaces(line, text, result):
    if re.search(r'\s$', text):
        msg = 'trailing whitespace'
        result[line].append(msg)


def check_nesting(line, text, max_nesting, indent, result):
    nesting = floor(re.match(r'\s*', text).end() / indent)
    if nesting > max_nesting:
        msg = 'nesting too deep ({} > {})'.format(nesting, max_nesting)
        result[line].append(msg)


def regex_critic(code, result, **rules):
    indent = rules.get('identation', 4)
    max_line_len = rules.get('line_length', 79)
    forbid_tr_whitespace = rules.get('forbid_trailing_whitespace', True)
    forbid_semicolons = rules.get('forbid_semicolons', True)
    max_nesting = rules.get('max_nesting', None)
    for line, text in enumerate(code.splitlines(), 1):
        check_indent(line, text, indent, result)
        check_line_len(line, text, max_line_len, result)
        if forbid_semicolons:
            check_mult_exprs(line, text, result)
        if forbid_tr_whitespace:
            check_trailing_whitespaces(line, text, result)
        if max_nesting:
            check_nesting(line, text, max_nesting, indent, result)


def critic(code, **rules):
    result = defaultdict(list)
    ast_critic(code, result, **rules)
    regex_critic(code, result, **rules)
    return dict(result)

Николай обнови решението на 17.05.2016 19:59 (преди над 1 година)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import ast
import re
from collections import defaultdict
from math import floor


def check_arity(node, max_arity, result):
    count_args = len(node.args.args)
    if node.args.vararg:
        count_args = count_args + 1
    if node.args.kwarg:
        count_args = count_args + 1
    if count_args > max_arity:
        msg = 'too many arguments({} > {})'.format(count_args, max_arity)
        result[node.lineno].append(msg)


def check_class_methods(cls, limit, result):
    cnt_methods = 0
    for node in ast.walk(cls):
        if isinstance(node, ast.FunctionDef):
            cnt_methods = cnt_methods + 1
    if cnt_methods > limit:
        msg = 'too many methods in class({} > {})'.format(cnt_methods, limit)
        result[cls.lineno].append(msg)


def check_lines(func, max_lines, result):
    last_line = func.lineno
    for node in ast.walk(func):
        if hasattr(node, 'lineno') and node.lineno > last_line:
            last_line = node.lineno
    lines = last_line - func.lineno
    if max_lines < lines:
        msg = 'method with too many lines ({} > {})'.format(lines, max_lines)
        result[func.lineno].append(msg)


def get_nesting(func, nesting):
    max_nesting = nesting
    if hasattr(func, 'body'):
        for node in ast.walk(func):
            if node is func:
                continue
            node_nesting = get_nesting(node, nesting + 1)
            if node_nesting > max_nesting:
                max_nesting = node_nesting
    return max_nesting


def check_nesting(func, max_nesting, result):
    nesting = get_nesting(func, 0)
    if nesting > max_nesting:
        msg = 'nesting too deep ({} > {})'.format(nesting, max_nesting)
        result[func.lineno].append(msg)


def ast_critic(code, result, **rules):
    max_arity = rules.get('max_arity', None)
    max_methods = rules.get('methods_per_class', None)
    max_lines = rules.get('max_lines_per_function', None)
    max_nesting = rules.get('max_nesting', None)
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef) or isinstance(node, ast.Lambda):
            if max_arity:
                check_arity(node, max_arity, result)
            if max_lines:
                check_lines(node, max_lines, result)
            if max_nesting:
                check_nesting(node, max_nesting, result)
        if max_methods and isinstance(node, ast.ClassDef):
            check_class_methods(node, max_methods, result)


def check_indent(line, text, indent, result):
    indentation = re.match(r'\s*', text).end()
    if indentation % indent != 0:
        i = indent
        while abs(indentation - i) > indent / 2:
            i = i + indent
        msg = 'indentation is {} instead of {}'.format(indentation, i)
        result[line].append(msg)


def check_line_len(line, text, max_line_len, result):
    if len(text) > max_line_len:
        msg = 'line too long ({} > {})'.format(len(text), max_line_len)
        result[line].append(msg)


def check_mult_exprs(line, text, result):
    if re.sub(r"('[^']*?')|(\"[^\"]*\")", '', text).find('; ') != -1:
        msg = 'multiple expressions on the same line'
        result[line].append(msg)


def check_trailing_whitespaces(line, text, result):
    if re.search(r'\s$', text):
        msg = 'trailing whitespace'
        result[line].append(msg)


def regex_critic(code, result, **rules):
    indent = rules.get('identation', 4)
    max_line_len = rules.get('line_length', 79)
    forbid_tr_whitespace = rules.get('forbid_trailing_whitespace', True)
    forbid_semicolons = rules.get('forbid_semicolons', True)
    for line, text in enumerate(code.splitlines(), 1):
        check_indent(line, text, indent, result)
        check_line_len(line, text, max_line_len, result)
        if forbid_semicolons:
            check_mult_exprs(line, text, result)
        if forbid_tr_whitespace:
            check_trailing_whitespaces(line, text, result)


def critic(code, **rules):
    result = defaultdict(list)
    ast_critic(code, result, **rules)
    regex_critic(code, result, **rules)
    return dict(result)

Николай обнови решението на 18.05.2016 14:25 (преди над 1 година)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import ast
import re
from collections import defaultdict
from math import floor


def check_arity(node, max_arity, result):
    count_args = len(node.args.args)
    if node.args.vararg:
        count_args = count_args + 1
    if node.args.kwarg:
        count_args = count_args + 1
    if count_args > max_arity:
        msg = 'too many arguments({} > {})'.format(count_args, max_arity)
        result[node.lineno].append(msg)


def check_class_methods(cls, limit, result):
    cnt_methods = 0
    for node in ast.walk(cls):
        if isinstance(node, ast.FunctionDef):
            cnt_methods = cnt_methods + 1
    if cnt_methods > limit:
        msg = 'too many methods in class({} > {})'.format(cnt_methods, limit)
        result[cls.lineno].append(msg)


def get_lines(node):
    max_line = node.lineno
    for x in ast.walk(node):
        if hasattr(x, 'lineno') and x.lineno > max_line:
            max_line = x.lineno
    return max_line - node.lineno


def num_lines(func, max_lines):
    last_line = func.lineno
    mult_lines = 0
    for node in ast.walk(func):
        if hasattr(node, 'lineno') and node.lineno > last_line:
            last_line = node.lineno
        if hasattr(node, 'elts') or hasattr(node, 'keys'):
            mult_lines = mult_lines + get_lines(node)
        if isinstance(node, ast.Str):
            mult_lines = mult_lines + len(re.findall(r'\n', node.s))
    return last_line - func.lineno - mult_lines


def check_lines(func, max_lines, result):
    lines = num_lines(func, max_lines)
    if max_lines < lines:
        msg = 'method with too many lines ({} > {})'.format(lines, max_lines)
        result[func.lineno].append(msg)


def get_nesting(func, nesting):
    max_nesting = [nesting, 0]
    if hasattr(func, 'body'):
        for node in ast.walk(func):
            if hasattr(node, 'lineno'):
                max_nesting[1] = node.lineno
            if node is func:
                continue
            node_nesting = get_nesting(node, nesting + 1)
            if node_nesting[0] > max_nesting[0]:
                max_nesting[0] = node_nesting[0]
    return max_nesting


def check_nesting(func, max_nesting, result):
    nesting = get_nesting(func, 0)
    if nesting[0] > max_nesting:
        msg = 'nesting too deep ({} > {})'.format(nesting[0], max_nesting)
        result[nesting[1]].append(msg)


def ast_critic(code, result, **rules):
    max_arity = rules.get('max_arity', None)
    max_methods = rules.get('methods_per_class', None)
    max_lines = rules.get('max_lines_per_function', None)
    max_nesting = rules.get('max_nesting', None)
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef) or isinstance(node, ast.Lambda):
            if max_arity:
                check_arity(node, max_arity, result)
            if max_lines:
                check_lines(node, max_lines, result)
            if max_nesting:
                check_nesting(node, max_nesting, result)
        if max_methods and isinstance(node, ast.ClassDef):
            check_class_methods(node, max_methods, result)


def check_indent(line, text, indent, result):
    indentation = re.match(r'\s*', text).end()
    if indentation % indent != 0:
        i = indent
        while abs(indentation - i) > indent / 2:
            i = i + indent
        msg = 'indentation is {} instead of {}'.format(indentation, i)
        result[line].append(msg)


def check_line_len(line, text, max_line_len, result):
    if len(text) > max_line_len:
        msg = 'line too long ({} > {})'.format(len(text), max_line_len)
        result[line].append(msg)


def check_mult_exprs(line, text, result):
    if re.sub(r"('[^']*?')|(\"[^\"]*\")", '', text).find('; ') != -1:
        msg = 'multiple expressions on the same line'
        result[line].append(msg)


def check_trailing_whitespaces(line, text, result):
    if re.search(r'\s$', text):
        msg = 'trailing whitespace'
        result[line].append(msg)


def regex_critic(code, result, **rules):
    indent = rules.get('identation', 4)
    max_line_len = rules.get('line_length', 79)
    forbid_tr_whitespace = rules.get('forbid_trailing_whitespace', True)
    forbid_semicolons = rules.get('forbid_semicolons', True)
    for line, text in enumerate(code.splitlines(), 1):
        check_indent(line, text, indent, result)
        check_line_len(line, text, max_line_len, result)
        if forbid_semicolons:
            check_mult_exprs(line, text, result)
        if forbid_tr_whitespace:
            check_trailing_whitespaces(line, text, result)


def critic(code, **rules):
    result = defaultdict(list)
    ast_critic(code, result, **rules)
    regex_critic(code, result, **rules)
    return dict(result)