timeit

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

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

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

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

Към профила на Александра Матева

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 7 успешни тест(а)
  • 4 неуспешни тест(а)

Код

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
import ast
import re


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):
    result = too_long_line(code, line_length)
    if forbid_semicolons:
        result = mult_expressions_on_line(code, result)
    result = wrong_indentation(code, indentation_size, result)
    if methods_per_class:
        result = how_many_methods_per_class(code, methods_per_class, result)
    if forbid_trailing_whitespace:
        result = trailing_whitespace(code, result)
    if max_arity:
        result = args_per_function(code, max_arity, result)
    if max_nesting:
        result = too_much_nesting(code, indentation_size, max_nesting, result)
    return result


def mult_expressions_on_line(code, result):
    line_num = 1
    code = code.split('\n')
    for line in code:
        if ';' in line and '=' in line:
            if line_num in result:
                result[line_num].append('multiple expressions on the same line')
            else:
                result[line_num] = ['multiple expressions on the same line']
        line_num += 1
    return result


def too_long_line(code, line_length):
    line_num = 1
    result = {}
    code = code.split('\n')
    for line in code:
        if line_length <= len(line):
            result[line_num] = ['line too long (' + str(len(line)) + ' > ' + str(line_length) + ')']
        line_num += 1
    return result


def wrong_indentation(code, n, result):
    line_num = 1
    code = code.split('\n')
    pattern = r'^( *)'
    for line in code:
        a = re.match(pattern, line)
        if len(a.group(1)) % n != 0:
            if line_num in result:
                result[line_num].append('indentation is ' + str(len(a.group(1))) + ' instead of ' + str(n))
            else:
                result[line_num] = ['indentation is ' + str(len(a.group(1))) + ' instead of ' + str(n)]
        line_num += 1
    return result


def too_much_nesting(code, n, q, result):
    line_num = 1
    code = code.split('\n')
    pattern = r'^( *)'
    for line in code:
        a = re.match(pattern, line)
        if len(a.group(1)) / n > q:
            if line_num in result:
                result[line_num].append('nesting too deep ' + '(' + str(int(len(a.group(1)) / n)) + ' > ' + str(q) + ')')
            else:
                result[line_num] = ['nesting too deep ' + '(' + str(int(len(a.group(1)) / n)) + ' > ' + str(q) + ')']
        line_num += 1
    return result


def how_many_methods_per_class(code, num, result):
    parsed_code = ast.parse(code)
    res = []
    line_num = 1
    for expression in parsed_code.body:
        if isinstance(expression, ast.ClassDef):
            functions = [x.name for x in expression.body]
            if len(functions) > num:
                res.append((expression.name, len(functions)))
    code = code.split('\n')
    for line in code:
        for name in res:
            if 'class ' + name[0] in line:
                if line_num in result:
                    result[line_num].append('too many methods in class(' + str(name[1]) + ' > ' + str(num) + ')')
                else:
                    result[line_num] = ['too many methods in class(' + str(name[1]) + ' > ' + str(num) + ')']
        line_num += 1

    return result


def args_per_function(code, num, result):
    parsed_code = ast.parse(stg)
    res = []
    line_num = 0
    for expression in parsed_code.body:
        if isinstance(expression, ast.FunctionDef):
            if len(expression.args.args) > num:
                res.append((expression.name, len(expression.args.args)))
        if isinstance(expression, ast.ClassDef):
            for func in expression.body:
                if len(func.args.args) > num:
                    res.append((func.name, len(func.args.args), expression.name))
    code = code.split('\n')
    for line in code:
        line_num += 1
        for name in res:
            if len(name) > 2 and 'class ' + name[2] in line:
                if 'def ' + name[0] in line:
                    if line_num in result:
                        result[line_num].append('too many arguments(' + str(name[1]) + ' > ' + str(num) + ')')
                    else:
                        result[line_num] = ['too many arguments(' + str(name[1]) + ' > ' + str(num) + ')']
                    res.remove(name)
            else:
                if 'def ' + name[0] in line:
                    if line_num in result:
                        result[line_num].append('too many arguments(' + str(name[1]) + ' > ' + str(num) + ')')
                    else:
                        result[line_num] = ['too many arguments(' + str(name[1]) + ' > ' + str(num) + ')']
                    res.remove(name)
            break
    return result


def trailing_whitespace(code, result):
    line_num = 1
    code = code.split('\n')
    for line in code:
        if re.match(r'.* $', line):
            if line_num in result:
                result[line_num].append('trailing whitespace')
            else:
                result[line_num] = ['trailing whitespace']
        line_num += 1
    return result

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

F....FE..E.
======================================================================
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
NameError: name 'stg' is not defined

======================================================================
ERROR: 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
NameError: name 'stg' is not defined

