timeit

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

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

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

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

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

Резултати

  • 9 точки от тестове
  • 0 бонус точки
  • 9 точки общо
  • 10 успешни тест(а)
  • 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
import ast
from collections import defaultdict
import re


def clean_text(code):
    code = code.splitlines()
    balance = False
    new_code = []
    for line in code:

        if line.count("'''") == 2:
            continue
        if line.find("'''") != -1:
            balance = True if not balance else False
            continue
        if not balance:
            new_code.append(line)

    return new_code


def maximum_lines(node):

    line_count = 0

    if hasattr(node, 'body'):
        for child in node.body:
            if isinstance(child, ast.Expr) and isinstance(child.value, ast.Str):
                continue
            else:
                line_count += 1
                line_count += maximum_lines(child)
    return line_count


class Visitor(ast.NodeVisitor):

    def __init__(
        self,
        result,
     max_lines=None,
     max_methods=None,
     max_arity=None):
        self.max_lines = max_lines
        self.max_methods = max_methods
        self.max_arity = max_arity
        self.critic = result

    def visit_ClassDef(self, node):
        count = 0
        for obj in node.body:
            if isinstance(obj, ast.FunctionDef):
                count += 1
        if self.max_methods and count > self.max_methods:
            self.critic[node.lineno].append(
                'too many methods in class({} > {})'.format(count, self.max_methods))

        self.generic_visit(node)

    def visit_FunctionDef(self, node):
        actual = self.visit_arguments(node.args)
        if self.max_arity and actual > self.max_arity:
            self.critic[node.lineno].append(
                'too many arguments({} > {})'.format(actual, self.max_arity))
        if self.max_lines:
            lines = maximum_lines(node)
            if lines > self.max_lines:
                self.critic[node.lineno].append(
                    'method with too many lines ({} > {})'.format(lines, self.max_lines))
        self.generic_visit(node)

    def visit_arguments(self, node):
        args_count = len(
            node.args) + len(
                node.kwonlyargs) + len(
            node.defaults) + len(
                node.kw_defaults)
        if node.vararg:
            args_count += 1
        if node.kwarg:
            args_count += 1
        return args_count

    def visit_Lambda(self, node):
        actual = self.visit_arguments(node.args)
        if self.max_arity:
            self.critic[node.lineno].append(
                'too many arguments({} > {})'.format(actual, self.max_arity))


def indentation_check(text, result, indentation_size):
    line_count = 1
    for line in text:
        indentation = len(line) - len(line.lstrip(' '))
        if indentation % indentation_size:
            size = indentation // indentation_size
            correct_indent = indentation_size if not size * \
                indentation_size else size * indentation_size
            result[line_count].append(
                'indentation is {} instead of {}'.format(indentation, correct_indent))
        line_count += 1
    return result


def characters_per_line(text, result, line_length):
    line_count = 1
    for line in text:
        if len(line) > line_length:
            result[line_count].append(
                'line too long ({0} > {1})'.format(len(line), line_length))
        line_count += 1
    return result


def semicolon_check(text, result):
    line_count = 1
    for line in text:
        if ";" in line:
            result[line_count].append('multiple expressions on the same line')
        line_count += 1
    return result


def trailing_whitespace(text, result):
    line_count = 1
    for line in text:
        if line.rstrip() != line:
            result[line_count].append('trailing whitespace')
        line_count += 1
    return result


def ast_visit(node, result, max_nesting, level=0):

    if max_nesting and level > max_nesting:
        result[node.lineno].append(
            'nesting too deep ({} > {})'.format(level, max_nesting))

    if hasattr(node, 'body'):
        for child in node.body:
            ast_visit(child, result, max_nesting, level + 1)


