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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import ast
from collections import defaultdict
from collections import OrderedDict


class CodeAnalyser:
    def __init__(self, code, rules):
        self.code = code
        self.rules = rules

    def run(self):
        result = defaultdict(list)
        for rule in self.rules:
            rule.analyse_code(self.code)
        self.traverse_nodes(ast.parse(self.code))
        rules_results = [rule.get_result() for rule in self.rules]
        for items in [result.items() for result in rules_results]:
            for line_no, error in items:
                result[line_no].extend(error)

        return result

    def traverse_nodes(self, node, nesting=-1):
        if not isinstance(node, ast.Module):
            for rule in self.rules:
                rule.analyse_node(node, nesting)
        if(hasattr(node, 'body')):
            for child in node.body:
                self.traverse_nodes(child, nesting + 1)


class Rule:
    def __init__(self, message, constrain=None):
        self.message = message
        self.constrain = constrain
        self.errors_container = defaultdict(list)

    def analyse_code(self, code):
        pass

    def analyse_node(self, node, nesting):
        pass

    def get_result(self):
        return self.errors_container


class ForbidTrailingWhitespace(Rule):
    def analyse_code(self, code):
        if self.constrain:
            lines = [line_no for line_no, line in enumerate(code.split('\n'))
                     if line != line.rstrip()]
            for line_no in lines:
                self.errors_container[line_no].append(self.message)


class LineLength(Rule):
    def analyse_code(self, code):
        lines = {line_no + 1: len(line) for line_no, line
                 in enumerate(code.split('\n')) if len(line) > self.constrain}
        for line_no, line_len in lines.items():
            self.errors_container[line_no].append(
                self.message % (line_len, self.constrain))


class MaxArity(Rule):
    def analyse_node(self, node, nesting):
        if (self.constrain is not None and
                isinstance(node, ast.FunctionDef) and
                len(node.args) > self.constrain):
            self.errors_container[node.lineno].append(
                self.message % (len(node.args), self.constrain))


class MaxLinesPerFunction(Rule):
    def analyse_node(self, node, nesting):
        if (self.constrain is not None and
                isinstance(node, ast.FunctionDef) and
                len(node.iter_fields) > self.constrain):
            self.errors_container[node.lineno].append(
                self.message % (len(node._fields), self.constrain))


class IndentationSize(Rule):
    def analyse_node(self, node, nesting):
        if node.col_offset != nesting * self.constrain:
            self.errors_container[node.lineno].append(
                self.message % (node.col_offset, nesting * self.constrain))


class MaxNesting(Rule):
    def analyse_node(self, node, nesting):
        if self.constrain is not None and nesting > self.constrain:
            self.errors_container[node.lineno].append(
                self.message % (nesting, self.constrain))


class ForbidSemicolons(Rule):
    def __init__(self, message, constrain=None):
        Rule.__init__(self, message, constrain)
        self.line_node_collector = defaultdict(list)

    def analyse_node(self, node, nesting):
        self.line_node_collector[node.lineno].append(node.col_offset)
        if len(self.line_node_collector[node.lineno]) > 1:
            node.col_offset = min(self.line_node_collector[node.lineno])

    def get_result(self):
        lines = [line_no for line_no, num_statements
                 in self.line_node_collector.items()
                 if len(num_statements) > 1]
        for line_no in lines:
            self.errors_container[line_no].append(self.message)

        return self.errors_container if self.constrain else {}


class MethodsPerClass(Rule):
    def analyse_node(self, node, nesting):
        if self.constrain is not None and isinstance(node, ast.ClassDef):
            number_of_methods = sum(1 for method in node.body
                                    if isinstance(method, ast.FunctionDef))
            if number_of_methods > self.constrain:
                self.errors_container[node.lineno].append(
                    self.message % (number_of_methods, self.constrain))


class RuleWrapper:
    def __init__(self, rule_instance, default_value):
        self.rule_instance = rule_instance
        self.default_value = default_value


