timeit

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

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

Решение на Статичен анализ на python код от Галин Ангелов

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

Към профила на Галин Ангелов

Резултати

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

Код

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 ast
from collections import defaultdict


def line_length(code, line_length=79):
    template = "line too long ({} > {})"
    result = {}
    lines = code.splitlines()
    for line in range(0, len(lines)):
        if len(lines[line]) > line_length:
            result[line + 1] = template.format(len(lines[line]), line_length)
    return result


def forbid_semicolons(code, forbid_semicolons=True):
    result = {}
    lines = code.splitlines()
    for line in range(0, len(lines)):
        if forbid_semicolons is True and ';' in lines[line]:
            result[line + 1] = "multiple expressions on the same line"
    return result


def max_arity(code, max_arity=1):
    template = 'too many arguments({} > {})'
    result = {}
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef):
            args_len = len(node.args.args)
            if max_arity is not None and args_len > max_arity:
                lineno = node.body[0].value.lineno
                result[lineno - 1] = template.format(args_len, max_arity)
    return result


def methods_for_each_class(class_node):
    result = 0
    for node in ast.walk(class_node):
        if isinstance(node, ast.FunctionDef):
            result += 1
    return result


def methods_per_class(code, methods=None):
    template = 'too many methods in class({} > {})'
    result = {}
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.ClassDef):
            method_res = methods_for_each_class(node)
            if methods is not None and method_res > methods:
                result[1] = template.format(method_res, methods)
    return result


def forbid_trailing_whitespace(code, forbid_trailing_whitespace=True):
    result = {}
    lines = code.splitlines()
    for line in range(0, len(lines)):
        if forbid_trailing_whitespace is True and lines[line].endswith(" "):
            result[line + 1] = "trailing whitespaces"
    return result


def indentation_size(code, indentation_size=4):
    template = "indentation is {} instead of {}"
    result = {}
    lines = code.splitlines()
    previous = lines[0]
    first_spaces = len(previous) - len(previous.lstrip(' '))
    for line in range(1, len(lines)):
        leading_spaces = len(lines[line]) - len(lines[line].lstrip(' '))
        total = first_spaces + indentation_size
        if leading_spaces == first_spaces or total == leading_spaces:
            first_spaces = leading_spaces
        else:
            wrong = total + leading_spaces % 4
            result[line + 1] = template.format(wrong, indentation_size)

    return result


def max_nesting(code, max_nesting=None):  
    template = "nesting too deep ({} > {})"
    result = {}
    lines = code.splitlines()
    previous = lines[0]
    first_spaces = len(previous) - len(previous.lstrip(' '))
    res = 0
    for line in range(1, len(lines)):
        leading_spaces = len(lines[line]) - len(lines[line].lstrip(' '))
        total = first_spaces + 4
        if leading_spaces == first_spaces or total == leading_spaces:
            first_spaces = leading_spaces
            res = res + 1
        if max_nesting is not None and res > max_nesting:
            result[line + 1] = template.format(res, max_nesting)
            break

    return result

def max_lines_helper(node):
    if 'body' not in node._fields:
        return 1
    else:
        res = 1
        for inner_node in node.body:
            res = res + max_lines_helper(inner_node)
    return res

def max_lines_per_function(code, max_lines_per_function=None):
    template = "method with too many lines ({} > {})"
    result ={}
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef):
            res = 0
            for inner_node in node.body:
                res = res + max_lines_helper(inner_node)
                max = max_lines_per_function
                if max is not None and res > max:
                    result[node.lineno] = template.format(res, max)
    return result


def critic(code, **rules):
    d1 = forbid_semicolons(code)
    d2 = line_length(code)
    d3 = max_arity(code)
    d4 = methods_per_class(code)
    d5 = forbid_trailing_whitespace(code)
    d6 = indentation_size(code)
    d7 = max_lines_per_function(code)
    d8 = max_nesting(code)
    for key, value in rules.items():
        if key == 'forbid_semicolons':
            d1 = forbid_semicolons(code, value)
        if key == 'line_length':
            d2 = line_length(code, value)
        if key == 'max_arity':
            d3 = max_arity(code, value)
        if key == 'methods_per_class':
            d4 = methods_per_class(code, value)
        if key == 'forbid_trailing_whitespace':
            d5 = forbid_trailing_whitespace(code, value)
        if key == 'indentation_size':
            d6 = indentation_size(code, value)
        if key == 'max_lines_per_function':
            d7 = max_lines_per_function(code, value)
        if key == 'max_nesting':
            d8 = max_nesting(code, value)

    dd = defaultdict(list)

    for d in (d1, d2, d3, d4, d5, d6, d7, d8):
        for key, value in d.items():
            dd[key].append(value)

    return(dict(dd))

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

FF....EF.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
AttributeError: 'If' object has no attribute 'value'

======================================================================
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
AttributeError: 'Pass' object has no attribute 'value'

