timeit

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

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

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

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

Към профила на Андрей Иванов

Резултати

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

Код

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


ast_important_nodes = (ast.If, ast.While, ast.For, ast.With,
                       ast.Try, ast.withitem, ast.ExceptHandler,
                       ast.ClassDef, ast.FunctionDef)


def ident_level(tree, max_nesting, current_level):
    new = defaultdict(int)
    if current_level > max_nesting:
        new[tree] = current_level
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    for node in list_nodes:
        if isinstance(node, ast.ClassDef):
            new.update(ident_level(node, max_nesting, current_level))
        else:
            new.update(ident_level(node, max_nesting, current_level+1))
    return new


def max_methods_class(node_class, methods_per_class):
    count_methods = len([node for node in node_class.body if isinstance(
        node, ast.FunctionDef)])
    if methods_per_class is not None and count_methods > methods_per_class:
        return count_methods
    return False


def meth_max_class(tree, methods_per_class):
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    dict_class = defaultdict(list)
    for node in list_nodes:
        if isinstance(node, ast.ClassDef):
            count_methods = max_methods_class(node, methods_per_class)
            if count_methods is not False:
                dict_class[node] = count_methods
        dict_class.update(meth_max_class(node, methods_per_class))
    return dict_class


def max_arguments(tree, max_args):
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    dict_function = defaultdict(list)
    for node in list_nodes:
        if isinstance(node, ast.FunctionDef):
            function_args = len(node.args.args)
            if function_args > max_args:
                dict_function[node] = function_args
        dict_function.update(max_arguments(node, max_args))
    return dict_function


function_important_nodes = (ast.If, ast.While, ast.For, ast.With, ast.Try,
                            ast.withitem, ast.ExceptHandler)


def log_line_function(function):
    list_nodes = {node for node in function.body}
    for node in list_nodes:
        if isinstance(node, function_important_nodes):
            list_nodes.union(log_line_function(node))
    return list_nodes


def max_log_lines(tree, max_lines):
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    dict_function = defaultdict(list)
    for node in list_nodes:
        if isinstance(node, ast.FunctionDef):
            function_log_lines = len(log_line_function(node))
            if function_log_lines > max_lines:
                dict_function[node] = function_log_lines
        dict_function.update(max_log_lines(node, max_lines))
    return dict_function


def line_depth(tree, line, current_level):
    save_nodes = defaultdict(int)
    if line in tree.body:
        save_nodes[line] = current_level
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    for node in list_nodes:
        save_nodes.update(line_depth(node, line, current_level+1))
    return save_nodes


def all_nodes(tree):
    save_nodes = [node for node in tree.body]
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    for node in list_nodes:
        save_nodes += (all_nodes(node))
    return save_nodes


def critic(code, **rules):
    list_lines = code.split('\n')
    result = defaultdict(list)
    index = 0
    for line in list_lines:
        index = index + 1
        if len(line) > rules.get("length_lines", 79):
            result[index].append(
                "line too long ({} > {})".format(len(line), rules.get(
                    "length_lines", 79)))
        line_without_str = re.sub('(".*?")|(\'.*?\')', '', line)
        line_without_str_com = re.sub('#.*', '', line_without_str)
        s = re.search(r';+', line_without_str_com)
        for_semi = rules.get("forbid_semicolons", True)
        if s is not None and for_semi is True:
            result[index].append("multiple expressions on the same line")
        s_white_space = re.search(r'.*\s+$', line)
        forbid_tr = rules.get("forbid_trailing_whitespace", True)
        if s_white_space is not None and forbid_tr is True:
            result[index].append("trailing whitespace")
    tree = ast.parse(code)
    method_in_class = rules.get("methods_per_class", None)
    if method_in_class is not None:
        classes_more_methods = meth_max_class(tree, method_in_class)
        for node in classes_more_methods:
            nd_ln = node.lineno
            result[nd_ln].append("too many methods in class({} > {})".format(
                classes_more_methods[node], method_in_class))
    args_in_function = rules.get("max_arity", None)
    log_lines_function = rules.get("max_lines_per_function", None)
    if args_in_function is not None:
        functions_more_args = max_arguments(tree, args_in_function)
        for node in functions_more_args:
            result[node.lineno].append("too many arguments({} > {}))".format(
                functions_more_args[node], args_in_function))
    if log_lines_function is not None:
        functions_more_lines = max_log_lines(tree, log_lines_function)
        for node in functions_more_lines:
            nd_ln = node.lineno
            result[nd_ln].append("method with too many lines ({} > {})".format(
                functions_more_lines[node], log_lines_function))
    max_levels = rules.get("max_nesting", None)
    if max_levels is not None:
        more_levels_dict = ident_level(tree, max_levels, 0)
        for node in more_levels_dict:
            for node_in in node.body:
                nd_ln = node_in.lineno
                result[nd_ln].append("nesting too deep ({} > {})".format(
                    more_levels_dict[node], max_levels))
    ident_size = rules.get("indentation_size", 4)
    line_checked = []
    for node in all_nodes(tree):
        depth = line_depth(tree, node, 0)
        line = list_lines[node.lineno - 1]
        line_white = re.search(r'^\s*', line)
        line_white_count = line_white.end() - line_white.start()
        rigth_chars = ident_size * depth[node]
        nd_ln = node.lineno
        if rigth_chars != line_white_count and nd_ln not in line_checked:
            result[nd_ln].append('indentation is {} instead of {}'.format(
                line_white_count, rigth_chars))
        line_checked.append(nd_ln)
    return result

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

......F..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:
'too many arguments(7 > 4))'
Items in the second set but not the first:
'too many arguments(7 > 4)'

======================================================================
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.115s

