timeit

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

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

Решение на Статичен анализ на python код от Мартин Стоев

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

Към профила на Мартин Стоев

Резултати

  • 3 точки от тестове
  • 0 бонус точки
  • 3 точки общо
  • 3 успешни тест(а)
  • 8 неуспешни тест(а)

Код

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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import re
import ast
from os import linesep


def critic(code, line_length=79, forbid_semicolons=True, max_nesting=None,
           indentation_size=4, methods_per_class=None, max_arity=None,
           forbid_trailing_whitespace=True, max_lines_per_function=None):
    errors = {}
    syntaxly_wrong = False
    for x in check_indentation_size(code, indentation_size):
        syntaxly_wrong = True
        msg = 'indentation is <<' + str(x[1]) + \
              '>> instead of <<' + str(indentation_size) + '>>'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    if not syntaxly_wrong:
        tree = ast.parse(code)
        if max_lines_per_function:
            max_lines = max_lines_per_function
            for x in check_max_lines_per_function(tree, max_lines):
                msg = 'method with too many lines (<<' + str(x[1]) + \
                      '>> > <<' + str(max_lines_per_function) + '>>)'
                if x[0] in errors:
                    errors[x[0]].add(msg)
                else:
                    errors[x[0]] = {msg}
        if max_nesting:
            for x in check_max_nesting(tree, max_nesting):
                msg = 'nesting too deep (<<' + str(x[1]) + \
                      '>> > <<' + str(max_nesting) + '>>)'
                if x[0] in errors:
                    errors[x[0]].add(msg)
                else:
                    errors[x[0]] = {msg}
        if methods_per_class:
            for x in check_methods_per_class(tree, methods_per_class):
                msg = 'too many methods in class(<<' + str(x[1]) + \
                      '>> > <<' + str(methods_per_class) + '>>)'
                if x[0] in errors:
                    errors[x[0]].add(msg)
                else:
                    errors[x[0]] = {msg}
        if max_arity:
            for x in check_max_arity(tree, max_arity):
                msg = 'too many arguments(<<' + str(x[1]) + \
                      '>> > <<' + str(max_arity) + '>>)'
                if x[0] in errors:
                    errors[x[0]].add(msg)
                else:
                    errors[x[0]] = {msg}
    for x in check_line_length(code, line_length):
        msg = 'line too long (<<' + str(x[1]) + \
              '>> > <<' + str(line_length) + '>>)'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    for x in check_forbidden_semicolon(code):
        msg = 'multiple expressions on the same line'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    for x in check_forbidden_trailing_whitespace(code):
        msg = 'trailing whitespace'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    for x in check_line_length(code, line_length):
        msg = 'line too long (<<' + str(x[1]) + \
              '>> > <<' + str(line_length) + '>>)'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    return errors


def get_all_functions(ast_object):
    result = set([x for x in ast_object.body
                  if isinstance(x, ast.FunctionDef)])
    for c in [x for x in ast_object.body if isinstance(x, ast.ClassDef)]:
        result |= get_all_functions(c)
    return result


def count_logical_rows(ast_object):
    result = 1
    if hasattr(ast_object, 'body'):
        for element in ast_object.body:
            result += count_logical_rows(element)
    else:
        return 0
    return result


def check_max_lines_per_function(ast_object, max):
    result = set()
    functions = get_all_functions(ast_object)
    for func in functions:
        current = count_logical_rows(func) - 1
        if current > max:
            result.add((func.lineno, current, max))
    return result


def check_nesting(ast_object):
    current = 1
    elements = [x for x in ast_object.body if not isinstance(x, ast.Expr)]
    for element in elements:
        current = max(current, check_nesting(element))
    return current + 1


def check_max_nesting(ast_object, max):
    result = set()
    all_functions = get_all_functions(ast_object)
    for func in all_functions:
        for element in func.body:
            current = check_nesting(element)
            if current > max:
                result.add((element.lineno, current, max))
    return result


def check_max_arity(ast_object, max):
    return [(f.lineno, len(f.args.args), max)
            for f in get_all_functions(ast_object) if len(f.args.args) >= max]

tree = ast.parse('asds', mode='exec')