======================================================================
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_forbid_trailing_whitespace (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:
'trailing whitespaces'
Items in the second set but not the first:
'trailing whitespace'

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

----------------------------------------------------------------------
Ran 11 tests in 0.108s

FAILED (failures=3, errors=2)

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

Галин обнови решението на 18.05.2016 14:22 (преди над 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 ast
from collections import defaultdict


def line_length(code, line_length=79):
    template = "line too long ({} > {})"
    result = {}
    lines = code.splitlines()
    for line in range(0, len(lines)):
        if len(lines[line]) > line_length:
            result[line + 1] = template.format(len(lines[line]), line_length)
    return result


def forbid_semicolons(code, forbid_semicolons=True):
    result = {}
    lines = code.splitlines()
    for line in range(0, len(lines)):
        if forbid_semicolons is True and ';' in lines[line]:
            result[line + 1] = "multiple expressions on the same line"
    return result


def max_arity(code, max_arity=1):
    template = 'too many arguments({} > {})'
    result = {}
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef):
            args_len = len(node.args.args)
            if max_arity is not None and args_len > max_arity:
                lineno = node.body[0].value.lineno
                result[lineno - 1] = template.format(args_len, max_arity)
    return result


def methods_for_each_class(class_node):
    result = 0
    for node in ast.walk(class_node):
        if isinstance(node, ast.FunctionDef):
            result += 1
    return result


def methods_per_class(code, methods=None):
    template = 'too many methods in class({} > {})'
    result = {}
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.ClassDef):
            method_res = methods_for_each_class(node)
            if methods is not None and method_res > methods:
                result[1] = template.format(method_res, methods)
    return result


def forbid_trailing_whitespace(code, forbid_trailing_whitespace=True):
    result = {}
    lines = code.splitlines()
    for line in range(0, len(lines)):
        if forbid_trailing_whitespace is True and lines[line].endswith(" "):
            result[line + 1] = "trailing whitespaces"
    return result


def indentation_size(code, indentation_size=4):
    template = "indentation is {} instead of {}"
    result = {}
    lines = code.splitlines()
    previous = lines[0]
    first_spaces = len(previous) - len(previous.lstrip(' '))
    for line in range(1, len(lines)):
        leading_spaces = len(lines[line]) - len(lines[line].lstrip(' '))
        total = first_spaces + indentation_size
        if leading_spaces == first_spaces or total == leading_spaces:
            first_spaces = leading_spaces
        else:
            wrong = total + leading_spaces % 4
            result[line + 1] = template.format(wrong, indentation_size)

    return result


def max_nesting(code, max_nesting=None):  
    template = "nesting too deep ({} > {})"
    result = {}
    lines = code.splitlines()
    previous = lines[0]
    first_spaces = len(previous) - len(previous.lstrip(' '))
    res = 0
    for line in range(1, len(lines)):
        leading_spaces = len(lines[line]) - len(lines[line].lstrip(' '))
        total = first_spaces + 4
        if leading_spaces == first_spaces or total == leading_spaces:
            first_spaces = leading_spaces
            res = res + 1
        if max_nesting is not None and res > max_nesting:
            result[line + 1] = template.format(res, max_nesting)
            break

    return result

def max_lines_helper(node):
    if 'body' not in node._fields:
        return 1
    else:
        res = 1
        for inner_node in node.body:
            res = res + max_lines_helper(inner_node)
    return res

def max_lines_per_function(code, max_lines_per_function=None):
    template = "method with too many lines ({} > {})"
    result ={}
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef):
            res = 0
            for inner_node in node.body:
                res = res + max_lines_helper(inner_node)
                max = max_lines_per_function
                if max is not None and res > max:
                    result[node.lineno] = template.format(res, max)
    return result


def critic(code, **rules):
    d1 = forbid_semicolons(code)
    d2 = line_length(code)
    d3 = max_arity(code)
    d4 = methods_per_class(code)
    d5 = forbid_trailing_whitespace(code)
    d6 = indentation_size(code)
    d7 = max_lines_per_function(code)
    d8 = max_nesting(code)
    for key, value in rules.items():
        if key == 'forbid_semicolons':
            d1 = forbid_semicolons(code, value)
        if key == 'line_length':
            d2 = line_length(code, value)
        if key == 'max_arity':
            d3 = max_arity(code, value)
        if key == 'methods_per_class':
            d4 = methods_per_class(code, value)
        if key == 'forbid_trailing_whitespace':
            d5 = forbid_trailing_whitespace(code, value)
        if key == 'indentation_size':
            d6 = indentation_size(code, value)
        if key == 'max_lines_per_function':
            d7 = max_lines_per_function(code, value)
        if key == 'max_nesting':
            d8 = max_nesting(code, value)

    dd = defaultdict(list)

    for d in (d1, d2, d3, d4, d5, d6, d7, d8):
        for key, value in d.items():
            dd[key].append(value)

    return(dict(dd))