FAILED (failures=2)

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

Андрей обнови решението на 18.05.2016 15:50 (преди над 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
import re
import ast
import inspect
from collections import defaultdict


ast_important_nodes = (ast.If, ast.While, ast.For, ast.With,
                       ast.Try, ast.withitem, ast.ExceptHandler,
                       ast.ClassDef, ast.FunctionDef)


def ident_level(tree, max_nesting, current_level):
    new = defaultdict(int)
    if current_level > max_nesting:
        new[tree] = current_level
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    for node in list_nodes:
        if isinstance(node, ast.ClassDef):
            new.update(ident_level(node, max_nesting, current_level))
        else:
            new.update(ident_level(node, max_nesting, current_level+1))
    return new


def max_methods_class(node_class, methods_per_class):
    count_methods = len([node for node in node_class.body if isinstance(
        node, ast.FunctionDef)])
    if methods_per_class is not None and count_methods > methods_per_class:
        return count_methods
    return False


def meth_max_class(tree, methods_per_class):
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    dict_class = defaultdict(list)
    for node in list_nodes:
        if isinstance(node, ast.ClassDef):
            count_methods = max_methods_class(node, methods_per_class)
            if count_methods is not False:
                dict_class[node] = count_methods
        dict_class.update(meth_max_class(node, methods_per_class))
    return dict_class


def max_arguments(tree, max_args):
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    dict_function = defaultdict(list)
    for node in list_nodes:
        if isinstance(node, ast.FunctionDef):
            function_args = len(node.args.args)
            if function_args > max_args:
                dict_function[node] = function_args
        dict_function.update(max_arguments(node, max_args))
    return dict_function


function_important_nodes = (ast.If, ast.While, ast.For, ast.With, ast.Try,
                            ast.withitem, ast.ExceptHandler)


def log_line_function(function):
    list_nodes = {node for node in function.body}
    for node in list_nodes:
        if isinstance(node, function_important_nodes):
            list_nodes.union(log_line_function(node))
    return list_nodes


def max_log_lines(tree, max_lines):
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    dict_function = defaultdict(list)
    for node in list_nodes:
        if isinstance(node, ast.FunctionDef):
            function_log_lines = len(log_line_function(node))
            if function_log_lines > max_lines:
                dict_function[node] = function_log_lines
        dict_function.update(max_log_lines(node, max_lines))
    return dict_function


def line_depth(tree, line, current_level):
    save_nodes = defaultdict(int)
    if line in tree.body:
        save_nodes[line] = current_level
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    for node in list_nodes:
        save_nodes.update(line_depth(node, line, current_level+1))
    return save_nodes


def all_nodes(tree):
    save_nodes = [node for node in tree.body]
    list_nodes = [node for node in tree.body if isinstance(
        node, ast_important_nodes)]
    for node in list_nodes:
        save_nodes += (all_nodes(node))
    return save_nodes


def critic(code, **rules):
    list_lines = code.split('\n')
    result = defaultdict(list)
    index = 0
    for line in list_lines:
        index = index + 1
        if len(line) > rules.get("length_lines", 79):
            result[index].append(
                "line too long ({} > {})".format(len(line), rules.get(
                    "length_lines", 79)))
        line_without_str = re.sub('(".*?")|(\'.*?\')', '', line)
        line_without_str_com = re.sub('#.*', '', line_without_str)
        s = re.search(r';+', line_without_str_com)
        for_semi = rules.get("forbid_semicolons", True)
        if s is not None and for_semi is True:
            result[index].append("multiple expressions on the same line")
        s_white_space = re.search(r'.*\s+$', line)
        forbid_tr = rules.get("forbid_trailing_whitespace", True)
        if s_white_space is not None and forbid_tr is True:
            result[index].append("trailing whitespace")
    tree = ast.parse(code)
    method_in_class = rules.get("methods_per_class", None)
    if method_in_class is not None:
        classes_more_methods = meth_max_class(tree, method_in_class)
        for node in classes_more_methods:
            nd_ln = node.lineno
            result[nd_ln].append("too many methods in class({} > {})".format(
                classes_more_methods[node], method_in_class))
    args_in_function = rules.get("max_arity", None)
    log_lines_function = rules.get("max_lines_per_function", None)
    if args_in_function is not None:
        functions_more_args = max_arguments(tree, args_in_function)
        for node in functions_more_args:
            result[node.lineno].append("too many arguments({} > {}))".format(
                functions_more_args[node], args_in_function))
    if log_lines_function is not None:
        functions_more_lines = max_log_lines(tree, log_lines_function)
        for node in functions_more_lines:
            nd_ln = node.lineno
            result[nd_ln].append("method with too many lines ({} > {})".format(
                functions_more_lines[node], log_lines_function))
    max_levels = rules.get("max_nesting", None)
    if max_levels is not None:
        more_levels_dict = ident_level(tree, max_levels, 0)
        for node in more_levels_dict:
            for node_in in node.body:
                nd_ln = node_in.lineno
                result[nd_ln].append("nesting too deep ({} > {})".format(
                    more_levels_dict[node], max_levels))
    ident_size = rules.get("indentation_size", 4)
    line_checked = []
    for node in all_nodes(tree):
        depth = line_depth(tree, node, 0)
        line = list_lines[node.lineno - 1]
        line_white = re.search(r'^\s*', line)
        line_white_count = line_white.end() - line_white.start()
        rigth_chars = ident_size * depth[node]
        nd_ln = node.lineno
        if rigth_chars != line_white_count and nd_ln not in line_checked:
            result[nd_ln].append('indentation is {} instead of {}'.format(
                line_white_count, rigth_chars))
        line_checked.append(nd_ln)
    return result