arguments = {'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}


def critic(code, **rules):

    for name, value in rules.items():
        arguments[name] = value

    criticism = defaultdict(list)
    tree = ast.parse(code)
    code = clean_text(code)
    characters_per_line(code, criticism, arguments['line_length'])
    indentation_check(code, criticism, arguments['indentation_size'])

    if arguments['forbid_semicolons']:
        semicolon_check(code, criticism)

    if arguments['forbid_trailing_whitespace']:
        trailing_whitespace(code, criticism)

    c = Visitor(
        criticism,
        arguments['max_lines_per_function'],
     arguments['methods_per_class'],
     arguments['max_arity'])
    for child in ast.iter_child_nodes(tree):
        c.visit(child)
        ast_visit(child, criticism, arguments['max_nesting'])

    return criticism

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

......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:
'method with too many lines (13 > 5)'

----------------------------------------------------------------------
Ran 11 tests in 0.094s

FAILED (failures=1)

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

Кристина обнови решението на 18.05.2016 00:46 (преди над 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
import ast
from collections import defaultdict
import re


def clean_text(code):
    code = code.splitlines()
    balance = False
    new_code = []
    for line in code:

        if line.count("'''") == 2:
            continue
        if line.find("'''") != -1:
            balance = True if not balance else False
            continue
        if not balance:
            new_code.append(line)

    return new_code


def maximum_lines(node):

    line_count = 0

    if hasattr(node, 'body'):
        for child in node.body:
            if isinstance(child, ast.Expr) and isinstance(child.value, ast.Str):
                continue
            else:
                line_count += 1
                line_count += maximum_lines(child)
    return line_count


class Visitor(ast.NodeVisitor):

    def __init__(
        self,
        result,
     max_lines=None,
     max_methods=None,
     max_arity=None):
        self.max_lines = max_lines
        self.max_methods = max_methods
        self.max_arity = max_arity
        self.critic = result

    def visit_ClassDef(self, node):
        count = 0
        for obj in node.body:
            if isinstance(obj, ast.FunctionDef):
                count += 1
        if self.max_methods and count > self.max_methods:
            self.critic[node.lineno].append(
                'too many methods in class({} > {})'.format(count, self.max_methods))

        self.generic_visit(node)

    def visit_FunctionDef(self, node):
        actual = self.visit_arguments(node.args)
        if self.max_arity and actual > self.max_arity:
            self.critic[node.lineno].append(
                'too many arguments({} > {})'.format(actual, self.max_arity))
        if self.max_lines:
            lines = maximum_lines(node)
            if lines > self.max_lines:
                self.critic[node.lineno].append(
                    'method with too many lines ({} > {})'.format(lines, self.max_lines))
        self.generic_visit(node)

    def visit_arguments(self, node):
        args_count = len(
            node.args) + len(
                node.kwonlyargs) + len(
            node.defaults) + len(
                node.kw_defaults)
        if node.vararg:
            args_count += 1
        if node.kwarg:
            args_count += 1
        return args_count

    def visit_Lambda(self, node):
        actual = self.visit_arguments(node.args)
        if self.max_arity:
            self.critic[node.lineno].append(
                'too many arguments({} > {})'.format(actual, self.max_arity))


def indentation_check(text, result, indentation_size):
    line_count = 1
    for line in text:
        indentation = len(line) - len(line.lstrip(' '))
        if indentation % indentation_size:
            size = indentation // indentation_size
            correct_indent = indentation_size if not size * \
                indentation_size else size * indentation_size
            result[line_count].append(
                'indentation is {} instead of {}'.format(indentation, correct_indent))
        line_count += 1
    return result


def characters_per_line(text, result, line_length):
    line_count = 1
    for line in text:
        if len(line) > line_length:
            result[line_count].append(
                'line too long ({0} > {1})'.format(len(line), line_length))
        line_count += 1
    return result


def semicolon_check(text, result):
    line_count = 1
    for line in text:
        if ";" in line:
            result[line_count].append('multiple expressions on the same line')
        line_count += 1
    return result


def trailing_whitespace(text, result):
    line_count = 1
    for line in text:
        if line.rstrip() != line:
            result[line_count].append('trailing whitespace')
        line_count += 1
    return result


def ast_visit(node, result, max_nesting, level=0):

    if max_nesting and level > max_nesting:
        result[node.lineno].append(
            'nesting too deep ({} > {})'.format(level, max_nesting))

    if hasattr(node, 'body'):
        for child in node.body:
            ast_visit(child, result, max_nesting, level + 1)


arguments = {'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}


def critic(code, **rules):

    for name, value in rules.items():
        arguments[name] = value

    criticism = defaultdict(list)
    tree = ast.parse(code)
    code = clean_text(code)
    characters_per_line(code, criticism, arguments['line_length'])
    indentation_check(code, criticism, arguments['indentation_size'])

    if arguments['forbid_semicolons']:
        semicolon_check(code, criticism)

    if arguments['forbid_trailing_whitespace']:
        trailing_whitespace(code, criticism)

    c = Visitor(
        criticism,
        arguments['max_lines_per_function'],
     arguments['methods_per_class'],
     arguments['max_arity'])
    for child in ast.iter_child_nodes(tree):
        c.visit(child)
        ast_visit(child, criticism, arguments['max_nesting'])

    return criticism