======================================================================
FAIL: test_dict_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
AssertionError: 2 != 0

======================================================================
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

----------------------------------------------------------------------
Ran 11 tests in 0.107s

FAILED (failures=2, errors=2)

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

Александра обнови решението на 17.05.2016 16:37 (преди над 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
import ast
import re


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):
    result = too_long_line(code, line_length)
    if forbid_semicolons:
        result = mult_expressions_on_line(code, result)
    result = wrong_indentation(code, indentation_size, result)
    if methods_per_class:
        result = how_many_methods_per_class(code, methods_per_class, result)
    if forbid_trailing_whitespace:
        result = trailing_whitespace(code, result)
    if max_arity:
        result = args_per_function(code, max_arity, result)
    if max_nesting:
        result = too_much_nesting(code, indentation_size, max_nesting, result)
    return result


def mult_expressions_on_line(code, result):
    line_num = 1
    code = code.split('\n')
    for line in code:
        if ';' in line and '=' in line:
            if line_num in result:
                result[line_num].append('multiple expressions on the same line')
            else:
                result[line_num] = ['multiple expressions on the same line']
        line_num += 1
    return result


def too_long_line(code, line_length):
    line_num = 1
    result = {}
    code = code.split('\n')
    for line in code:
        if line_length <= len(line):
            result[line_num] = ['line too long (' + str(len(line)) + ' > ' + str(line_length) + ')']
        line_num += 1
    return result


def wrong_indentation(code, n, result):
    line_num = 1
    code = code.split('\n')
    pattern = r'^( *)'
    for line in code:
        a = re.match(pattern, line)
        if len(a.group(1)) % n != 0:
            if line_num in result:
                result[line_num].append('indentation is ' + str(len(a.group(1))) + ' instead of ' + str(n))
            else:
                result[line_num] = ['indentation is ' + str(len(a.group(1))) + ' instead of ' + str(n)]
        line_num += 1
    return result


def too_much_nesting(code, n, q, result):
    line_num = 1
    code = code.split('\n')
    pattern = r'^( *)'
    for line in code:
        a = re.match(pattern, line)
        if len(a.group(1)) / n > q:
            if line_num in result:
                result[line_num].append('nesting too deep ' + '(' + str(int(len(a.group(1)) / n)) + ' > ' + str(q) + ')')
            else:
                result[line_num] = ['nesting too deep ' + '(' + str(int(len(a.group(1)) / n)) + ' > ' + str(q) + ')']
        line_num += 1
    return result


def how_many_methods_per_class(code, num, result):
    parsed_code = ast.parse(code)
    res = []
    line_num = 1
    for expression in parsed_code.body:
        if isinstance(expression, ast.ClassDef):
            functions = [x.name for x in expression.body]
            if len(functions) > num:
                res.append((expression.name, len(functions)))
    code = code.split('\n')
    for line in code:
        for name in res:
            if 'class ' + name[0] in line:
                if line_num in result:
                    result[line_num].append('too many methods in class(' + str(name[1]) + ' > ' + str(num) + ')')
                else:
                    result[line_num] = ['too many methods in class(' + str(name[1]) + ' > ' + str(num) + ')']
        line_num += 1

    return result


def args_per_function(code, num, result):
    parsed_code = ast.parse(stg)
    res = []
    line_num = 0
    for expression in parsed_code.body:
        if isinstance(expression, ast.FunctionDef):
            if len(expression.args.args) > num:
                res.append((expression.name, len(expression.args.args)))
        if isinstance(expression, ast.ClassDef):
            for func in expression.body:
                if len(func.args.args) > num:
                    res.append((func.name, len(func.args.args), expression.name))
    code = code.split('\n')
    for line in code:
        line_num += 1
        for name in res:
            if len(name) > 2 and 'class ' + name[2] in line:
                if 'def ' + name[0] in line:
                    if line_num in result:
                        result[line_num].append('too many arguments(' + str(name[1]) + ' > ' + str(num) + ')')
                    else:
                        result[line_num] = ['too many arguments(' + str(name[1]) + ' > ' + str(num) + ')']
                    res.remove(name)
            else:
                if 'def ' + name[0] in line:
                    if line_num in result:
                        result[line_num].append('too many arguments(' + str(name[1]) + ' > ' + str(num) + ')')
                    else:
                        result[line_num] = ['too many arguments(' + str(name[1]) + ' > ' + str(num) + ')']
                    res.remove(name)
            break
    return result


def trailing_whitespace(code, result):
    line_num = 1
    code = code.split('\n')
    for line in code:
        if re.match(r'.* $', line):
            if line_num in result:
                result[line_num].append('trailing whitespace')
            else:
                result[line_num] = ['trailing whitespace']
        line_num += 1
    return result