def critic(code, **rules):
    rules_data = OrderedDict([
            ('line_length', RuleWrapper(
                LineLength('line too long (%d > %d)'), 79)),
            ('max_arity', RuleWrapper(
                MaxArity('too many arguments(%s > %s)'), None)),
            ('max_nesting', RuleWrapper(
                MaxNesting('nesting too deep (%s > %s)'), None)),
            ('forbid_semicolons', RuleWrapper(
                ForbidSemicolons(
                    'multiple expressions on the same line'), True)),
            ('indentation_size', RuleWrapper(
                IndentationSize('indentation is %s instead of %s'), 4)),
            ('methods_per_class', RuleWrapper(
                MethodsPerClass('too many methods in class(%s > %s)'), None)),
            ('forbid_trailing_whitespace', RuleWrapper(
                ForbidTrailingWhitespace('trailing whitespace'), True)),
            ('max_lines_per_function', RuleWrapper(
                MaxLinesPerFunction(
                    'method with too many lines (%s > %s)'), None))
        ])

    for rule_name, rule_wrapper in rules_data.items():
        rule = rule_wrapper.rule_instance
        rule.constrain = rules.get(
            rule_name, rule_wrapper.default_value)

    analyser = CodeAnalyser(code, [rule_wrapper.rule_instance
                                   for _, rule_wrapper in rules_data.items()])
    return analyser.run()

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

.F...EE..E.
======================================================================
ERROR: 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
AttributeError: 'FunctionDef' object has no attribute 'iter_fields'

======================================================================
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
TypeError: object of type 'arguments' has no len()

======================================================================
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
TypeError: object of type 'arguments' has no len()

======================================================================
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 second set but not the first:
'trailing whitespace'

----------------------------------------------------------------------
Ran 11 tests in 0.096s

FAILED (failures=1, errors=3)

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