def check_methods_per_class(ast_object, max, inside_class=False):
    result = set()  # tuples inside (line, actual, allowed)
    if inside_class:
        methods = [x for x in ast_object.body
                   if isinstance(x, ast.FunctionDef)]
        if len(methods) >= max:
            result.add((ast_object.lineno, len(methods), max))
    classes = [x for x in ast_object.body if isinstance(x, ast.ClassDef)]
    for c in classes:
        result |= check_methods_per_class(c, max, True)
    return result


def check_line_length(code, length):
    result = set()
    for i, j in enumerate(code.split(linesep)):
        if(len(j) > length):
            result.add((i + 1, len(j), length))
    return result


def check_forbidden_semicolon(code):
    result = set()
    for i, j in enumerate(code.split(linesep)):
        if(';' in j):
            result.add((i + 1, 0, 0))
    return result


def check_forbidden_trailing_whitespace(code):
    result = set()
    for i, j in enumerate(code.split(linesep)[0:-1]):
        if(j[-1] == ' '):
            result.add((i + 1, 0, 0))
    return result


def check_indentation_size(code, size):
    result = set()
    reg = r'\t*( *).*'
    for i, j in enumerate(code.split(linesep)):
        m = re.search(reg, j)
        if len(m.group(1)) % size != 0:
            result.add((i + 1, len(m.group(1)), size))
    return result

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

..FFFFEEEF.
======================================================================
ERROR: 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
AttributeError: 'Assign' object has no attribute 'body'

