codinsight 0.0.9__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codinsight-0.0.9 → codinsight-0.2.0}/PKG-INFO +1 -1
- {codinsight-0.0.9 → codinsight-0.2.0}/pyproject.toml +1 -1
- codinsight-0.2.0/src/code_insight/code_analysis/abstract.py +91 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/code_analysis/algorithm.py +240 -31
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/code_analysis/complexity.py +175 -23
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/code_analysis/quality.py +205 -29
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/code_analysis/readability.py +243 -31
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/code_analysis/redundancy.py +143 -17
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/code_analysis/struct.py +259 -41
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/code_analysis/style.py +126 -18
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/core.py +103 -13
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/multi_analysis.py +164 -12
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/trend_analysis/trend_analysis.py +78 -6
- {codinsight-0.0.9 → codinsight-0.2.0}/src/codinsight.egg-info/PKG-INFO +1 -1
- codinsight-0.0.9/src/code_insight/code_analysis/abstract.py +0 -38
- {codinsight-0.0.9 → codinsight-0.2.0}/README.md +0 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/setup.cfg +0 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/__init__.py +0 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/code_analysis/__init__.py +0 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/py.typed +0 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/src/code_insight/trend_analysis/__init__.py +0 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/src/codinsight.egg-info/SOURCES.txt +0 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/src/codinsight.egg-info/dependency_links.txt +0 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/src/codinsight.egg-info/requires.txt +0 -0
- {codinsight-0.0.9 → codinsight-0.2.0}/src/codinsight.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Generic, TypeVar
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BaseAnalysisConfig(BaseModel):
|
|
8
|
+
"""
|
|
9
|
+
解析設定のベースクラス
|
|
10
|
+
|
|
11
|
+
Attributes
|
|
12
|
+
----------
|
|
13
|
+
enabled : bool
|
|
14
|
+
解析の有効/無効フラグ, by default True
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
enabled: bool = True
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseAnalysisResult(BaseModel):
|
|
21
|
+
"""
|
|
22
|
+
解析結果のベースモデル
|
|
23
|
+
|
|
24
|
+
Notes
|
|
25
|
+
-----
|
|
26
|
+
全ての解析結果クラスの基底クラス
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
R = TypeVar("R", bound=BaseAnalysisResult)
|
|
31
|
+
C = TypeVar("C", bound=BaseAnalysisConfig)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AbstractAnalysis(ABC, Generic[R, C]):
|
|
35
|
+
"""
|
|
36
|
+
解析抽象クラス
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
R : TypeVar
|
|
41
|
+
解析結果の型
|
|
42
|
+
C : TypeVar
|
|
43
|
+
解析設定の型
|
|
44
|
+
|
|
45
|
+
Attributes
|
|
46
|
+
----------
|
|
47
|
+
config : C
|
|
48
|
+
解析設定
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
config: C
|
|
52
|
+
|
|
53
|
+
def __init__(self, config: C | None = None) -> None:
|
|
54
|
+
"""
|
|
55
|
+
コンストラクタ
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
config : C | None, optional
|
|
60
|
+
解析設定, by default None
|
|
61
|
+
"""
|
|
62
|
+
self.config = config or self.get_default_config()
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def get_default_config(self) -> C:
|
|
66
|
+
"""
|
|
67
|
+
デフォルト設定を取得
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
C
|
|
72
|
+
デフォルト設定
|
|
73
|
+
"""
|
|
74
|
+
raise NotImplementedError("get_default_config method must be implemented")
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def analyze(self, source_code: str) -> R:
|
|
78
|
+
"""
|
|
79
|
+
コードを解析する
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
source_code : str
|
|
84
|
+
解析対象のソースコード
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
R
|
|
89
|
+
解析結果
|
|
90
|
+
"""
|
|
91
|
+
raise NotImplementedError("analyze method must be implemented")
|
|
@@ -11,8 +11,13 @@ from code_insight.code_analysis.abstract import (
|
|
|
11
11
|
class AlgorithmAnalysisConfig(BaseAnalysisConfig):
|
|
12
12
|
"""
|
|
13
13
|
アルゴリズム解析設定
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
|
|
15
|
+
Attributes
|
|
16
|
+
----------
|
|
17
|
+
max_nesting_depth_threshold : int
|
|
18
|
+
最大ネスト深度閾値, by default 4
|
|
19
|
+
cyclomatic_complexity_threshold : float
|
|
20
|
+
サイクロマティック複雑度閾値, by default 5.0
|
|
16
21
|
"""
|
|
17
22
|
|
|
18
23
|
max_nesting_depth_threshold: int = 4
|
|
@@ -22,19 +27,27 @@ class AlgorithmAnalysisConfig(BaseAnalysisConfig):
|
|
|
22
27
|
class AlgorithmAnalysisResult(BaseAnalysisResult):
|
|
23
28
|
"""
|
|
24
29
|
解析結果(アルゴリズム)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
|
|
31
|
+
Attributes
|
|
32
|
+
----------
|
|
33
|
+
if_count : int
|
|
34
|
+
if文の数
|
|
35
|
+
for_count : int
|
|
36
|
+
for文の数
|
|
37
|
+
while_count : int
|
|
38
|
+
while文の数
|
|
39
|
+
try_count : int
|
|
40
|
+
try-except文の数
|
|
41
|
+
recursion_rate : float
|
|
42
|
+
再帰関数の割合
|
|
43
|
+
lambda_count : int
|
|
44
|
+
lambda式の数
|
|
45
|
+
comprehension_count : int
|
|
46
|
+
リスト内包表記の数
|
|
47
|
+
functional_call_count : int
|
|
48
|
+
map/filter/reduce呼び出しの数
|
|
49
|
+
max_nesting_depth : int
|
|
50
|
+
制御構文の最大ネスト深度
|
|
38
51
|
"""
|
|
39
52
|
|
|
40
53
|
if_count: int
|
|
@@ -49,18 +62,50 @@ class AlgorithmAnalysisResult(BaseAnalysisResult):
|
|
|
49
62
|
|
|
50
63
|
|
|
51
64
|
class Algorithm(AbstractAnalysis[AlgorithmAnalysisResult, AlgorithmAnalysisConfig]):
|
|
52
|
-
"""
|
|
65
|
+
"""
|
|
66
|
+
解析クラス(アルゴリズム)
|
|
67
|
+
|
|
68
|
+
Notes
|
|
69
|
+
-----
|
|
70
|
+
コードのアルゴリズム的特徴を多角的に解析するクラス
|
|
71
|
+
"""
|
|
53
72
|
|
|
54
73
|
def __init__(self, config: AlgorithmAnalysisConfig | None = None) -> None:
|
|
55
|
-
"""
|
|
74
|
+
"""
|
|
75
|
+
コンストラクタ
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
config : AlgorithmAnalysisConfig | None, optional
|
|
80
|
+
アルゴリズム解析設定, by default None
|
|
81
|
+
"""
|
|
56
82
|
super().__init__(config)
|
|
57
83
|
|
|
58
84
|
def get_default_config(self) -> AlgorithmAnalysisConfig:
|
|
59
|
-
"""
|
|
85
|
+
"""
|
|
86
|
+
デフォルト設定を取得
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
AlgorithmAnalysisConfig
|
|
91
|
+
デフォルトのアルゴリズム解析設定
|
|
92
|
+
"""
|
|
60
93
|
return AlgorithmAnalysisConfig()
|
|
61
94
|
|
|
62
95
|
def analyze(self, source_code: str) -> AlgorithmAnalysisResult:
|
|
63
|
-
"""
|
|
96
|
+
"""
|
|
97
|
+
コード解析
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
source_code : str
|
|
102
|
+
解析対象のソースコード
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
AlgorithmAnalysisResult
|
|
107
|
+
アルゴリズム解析結果
|
|
108
|
+
"""
|
|
64
109
|
if not self.config.enabled:
|
|
65
110
|
return AlgorithmAnalysisResult(
|
|
66
111
|
if_count=0,
|
|
@@ -89,33 +134,115 @@ class Algorithm(AbstractAnalysis[AlgorithmAnalysisResult, AlgorithmAnalysisConfi
|
|
|
89
134
|
)
|
|
90
135
|
|
|
91
136
|
def parse_source_code(self, source_code: str) -> ast.AST:
|
|
92
|
-
"""
|
|
137
|
+
"""
|
|
138
|
+
ソースコードを解析
|
|
139
|
+
|
|
140
|
+
Parameters
|
|
141
|
+
----------
|
|
142
|
+
source_code : str
|
|
143
|
+
解析対象のソースコード
|
|
144
|
+
|
|
145
|
+
Returns
|
|
146
|
+
-------
|
|
147
|
+
ast.AST
|
|
148
|
+
解析済みのAST
|
|
149
|
+
"""
|
|
93
150
|
return ast.parse(source_code)
|
|
94
151
|
|
|
95
152
|
def get_if_count(self, source_code: str, tree: ast.AST | None = None) -> int:
|
|
96
|
-
"""
|
|
153
|
+
"""
|
|
154
|
+
if文の数を取得
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
source_code : str
|
|
159
|
+
解析対象のソースコード
|
|
160
|
+
tree : ast.AST | None, optional
|
|
161
|
+
解析済みのAST, by default None
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
int
|
|
166
|
+
if文の数
|
|
167
|
+
"""
|
|
97
168
|
tree = tree or self.parse_source_code(source_code)
|
|
98
169
|
return sum(isinstance(node, ast.If) for node in ast.walk(tree))
|
|
99
170
|
|
|
100
171
|
def get_for_count(self, source_code: str, tree: ast.AST | None = None) -> int:
|
|
101
|
-
"""
|
|
172
|
+
"""
|
|
173
|
+
for文の数を取得
|
|
174
|
+
|
|
175
|
+
Parameters
|
|
176
|
+
----------
|
|
177
|
+
source_code : str
|
|
178
|
+
解析対象のソースコード
|
|
179
|
+
tree : ast.AST | None, optional
|
|
180
|
+
解析済みのAST, by default None
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
int
|
|
185
|
+
for文の数
|
|
186
|
+
"""
|
|
102
187
|
tree = tree or self.parse_source_code(source_code)
|
|
103
188
|
return sum(isinstance(node, (ast.For, ast.AsyncFor)) for node in ast.walk(tree))
|
|
104
189
|
|
|
105
190
|
def get_while_count(self, source_code: str, tree: ast.AST | None = None) -> int:
|
|
106
|
-
"""
|
|
191
|
+
"""
|
|
192
|
+
while文の数を取得
|
|
193
|
+
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
source_code : str
|
|
197
|
+
解析対象のソースコード
|
|
198
|
+
tree : ast.AST | None, optional
|
|
199
|
+
解析済みのAST, by default None
|
|
200
|
+
|
|
201
|
+
Returns
|
|
202
|
+
-------
|
|
203
|
+
int
|
|
204
|
+
while文の数
|
|
205
|
+
"""
|
|
107
206
|
tree = tree or self.parse_source_code(source_code)
|
|
108
207
|
return sum(isinstance(node, ast.While) for node in ast.walk(tree))
|
|
109
208
|
|
|
110
209
|
def get_try_count(self, source_code: str, tree: ast.AST | None = None) -> int:
|
|
111
|
-
"""
|
|
210
|
+
"""
|
|
211
|
+
try-except文の数を取得
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
source_code : str
|
|
216
|
+
解析対象のソースコード
|
|
217
|
+
tree : ast.AST | None, optional
|
|
218
|
+
解析済みのAST, by default None
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
int
|
|
223
|
+
try-except文の数
|
|
224
|
+
"""
|
|
112
225
|
tree = tree or self.parse_source_code(source_code)
|
|
113
226
|
return sum(isinstance(node, ast.Try) for node in ast.walk(tree))
|
|
114
227
|
|
|
115
228
|
def get_recursion_rate(
|
|
116
229
|
self, source_code: str, tree: ast.AST | None = None
|
|
117
230
|
) -> float:
|
|
118
|
-
"""
|
|
231
|
+
"""
|
|
232
|
+
再帰関数の割合を取得
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
source_code : str
|
|
237
|
+
解析対象のソースコード
|
|
238
|
+
tree : ast.AST | None, optional
|
|
239
|
+
解析済みのAST, by default None
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
float
|
|
244
|
+
再帰関数の割合
|
|
245
|
+
"""
|
|
119
246
|
tree = tree or self.parse_source_code(source_code)
|
|
120
247
|
function_names: Set[str] = set()
|
|
121
248
|
recursive_functions: Set[str] = set()
|
|
@@ -139,14 +266,42 @@ class Algorithm(AbstractAnalysis[AlgorithmAnalysisResult, AlgorithmAnalysisConfi
|
|
|
139
266
|
return 0.0
|
|
140
267
|
|
|
141
268
|
def get_lambda_count(self, source_code: str, tree: ast.AST | None = None) -> int:
|
|
142
|
-
"""
|
|
269
|
+
"""
|
|
270
|
+
lambda式の数を取得
|
|
271
|
+
|
|
272
|
+
Parameters
|
|
273
|
+
----------
|
|
274
|
+
source_code : str
|
|
275
|
+
解析対象のソースコード
|
|
276
|
+
tree : ast.AST | None, optional
|
|
277
|
+
解析済みのAST, by default None
|
|
278
|
+
|
|
279
|
+
Returns
|
|
280
|
+
-------
|
|
281
|
+
int
|
|
282
|
+
lambda式の数
|
|
283
|
+
"""
|
|
143
284
|
tree = tree or self.parse_source_code(source_code)
|
|
144
285
|
return sum(isinstance(node, ast.Lambda) for node in ast.walk(tree))
|
|
145
286
|
|
|
146
287
|
def get_comprehension_count(
|
|
147
288
|
self, source_code: str, tree: ast.AST | None = None
|
|
148
289
|
) -> int:
|
|
149
|
-
"""
|
|
290
|
+
"""
|
|
291
|
+
内包表記の数を取得
|
|
292
|
+
|
|
293
|
+
Parameters
|
|
294
|
+
----------
|
|
295
|
+
source_code : str
|
|
296
|
+
解析対象のソースコード
|
|
297
|
+
tree : ast.AST | None, optional
|
|
298
|
+
解析済みのAST, by default None
|
|
299
|
+
|
|
300
|
+
Returns
|
|
301
|
+
-------
|
|
302
|
+
int
|
|
303
|
+
内包表記の数
|
|
304
|
+
"""
|
|
150
305
|
tree = tree or self.parse_source_code(source_code)
|
|
151
306
|
return sum(
|
|
152
307
|
isinstance(
|
|
@@ -158,7 +313,21 @@ class Algorithm(AbstractAnalysis[AlgorithmAnalysisResult, AlgorithmAnalysisConfi
|
|
|
158
313
|
def get_functional_call_count(
|
|
159
314
|
self, source_code: str, tree: ast.AST | None = None
|
|
160
315
|
) -> int:
|
|
161
|
-
"""
|
|
316
|
+
"""
|
|
317
|
+
map/filter/reduce呼び出しの数を取得
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
source_code : str
|
|
322
|
+
解析対象のソースコード
|
|
323
|
+
tree : ast.AST | None, optional
|
|
324
|
+
解析済みのAST, by default None
|
|
325
|
+
|
|
326
|
+
Returns
|
|
327
|
+
-------
|
|
328
|
+
int
|
|
329
|
+
map/filter/reduce呼び出しの数
|
|
330
|
+
"""
|
|
162
331
|
tree = tree or self.parse_source_code(source_code)
|
|
163
332
|
functional_names = {"map", "filter", "reduce"}
|
|
164
333
|
count = 0
|
|
@@ -176,7 +345,21 @@ class Algorithm(AbstractAnalysis[AlgorithmAnalysisResult, AlgorithmAnalysisConfi
|
|
|
176
345
|
def get_cyclomatic_complexity(
|
|
177
346
|
self, source_code: str, tree: ast.AST | None = None
|
|
178
347
|
) -> float:
|
|
179
|
-
"""
|
|
348
|
+
"""
|
|
349
|
+
循環的複雑度の平均を取得
|
|
350
|
+
|
|
351
|
+
Parameters
|
|
352
|
+
----------
|
|
353
|
+
source_code : str
|
|
354
|
+
解析対象のソースコード
|
|
355
|
+
tree : ast.AST | None, optional
|
|
356
|
+
解析済みのAST, by default None
|
|
357
|
+
|
|
358
|
+
Returns
|
|
359
|
+
-------
|
|
360
|
+
float
|
|
361
|
+
循環的複雑度の平均
|
|
362
|
+
"""
|
|
180
363
|
tree = tree or self.parse_source_code(source_code)
|
|
181
364
|
complexities = []
|
|
182
365
|
|
|
@@ -188,7 +371,19 @@ class Algorithm(AbstractAnalysis[AlgorithmAnalysisResult, AlgorithmAnalysisConfi
|
|
|
188
371
|
return sum(complexities) / len(complexities) if complexities else 0.0
|
|
189
372
|
|
|
190
373
|
def _calculate_function_complexity(self, func_node: ast.FunctionDef) -> int:
|
|
191
|
-
"""
|
|
374
|
+
"""
|
|
375
|
+
関数の循環的複雑度を計算
|
|
376
|
+
|
|
377
|
+
Parameters
|
|
378
|
+
----------
|
|
379
|
+
func_node : ast.FunctionDef
|
|
380
|
+
関数定義ノード
|
|
381
|
+
|
|
382
|
+
Returns
|
|
383
|
+
-------
|
|
384
|
+
int
|
|
385
|
+
循環的複雑度
|
|
386
|
+
"""
|
|
192
387
|
complexity = 1
|
|
193
388
|
|
|
194
389
|
for node in ast.walk(func_node):
|
|
@@ -204,7 +399,21 @@ class Algorithm(AbstractAnalysis[AlgorithmAnalysisResult, AlgorithmAnalysisConfi
|
|
|
204
399
|
def get_max_nesting_depth(
|
|
205
400
|
self, source_code: str, tree: ast.AST | None = None
|
|
206
401
|
) -> int:
|
|
207
|
-
"""
|
|
402
|
+
"""
|
|
403
|
+
制御構文の最大ネスト深度を取得
|
|
404
|
+
|
|
405
|
+
Parameters
|
|
406
|
+
----------
|
|
407
|
+
source_code : str
|
|
408
|
+
解析対象のソースコード
|
|
409
|
+
tree : ast.AST | None, optional
|
|
410
|
+
解析済みのAST, by default None
|
|
411
|
+
|
|
412
|
+
Returns
|
|
413
|
+
-------
|
|
414
|
+
int
|
|
415
|
+
最大ネスト深度
|
|
416
|
+
"""
|
|
208
417
|
tree = tree or self.parse_source_code(source_code)
|
|
209
418
|
max_depth = 0
|
|
210
419
|
|