Илия обнови решението на 18.05.2016 09:54 (преди над 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
import ast
from collections import defaultdict
from collections import OrderedDict


class CodeAnalyser:
    def __init__(self, code, rules):
        self.code = code
        self.rules = rules

    def run(self):
        result = defaultdict(list)
        for rule in self.rules:
            rule.analyse_code(self.code)
        self.traverse_nodes(ast.parse(self.code))
        rules_results = [rule.get_result() for rule in self.rules]
        for items in [result.items() for result in rules_results]:
            for line_no, error in items:
                result[line_no].extend(error)

        return result

    def traverse_nodes(self, node, nesting=-1):
        if not isinstance(node, ast.Module):
            for rule in self.rules:
                rule.analyse_node(node, nesting)
        if(hasattr(node, 'body')):
            for child in node.body:
                self.traverse_nodes(child, nesting + 1)


class Rule:
    def __init__(self, message, constrain=None):
        self.message = message
        self.constrain = constrain
        self.errors_container = defaultdict(list)

    def analyse_code(self, code):
        pass

    def analyse_node(self, node, nesting):
        pass

    def get_result(self):
        return self.errors_container


class ForbidTrailingWhitespace(Rule):
    def analyse_code(self, code):
        if self.constrain:
            lines = [line_no for line_no, line in enumerate(code.split('\n'))
                     if line != line.rstrip()]
            for line_no in lines:
                self.errors_container[line_no].append(self.message)


class LineLength(Rule):
    def analyse_code(self, code):
        lines = {line_no + 1: len(line) for line_no, line
                 in enumerate(code.split('\n')) if len(line) > self.constrain}
        for line_no, line_len in lines.items():
            self.errors_container[line_no].append(
                self.message % (line_len, self.constrain))


class MaxArity(Rule):
    def analyse_node(self, node, nesting):
        if (self.constrain is not None and
                isinstance(node, ast.FunctionDef) and
                len(node.args) > self.constrain):
            self.errors_container[node.lineno].append(
                self.message % (len(node.args), self.constrain))


class MaxLinesPerFunction(Rule):
    def analyse_node(self, node, nesting):
        if (self.constrain is not None and
                isinstance(node, ast.FunctionDef) and
                len(node.iter_fields) > self.constrain):
            self.errors_container[node.lineno].append(
                self.message % (len(node._fields), self.constrain))


class IndentationSize(Rule):
    def analyse_node(self, node, nesting):
        if node.col_offset != nesting * self.constrain:
            self.errors_container[node.lineno].append(
                self.message % (node.col_offset, nesting * self.constrain))


class MaxNesting(Rule):
    def analyse_node(self, node, nesting):
        if self.constrain is not None and nesting > self.constrain:
            self.errors_container[node.lineno].append(
                self.message % (nesting, self.constrain))


class ForbidSemicolons(Rule):
    def __init__(self, message, constrain=None):
        Rule.__init__(self, message, constrain)
        self.line_node_collector = defaultdict(list)

    def analyse_node(self, node, nesting):
        self.line_node_collector[node.lineno].append(node.col_offset)
        if len(self.line_node_collector[node.lineno]) > 1:
            node.col_offset = min(self.line_node_collector[node.lineno])

    def get_result(self):
        lines = [line_no for line_no, num_statements
                 in self.line_node_collector.items()
                 if len(num_statements) > 1]
        for line_no in lines:
            self.errors_container[line_no].append(self.message)

        return self.errors_container if self.constrain else {}


class MethodsPerClass(Rule):
    def analyse_node(self, node, nesting):
        if self.constrain is not None and isinstance(node, ast.ClassDef):
            number_of_methods = sum(1 for method in node.body
                                    if isinstance(method, ast.FunctionDef))
            if number_of_methods > self.constrain:
                self.errors_container[node.lineno].append(
                    self.message % (number_of_methods, self.constrain))


class RuleWrapper:
    def __init__(self, rule_instance, default_value):
        self.rule_instance = rule_instance
        self.default_value = default_value


def critic(code, **rules):
    OrderedDict([('b', 2), ('a', 1)])

    rules_data = OrderedDict([
            ('line_length', RuleWrapper(
                LineLength('line too long (%d > %d)'), 79)),
            ('max_arity', RuleWrapper(
                MaxArity('too many arguments(%s > %s)'), None)),
            ('max_nesting', RuleWrapper(
                MaxNesting('nesting too deep (%s > %s)'), None)),
            ('forbid_semicolons', RuleWrapper(
                ForbidSemicolons(
                    'multiple expressions on the same line'), True)),
            ('indentation_size', RuleWrapper(
                IndentationSize('indentation is %s instead of %s'), 4)),
            ('methods_per_class', RuleWrapper(
                MethodsPerClass('too many methods in class(%s > %s)'), None)),
            ('forbid_trailing_whitespace', RuleWrapper(
                ForbidTrailingWhitespace('trailing whitespace'), True)),
            ('max_lines_per_function', RuleWrapper(
                MaxLinesPerFunction(
                    'method with too many lines (%s > %s)'), None))
        ])

    for rule_name, rule_wrapper in rules_data.items():
        rule = rule_wrapper.rule_instance
        rule.constrain = rules.get(
            rule_name, rule_wrapper.default_value)

    analyser = CodeAnalyser(code, [rule_wrapper.rule_instance
                                   for _, rule_wrapper in rules_data.items()])
    return analyser.run()

Илия обнови решението на 18.05.2016 09:54 (преди над 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
import ast
from collections import defaultdict
from collections import OrderedDict


class CodeAnalyser:
    def __init__(self, code, rules):
        self.code = code
        self.rules = rules

    def run(self):
        result = defaultdict(list)
        for rule in self.rules:
            rule.analyse_code(self.code)
        self.traverse_nodes(ast.parse(self.code))
        rules_results = [rule.get_result() for rule in self.rules]
        for items in [result.items() for result in rules_results]:
            for line_no, error in items:
                result[line_no].extend(error)

        return result

    def traverse_nodes(self, node, nesting=-1):
        if not isinstance(node, ast.Module):
            for rule in self.rules:
                rule.analyse_node(node, nesting)
        if(hasattr(node, 'body')):
            for child in node.body:
                self.traverse_nodes(child, nesting + 1)


class Rule:
    def __init__(self, message, constrain=None):
        self.message = message
        self.constrain = constrain
        self.errors_container = defaultdict(list)

    def analyse_code(self, code):
        pass

    def analyse_node(self, node, nesting):
        pass

    def get_result(self):
        return self.errors_container


class ForbidTrailingWhitespace(Rule):
    def analyse_code(self, code):
        if self.constrain:
            lines = [line_no for line_no, line in enumerate(code.split('\n'))
                     if line != line.rstrip()]
            for line_no in lines:
                self.errors_container[line_no].append(self.message)


class LineLength(Rule):
    def analyse_code(self, code):
        lines = {line_no + 1: len(line) for line_no, line
                 in enumerate(code.split('\n')) if len(line) > self.constrain}
        for line_no, line_len in lines.items():
            self.errors_container[line_no].append(
                self.message % (line_len, self.constrain))


class MaxArity(Rule):
    def analyse_node(self, node, nesting):
        if (self.constrain is not None and
                isinstance(node, ast.FunctionDef) and
                len(node.args) > self.constrain):
            self.errors_container[node.lineno].append(
                self.message % (len(node.args), self.constrain))


class MaxLinesPerFunction(Rule):
    def analyse_node(self, node, nesting):
        if (self.constrain is not None and
                isinstance(node, ast.FunctionDef) and
                len(node.iter_fields) > self.constrain):
            self.errors_container[node.lineno].append(
                self.message % (len(node._fields), self.constrain))


class IndentationSize(Rule):
    def analyse_node(self, node, nesting):
        if node.col_offset != nesting * self.constrain:
            self.errors_container[node.lineno].append(
                self.message % (node.col_offset, nesting * self.constrain))


class MaxNesting(Rule):
    def analyse_node(self, node, nesting):
        if self.constrain is not None and nesting > self.constrain:
            self.errors_container[node.lineno].append(
                self.message % (nesting, self.constrain))


class ForbidSemicolons(Rule):
    def __init__(self, message, constrain=None):
        Rule.__init__(self, message, constrain)
        self.line_node_collector = defaultdict(list)

    def analyse_node(self, node, nesting):
        self.line_node_collector[node.lineno].append(node.col_offset)
        if len(self.line_node_collector[node.lineno]) > 1:
            node.col_offset = min(self.line_node_collector[node.lineno])

    def get_result(self):
        lines = [line_no for line_no, num_statements
                 in self.line_node_collector.items()
                 if len(num_statements) > 1]
        for line_no in lines:
            self.errors_container[line_no].append(self.message)

        return self.errors_container if self.constrain else {}


class MethodsPerClass(Rule):
    def analyse_node(self, node, nesting):
        if self.constrain is not None and isinstance(node, ast.ClassDef):
            number_of_methods = sum(1 for method in node.body
                                    if isinstance(method, ast.FunctionDef))
            if number_of_methods > self.constrain:
                self.errors_container[node.lineno].append(
                    self.message % (number_of_methods, self.constrain))


class RuleWrapper:
    def __init__(self, rule_instance, default_value):
        self.rule_instance = rule_instance
        self.default_value = default_value


def critic(code, **rules):
    rules_data = OrderedDict([
            ('line_length', RuleWrapper(
                LineLength('line too long (%d > %d)'), 79)),
            ('max_arity', RuleWrapper(
                MaxArity('too many arguments(%s > %s)'), None)),
            ('max_nesting', RuleWrapper(
                MaxNesting('nesting too deep (%s > %s)'), None)),
            ('forbid_semicolons', RuleWrapper(
                ForbidSemicolons(
                    'multiple expressions on the same line'), True)),
            ('indentation_size', RuleWrapper(
                IndentationSize('indentation is %s instead of %s'), 4)),
            ('methods_per_class', RuleWrapper(
                MethodsPerClass('too many methods in class(%s > %s)'), None)),
            ('forbid_trailing_whitespace', RuleWrapper(
                ForbidTrailingWhitespace('trailing whitespace'), True)),
            ('max_lines_per_function', RuleWrapper(
                MaxLinesPerFunction(
                    'method with too many lines (%s > %s)'), None))
        ])

    for rule_name, rule_wrapper in rules_data.items():
        rule = rule_wrapper.rule_instance
        rule.constrain = rules.get(
            rule_name, rule_wrapper.default_value)

    analyser = CodeAnalyser(code, [rule_wrapper.rule_instance
                                   for _, rule_wrapper in rules_data.items()])
    return analyser.run()