======================================================================
ERROR: test_no_issues (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
IndexError: string index out of range

======================================================================
ERROR: test_too_deep_nesting (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
KeyError: 5

======================================================================
FAIL: test_indentation (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:
'indentation is <<5>> instead of <<4>>'
Items in the second set but not the first:
'indentation is 5 instead of 4'

======================================================================
FAIL: test_line_too_long (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:
'line too long (<<100>> > <<79>>)'
Items in the second set but not the first:
'line too long (100 > 79)'

======================================================================
FAIL: test_long_line_with_several_statements (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:
'line too long (<<116>> > <<79>>)'
Items in the second set but not the first:
'line too long (116 > 79)'

======================================================================
FAIL: test_max_lines_per_function (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: 0 != 1

======================================================================
FAIL: test_too_many_arguments (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: {'too many arguments(<<11>> > <<5>>)'} not found in ({'too many arguments (11 > 5)'}, {'too many arguments(11 > 5)'})

----------------------------------------------------------------------
Ran 11 tests in 0.119s

FAILED (failures=5, errors=3)

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

Мартин обнови решението на 18.05.2016 14:40 (преди над 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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import re
import ast
from os import linesep


def critic(code, line_length=79, forbid_semicolons=True, max_nesting=None,
           indentation_size=4, methods_per_class=None, max_arity=None,
           forbid_trailing_whitespace=True, max_lines_per_function=None):
    errors = {}
    tree = ast.parse(code)
    if max_lines_per_function:
        for x in check_max_lines_per_function(tree, max_lines_per_function):
            msg = 'method with too many lines (<<' + str(x[1]) + \
                  '>> > <<' + str(max_lines_per_function) + '>>)'
            if x[0] in errors:
                errors[x[0]].add(msg)
            else:
                errors[x[0]] = {msg}
    if max_nesting:
        for x in check_max_nesting(tree, max_nesting):
            msg = 'nesting too deep (<<' + str(x[1]) + \
                  '>> > <<' + str(max_nesting) + '>>)'
            if x[0] in errors:
                errors[x[0]].add(msg)
            else:
                errors[x[0]] = {msg}
    if methods_per_class:
        for x in check_methods_per_class(tree, methods_per_class):
            msg = 'too many methods in class(<<' + str(x[1]) + \
                  '>> > <<' + str(methods_per_class) + '>>)'
            if x[0] in errors:
                errors[x[0]].add(msg)
            else:
                errors[x[0]] = {msg}
    if max_arity:
        for x in check_max_arity(tree, max_arity):
            msg = 'too many arguments(<<' + str(x[1]) + \
                  '>> > <<' + str(max_arity) + '>>)'
            if x[0] in errors:
                errors[x[0]].add(msg)
            else:
                errors[x[0]] = {msg}
    for x in check_line_length(code, line_length):
        msg = 'line too long (<<' + str(x[1]) + \
              '>> > <<' + str(line_length) + '>>)'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    for x in check_forbidden_semicolon(code):
        msg = 'multiple expressions on the same line'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    for x in check_forbidden_trailing_whitespace(code):
        msg = 'trailing whitespace'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    for x in check_line_length(code, line_length):
        msg = 'line too long (<<' + str(x[1]) + \
              '>> > <<' + str(line_length) + '>>)'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    return errors


def get_all_functions(ast_object):
    result = set([x for x in ast_object.body
                  if isinstance(x, ast.FunctionDef)])
    for c in [x for x in ast_object.body if isinstance(x, ast.ClassDef)]:
        result |= get_all_functions(c)
    return result


def count_logical_rows(ast_object):
    result = 1
    if hasattr(ast_object, 'body'):
        for element in ast_object.body:
            result += count_logical_rows(element)
    else:
        return 0
    return result


def check_max_lines_per_function(ast_object, max):
    result = set()
    functions = get_all_functions(ast_object)
    for func in functions:
        current = count_logical_rows(func) - 1
        if current > max:
            result.add((func.lineno, current, max))
    return result


def check_nesting(ast_object):
    current = 1
    elements = [x for x in ast_object.body if not isinstance(x, ast.Expr)]
    for element in elements:
        current = max(current, check_nesting(element))
    return current + 1


def check_max_nesting(ast_object, max):
    result = set()
    all_functions = get_all_functions(ast_object)
    for func in all_functions:
        for element in func.body:
            current = check_nesting(element)
            if current > max:
                result.add((element.lineno, current, max))
    return result


def check_max_arity(ast_object, max):
    return [(f.lineno, len(f.args.args), max)
            for f in get_all_functions(ast_object) if len(f.args.args) >= max]

tree = ast.parse('asds', mode='exec')


def check_methods_per_class(ast_object, max, inside_class=False):
    result = set()  # tuples inside (line, actual, allowed)
    if inside_class:
        methods = [x for x in ast_object.body
                   if isinstance(x, ast.FunctionDef)]
        if len(methods) >= max:
            result.add((ast_object.lineno, len(methods), max))
    classes = [x for x in ast_object.body if isinstance(x, ast.ClassDef)]
    for c in classes:
        result |= check_methods_per_class(c, max, True)
    return result


def check_line_length(code, length):
    result = set()
    for i, j in enumerate(code.split(linesep)):
        if(len(j) > length):
            result.add((i + 1, len(j), length))
    return result


def check_forbidden_semicolon(code):
    result = set()
    for i, j in enumerate(code.split(linesep)):
        if(';' in j):
            result.add((i + 1, 0, 0))
    return result


def check_forbidden_trailing_whitespace(code):
    result = set()
    for i, j in enumerate(code.split(linesep)[0:-1]):
        if(j[-1] == ' '):
            result.add((i + 1, 0, 0))
    return result

Мартин обнови решението на 18.05.2016 16:36 (преди над 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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import re
import ast
from os import linesep


def critic(code, line_length=79, forbid_semicolons=True, max_nesting=None,
           indentation_size=4, methods_per_class=None, max_arity=None,
           forbid_trailing_whitespace=True, max_lines_per_function=None):
    errors = {}
    syntaxly_wrong = False
    for x in check_indentation_size(code, indentation_size):
        syntaxly_wrong = True
        msg = 'indentation is <<' + str(x[1]) + \
              '>> instead of <<' + str(indentation_size) + '>>'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    if not syntaxly_wrong:
        tree = ast.parse(code)
        if max_lines_per_function:
            max_lines = max_lines_per_function
            for x in check_max_lines_per_function(tree, max_lines):
                msg = 'method with too many lines (<<' + str(x[1]) + \
                      '>> > <<' + str(max_lines_per_function) + '>>)'
                if x[0] in errors:
                    errors[x[0]].add(msg)
                else:
                    errors[x[0]] = {msg}
        if max_nesting:
            for x in check_max_nesting(tree, max_nesting):
                msg = 'nesting too deep (<<' + str(x[1]) + \
                      '>> > <<' + str(max_nesting) + '>>)'
                if x[0] in errors:
                    errors[x[0]].add(msg)
                else:
                    errors[x[0]] = {msg}
        if methods_per_class:
            for x in check_methods_per_class(tree, methods_per_class):
                msg = 'too many methods in class(<<' + str(x[1]) + \
                      '>> > <<' + str(methods_per_class) + '>>)'
                if x[0] in errors:
                    errors[x[0]].add(msg)
                else:
                    errors[x[0]] = {msg}
        if max_arity:
            for x in check_max_arity(tree, max_arity):
                msg = 'too many arguments(<<' + str(x[1]) + \
                      '>> > <<' + str(max_arity) + '>>)'
                if x[0] in errors:
                    errors[x[0]].add(msg)
                else:
                    errors[x[0]] = {msg}
    for x in check_line_length(code, line_length):
        msg = 'line too long (<<' + str(x[1]) + \
              '>> > <<' + str(line_length) + '>>)'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    for x in check_forbidden_semicolon(code):
        msg = 'multiple expressions on the same line'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    for x in check_forbidden_trailing_whitespace(code):
        msg = 'trailing whitespace'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    for x in check_line_length(code, line_length):
        msg = 'line too long (<<' + str(x[1]) + \
              '>> > <<' + str(line_length) + '>>)'
        if x[0] in errors:
            errors[x[0]].add(msg)
        else:
            errors[x[0]] = {msg}
    return errors


def get_all_functions(ast_object):
    result = set([x for x in ast_object.body
                  if isinstance(x, ast.FunctionDef)])
    for c in [x for x in ast_object.body if isinstance(x, ast.ClassDef)]:
        result |= get_all_functions(c)
    return result


def count_logical_rows(ast_object):
    result = 1
    if hasattr(ast_object, 'body'):
        for element in ast_object.body:
            result += count_logical_rows(element)
    else:
        return 0
    return result


def check_max_lines_per_function(ast_object, max):
    result = set()
    functions = get_all_functions(ast_object)
    for func in functions:
        current = count_logical_rows(func) - 1
        if current > max:
            result.add((func.lineno, current, max))
    return result


def check_nesting(ast_object):
    current = 1
    elements = [x for x in ast_object.body if not isinstance(x, ast.Expr)]
    for element in elements:
        current = max(current, check_nesting(element))
    return current + 1


def check_max_nesting(ast_object, max):
    result = set()
    all_functions = get_all_functions(ast_object)
    for func in all_functions:
        for element in func.body:
            current = check_nesting(element)
            if current > max:
                result.add((element.lineno, current, max))
    return result


def check_max_arity(ast_object, max):
    return [(f.lineno, len(f.args.args), max)
            for f in get_all_functions(ast_object) if len(f.args.args) >= max]

tree = ast.parse('asds', mode='exec')


def check_methods_per_class(ast_object, max, inside_class=False):
    result = set()  # tuples inside (line, actual, allowed)
    if inside_class:
        methods = [x for x in ast_object.body
                   if isinstance(x, ast.FunctionDef)]
        if len(methods) >= max:
            result.add((ast_object.lineno, len(methods), max))
    classes = [x for x in ast_object.body if isinstance(x, ast.ClassDef)]
    for c in classes:
        result |= check_methods_per_class(c, max, True)
    return result


def check_line_length(code, length):
    result = set()
    for i, j in enumerate(code.split(linesep)):
        if(len(j) > length):
            result.add((i + 1, len(j), length))
    return result


def check_forbidden_semicolon(code):
    result = set()
    for i, j in enumerate(code.split(linesep)):
        if(';' in j):
            result.add((i + 1, 0, 0))
    return result


def check_forbidden_trailing_whitespace(code):
    result = set()
    for i, j in enumerate(code.split(linesep)[0:-1]):
        if(j[-1] == ' '):
            result.add((i + 1, 0, 0))
    return result


def check_indentation_size(code, size):
    result = set()
    reg = r'\t*( *).*'
    for i, j in enumerate(code.split(linesep)):
        m = re.search(reg, j)
        if len(m.group(1)) % size != 0:
            result.add((i + 1, len(m.group(1)), size))
    return result