codinsight 0.2.0__tar.gz → 0.4.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.
Files changed (26) hide show
  1. {codinsight-0.2.0 → codinsight-0.4.0}/PKG-INFO +3 -2
  2. {codinsight-0.2.0 → codinsight-0.4.0}/pyproject.toml +19 -2
  3. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/redundancy.py +1 -2
  4. codinsight-0.4.0/src/code_insight/code_analysis/security.py +327 -0
  5. codinsight-0.4.0/src/code_insight/code_analysis/vector.py +80 -0
  6. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/core.py +23 -5
  7. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/multi_analysis.py +9 -10
  8. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/trend_analysis/trend_analysis.py +1 -1
  9. {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/PKG-INFO +3 -2
  10. {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/SOURCES.txt +2 -0
  11. {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/requires.txt +1 -0
  12. {codinsight-0.2.0 → codinsight-0.4.0}/README.md +0 -0
  13. {codinsight-0.2.0 → codinsight-0.4.0}/setup.cfg +0 -0
  14. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/__init__.py +0 -0
  15. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/__init__.py +0 -0
  16. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/abstract.py +0 -0
  17. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/algorithm.py +0 -0
  18. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/complexity.py +0 -0
  19. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/quality.py +0 -0
  20. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/readability.py +0 -0
  21. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/struct.py +0 -0
  22. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/code_analysis/style.py +0 -0
  23. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/py.typed +0 -0
  24. {codinsight-0.2.0 → codinsight-0.4.0}/src/code_insight/trend_analysis/__init__.py +0 -0
  25. {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/dependency_links.txt +0 -0
  26. {codinsight-0.2.0 → codinsight-0.4.0}/src/codinsight.egg-info/top_level.txt +0 -0
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codinsight
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Add your description here
5
- Requires-Python: >=3.12
5
+ Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
7
+ Requires-Dist: codetovec
7
8
  Requires-Dist: matplotlib>=3.10.5
8
9
  Requires-Dist: pandas>=2.3.1
9
10
  Requires-Dist: pycodestyle>=2.14.0
@@ -4,11 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codinsight"
7
- version = "0.2.0"
7
+ version = "0.4.0"
8
8
  description = "Add your description here"
9
9
  readme = "README.md"
10
- requires-python = ">=3.12"
10
+ requires-python = ">=3.13"
11
11
  dependencies = [
12
+ "codetovec",
12
13
  "matplotlib>=3.10.5",
13
14
  "pandas>=2.3.1",
14
15
  "pycodestyle>=2.14.0",
@@ -35,6 +36,22 @@ addopts = "--exclude=tests"
35
36
  packages = ["src/code_insight"]
36
37
  include = ["src/code_insight/py.typed"]
37
38
 
39
+ [tool.ruff]
40
+ line-length = 88
41
+ include = ["codinsight/**/*.py"]
42
+
43
+ [tool.ruff.lint]
44
+ select = ["F", "E", "I", "B"]
45
+
46
+ [tool.ruff.lint.isort]
47
+ # black profileと同等の設定
48
+ split-on-trailing-comma = true
49
+ combine-as-imports = true
50
+
51
+ [tool.ruff.format]
52
+ quote-style = "double"
53
+ indent-style = "space"
54
+
38
55
  [tool.pytest.ini_options]
39
56
  testpaths = ["tests"]
40
57
  pythonpath = ["src"]
@@ -286,8 +286,7 @@ class Redundancy(AbstractAnalysis[RedundancyAnalysisResult, RedundancyAnalysisCo
286
286
  if isinstance(node_value, bytes):
287
287
  node_value = node_value.decode()
288
288
  structure_elements.append(
289
- f"return_const_{type(node.value.value).__name__}_"
290
- f"{node_value}"
289
+ f"return_const_{type(node.value.value).__name__}_{node_value}"
291
290
  )
292
291
  elif isinstance(node.value, ast.BinOp):
293
292
  structure_elements.append(
@@ -0,0 +1,327 @@
1
+ import ast
2
+ import re
3
+ from typing import List, Set
4
+
5
+ from code_insight.code_analysis.abstract import (
6
+ AbstractAnalysis,
7
+ BaseAnalysisConfig,
8
+ BaseAnalysisResult,
9
+ )
10
+
11
+
12
+ class SecurityAnalysisConfig(BaseAnalysisConfig):
13
+ """
14
+ セキュリティ解析設定
15
+
16
+ Attributes
17
+ ----------
18
+ check_hardcoded_secrets : bool
19
+ ハードコードされた秘密情報をチェックするか, by default True
20
+ check_dangerous_functions : bool
21
+ 危険な関数の使用をチェックするか, by default True
22
+ check_sql_injection : bool
23
+ SQLインジェクション脆弱性をチェックするか, by default True
24
+ secret_patterns : List[str]
25
+ 秘密情報検出パターン, by default 標準パターン
26
+ """
27
+
28
+ check_hardcoded_secrets: bool = True
29
+ check_dangerous_functions: bool = True
30
+ check_sql_injection: bool = True
31
+ secret_patterns: List[str] = [
32
+ r"password\s*=\s*['\"][^'\"]{3,}['\"]",
33
+ r"api_key\s*=\s*['\"][^'\"]{10,}['\"]",
34
+ r"secret\s*=\s*['\"][^'\"]{8,}['\"]",
35
+ r"token\s*=\s*['\"][^'\"]{10,}['\"]",
36
+ r"key\s*=\s*['\"][^'\"]{8,}['\"]",
37
+ ]
38
+
39
+
40
+ class SecurityAnalysisResult(BaseAnalysisResult):
41
+ """
42
+ 解析結果(セキュリティ)
43
+
44
+ Attributes
45
+ ----------
46
+ hardcoded_secrets_count : int
47
+ ハードコードされた秘密情報の数
48
+ dangerous_function_count : int
49
+ 危険な関数の使用数
50
+ sql_injection_risk_count : int
51
+ SQLインジェクション脆弱性の可能性がある箇所の数
52
+ input_validation_missing_count : int
53
+ 入力検証不備の数
54
+ security_score : float
55
+ セキュリティスコア(0.0-1.0、高いほど安全)
56
+ """
57
+
58
+ hardcoded_secrets_count: int
59
+ dangerous_function_count: int
60
+ sql_injection_risk_count: int
61
+ input_validation_missing_count: int
62
+ security_score: float
63
+
64
+
65
+ class Security(AbstractAnalysis[SecurityAnalysisResult, SecurityAnalysisConfig]):
66
+ """
67
+ 解析クラス(セキュリティ)
68
+
69
+ Notes
70
+ -----
71
+ コードのセキュリティ脆弱性を多角的に解析するクラス
72
+ """
73
+
74
+ def __init__(self, config: SecurityAnalysisConfig | None = None) -> None:
75
+ """
76
+ コンストラクタ
77
+
78
+ Parameters
79
+ ----------
80
+ config : SecurityAnalysisConfig | None, optional
81
+ セキュリティ解析設定, by default None
82
+ """
83
+ super().__init__(config)
84
+
85
+ def get_default_config(self) -> SecurityAnalysisConfig:
86
+ """
87
+ デフォルト設定を取得
88
+
89
+ Returns
90
+ -------
91
+ SecurityAnalysisConfig
92
+ デフォルトのセキュリティ解析設定
93
+ """
94
+ return SecurityAnalysisConfig()
95
+
96
+ def analyze(self, source_code: str) -> SecurityAnalysisResult:
97
+ """
98
+ コード解析
99
+
100
+ Parameters
101
+ ----------
102
+ source_code : str
103
+ 解析対象のソースコード
104
+
105
+ Returns
106
+ -------
107
+ SecurityAnalysisResult
108
+ セキュリティ解析結果
109
+ """
110
+ if not self.config.enabled:
111
+ return SecurityAnalysisResult(
112
+ hardcoded_secrets_count=0,
113
+ dangerous_function_count=0,
114
+ sql_injection_risk_count=0,
115
+ input_validation_missing_count=0,
116
+ security_score=1.0,
117
+ )
118
+
119
+ tree = self.parse_source_code(source_code)
120
+
121
+ hardcoded_secrets = self.get_hardcoded_secrets_count(source_code, tree)
122
+ dangerous_functions = self.get_dangerous_function_count(source_code, tree)
123
+ sql_injection_risks = self.get_sql_injection_risk_count(source_code, tree)
124
+ input_validation_missing = self.get_input_validation_missing_count(source_code, tree)
125
+
126
+ security_score = self.calculate_security_score(
127
+ hardcoded_secrets, dangerous_functions, sql_injection_risks, input_validation_missing
128
+ )
129
+
130
+ return SecurityAnalysisResult(
131
+ hardcoded_secrets_count=hardcoded_secrets,
132
+ dangerous_function_count=dangerous_functions,
133
+ sql_injection_risk_count=sql_injection_risks,
134
+ input_validation_missing_count=input_validation_missing,
135
+ security_score=security_score,
136
+ )
137
+
138
+ def parse_source_code(self, source_code: str) -> ast.AST:
139
+ """
140
+ ソースコードを解析
141
+
142
+ Parameters
143
+ ----------
144
+ source_code : str
145
+ 解析対象のソースコード
146
+
147
+ Returns
148
+ -------
149
+ ast.AST
150
+ 解析済みのAST
151
+ """
152
+ return ast.parse(source_code)
153
+
154
+ def get_hardcoded_secrets_count(
155
+ self, source_code: str, tree: ast.AST | None = None
156
+ ) -> int:
157
+ """
158
+ ハードコードされた秘密情報の数を取得
159
+
160
+ Parameters
161
+ ----------
162
+ source_code : str
163
+ 解析対象のソースコード
164
+ tree : ast.AST | None, optional
165
+ 解析済みのAST, by default None
166
+
167
+ Returns
168
+ -------
169
+ int
170
+ ハードコードされた秘密情報の数
171
+ """
172
+ if not self.config.check_hardcoded_secrets:
173
+ return 0
174
+
175
+ count = 0
176
+ source_lower = source_code.lower()
177
+
178
+ for pattern in self.config.secret_patterns:
179
+ matches = re.findall(pattern, source_lower, re.IGNORECASE)
180
+ count += len(matches)
181
+
182
+ return count
183
+
184
+ def get_dangerous_function_count(
185
+ self, source_code: str, tree: ast.AST | None = None
186
+ ) -> int:
187
+ """
188
+ 危険な関数の使用数を取得
189
+
190
+ Parameters
191
+ ----------
192
+ source_code : str
193
+ 解析対象のソースコード
194
+ tree : ast.AST | None, optional
195
+ 解析済みのAST, by default None
196
+
197
+ Returns
198
+ -------
199
+ int
200
+ 危険な関数の使用数
201
+ """
202
+ if not self.config.check_dangerous_functions:
203
+ return 0
204
+
205
+ tree = tree or self.parse_source_code(source_code)
206
+ dangerous_functions = {"eval", "exec", "compile", "__import__"}
207
+ count = 0
208
+
209
+ for node in ast.walk(tree):
210
+ if isinstance(node, ast.Call):
211
+ if isinstance(node.func, ast.Name) and node.func.id in dangerous_functions:
212
+ count += 1
213
+
214
+ return count
215
+
216
+ def get_sql_injection_risk_count(
217
+ self, source_code: str, tree: ast.AST | None = None
218
+ ) -> int:
219
+ """
220
+ SQLインジェクション脆弱性の可能性がある箇所の数を取得
221
+
222
+ Parameters
223
+ ----------
224
+ source_code : str
225
+ 解析対象のソースコード
226
+ tree : ast.AST | None, optional
227
+ 解析済みのAST, by default None
228
+
229
+ Returns
230
+ -------
231
+ int
232
+ SQLインジェクション脆弱性の可能性がある箇所の数
233
+ """
234
+ if not self.config.check_sql_injection:
235
+ return 0
236
+
237
+ tree = tree or self.parse_source_code(source_code)
238
+ count = 0
239
+
240
+ sql_patterns = [
241
+ r"select\s+.*\s+from\s+.*\+",
242
+ r"insert\s+into\s+.*\+",
243
+ r"update\s+.*\s+set\s+.*\+",
244
+ r"delete\s+from\s+.*\+",
245
+ ]
246
+
247
+ for node in ast.walk(tree):
248
+ if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add):
249
+ if isinstance(node.left, ast.Constant) and isinstance(node.left.value, str):
250
+ sql_text = node.left.value.lower()
251
+ for pattern in sql_patterns:
252
+ if re.search(pattern, sql_text):
253
+ count += 1
254
+ break
255
+
256
+ return count
257
+
258
+ def get_input_validation_missing_count(
259
+ self, source_code: str, tree: ast.AST | None = None
260
+ ) -> int:
261
+ """
262
+ 入力検証不備の数を取得
263
+
264
+ Parameters
265
+ ----------
266
+ source_code : str
267
+ 解析対象のソースコード
268
+ tree : ast.AST | None, optional
269
+ 解析済みのAST, by default None
270
+
271
+ Returns
272
+ -------
273
+ int
274
+ 入力検証不備の数
275
+ """
276
+ tree = tree or self.parse_source_code(source_code)
277
+ count = 0
278
+ input_functions = {"input", "raw_input"}
279
+
280
+ for node in ast.walk(tree):
281
+ if isinstance(node, ast.Call):
282
+ if isinstance(node.func, ast.Name) and node.func.id in input_functions:
283
+ parent_found = False
284
+ for parent in ast.walk(tree):
285
+ if isinstance(parent, ast.If):
286
+ for child in ast.walk(parent):
287
+ if child is node:
288
+ parent_found = True
289
+ break
290
+ if not parent_found:
291
+ count += 1
292
+
293
+ return count
294
+
295
+ def calculate_security_score(
296
+ self,
297
+ hardcoded_secrets: int,
298
+ dangerous_functions: int,
299
+ sql_injection_risks: int,
300
+ input_validation_missing: int,
301
+ ) -> float:
302
+ """
303
+ セキュリティスコアを計算
304
+
305
+ Parameters
306
+ ----------
307
+ hardcoded_secrets : int
308
+ ハードコードされた秘密情報の数
309
+ dangerous_functions : int
310
+ 危険な関数の使用数
311
+ sql_injection_risks : int
312
+ SQLインジェクション脆弱性の可能性がある箇所の数
313
+ input_validation_missing : int
314
+ 入力検証不備の数
315
+
316
+ Returns
317
+ -------
318
+ float
319
+ セキュリティスコア(0.0-1.0、高いほど安全)
320
+ """
321
+ total_issues = hardcoded_secrets + dangerous_functions + sql_injection_risks + input_validation_missing
322
+
323
+ if total_issues == 0:
324
+ return 1.0
325
+
326
+ penalty = min(total_issues * 0.1, 1.0)
327
+ return max(1.0 - penalty, 0.0)
@@ -0,0 +1,80 @@
1
+
2
+ from code_insight.code_analysis.abstract import (
3
+ AbstractAnalysis,
4
+ BaseAnalysisConfig,
5
+ BaseAnalysisResult,
6
+ )
7
+
8
+ from codetovec import CodeToVec
9
+
10
+
11
+ class VectorAnalysisConfig(BaseAnalysisConfig):
12
+ """
13
+ ベクトル解析の設定
14
+
15
+ Attributes
16
+ ----------
17
+ remove_comments : bool
18
+ コメントを削除するかどうか, by default False
19
+ remove_docstrings : bool
20
+ Docstringを削除するかどうか, by default False
21
+ remove_blank_lines : bool
22
+ 空行を削除するかどうか, by default False
23
+ """
24
+
25
+ remove_comments: bool = False
26
+ remove_docstrings: bool = False
27
+ remove_blank_lines: bool = False
28
+
29
+
30
+ class VectorAnalysisResult(BaseAnalysisResult):
31
+ """
32
+ ベクトル解析結果
33
+
34
+ Attributes
35
+ ----------
36
+ vector : list[float]
37
+ 抽出されたベクトル表現
38
+ """
39
+
40
+ vector: list[float]
41
+
42
+
43
+ class Vector(AbstractAnalysis[VectorAnalysisResult, VectorAnalysisConfig]):
44
+ """
45
+ ベクトル解析クラス
46
+ """
47
+
48
+ def get_default_config(self) -> VectorAnalysisConfig:
49
+ """
50
+ デフォルト設定を取得
51
+
52
+ Returns
53
+ -------
54
+ VectorAnalysisConfig
55
+ デフォルトのベクトル解析設定
56
+ """
57
+ return VectorAnalysisConfig()
58
+
59
+ def analyze(self, source_code: str) -> VectorAnalysisResult:
60
+ """
61
+ ソースコードからベクトルを抽出する
62
+
63
+ Parameters
64
+ ----------
65
+ source_code : str
66
+ 解析対象のソースコード
67
+
68
+ Returns
69
+ -------
70
+ VectorAnalysisResult
71
+ 抽出されたベクトル表現
72
+ """
73
+ codetovec = CodeToVec()
74
+ vector = codetovec.execute(
75
+ text=source_code,
76
+ remove_comments=self.config.remove_comments,
77
+ remove_docstrings=self.config.remove_docstrings,
78
+ remove_blank_lines=self.config.remove_blank_lines,
79
+ )
80
+ return VectorAnalysisResult(vector=vector)
@@ -1,5 +1,5 @@
1
1
  from enum import StrEnum, auto
2
- from typing import Any, Type
2
+ from typing import Any
3
3
 
4
4
  from pydantic import BaseModel
5
5
 
@@ -16,8 +16,10 @@ from code_insight.code_analysis.readability import (
16
16
  ReadabilityAnalysisConfig,
17
17
  )
18
18
  from code_insight.code_analysis.redundancy import Redundancy, RedundancyAnalysisConfig
19
+ from code_insight.code_analysis.security import Security, SecurityAnalysisConfig
19
20
  from code_insight.code_analysis.struct import Struct, StructAnalysisConfig
20
- from code_insight.code_analysis.style import Style, StyleAnalysisConfig
21
+ from code_insight.code_analysis.style import Style, StyleAnalysisConfig
22
+ from code_insight.code_analysis.vector import Vector, VectorAnalysisConfig
21
23
 
22
24
 
23
25
  class AnalysisConfigs(BaseModel):
@@ -40,6 +42,8 @@ class AnalysisConfigs(BaseModel):
40
42
  複雑度解析設定, by default None
41
43
  quality : QualityAnalysisConfig | None
42
44
  品質解析設定, by default None
45
+ security : SecurityAnalysisConfig | None
46
+ セキュリティ解析設定, by default None
43
47
  """
44
48
 
45
49
  style: StyleAnalysisConfig | None = None
@@ -49,6 +53,8 @@ class AnalysisConfigs(BaseModel):
49
53
  algorithm: AlgorithmAnalysisConfig | None = None
50
54
  complexity: ComplexityAnalysisConfig | None = None
51
55
  quality: QualityAnalysisConfig | None = None
56
+ security: SecurityAnalysisConfig | None = None
57
+ vector: VectorAnalysisConfig | None = None
52
58
 
53
59
 
54
60
  class CodeAnalysisType(StrEnum):
@@ -71,6 +77,10 @@ class CodeAnalysisType(StrEnum):
71
77
  複雑度解析
72
78
  QUALITY : str
73
79
  品質解析
80
+ SECURITY : str
81
+ セキュリティ解析
82
+ VECTOR : str
83
+ ベクトル解析
74
84
  """
75
85
 
76
86
  STYLE = auto()
@@ -80,6 +90,8 @@ class CodeAnalysisType(StrEnum):
80
90
  ALGORITHM = auto()
81
91
  COMPLEXITY = auto()
82
92
  QUALITY = auto()
93
+ SECURITY = auto()
94
+ VECTOR = auto()
83
95
 
84
96
  @staticmethod
85
97
  def get_code_analysis_class(
@@ -119,6 +131,10 @@ class CodeAnalysisType(StrEnum):
119
131
  return Complexity(config) # type: ignore
120
132
  elif type == CodeAnalysisType.QUALITY:
121
133
  return Quality(config) # type: ignore
134
+ elif type == CodeAnalysisType.SECURITY:
135
+ return Security(config) # type: ignore
136
+ elif type == CodeAnalysisType.VECTOR:
137
+ return Vector(config) # type: ignore
122
138
  else:
123
139
  raise ValueError(f"Invalid code analysis type: {type}")
124
140
 
@@ -156,7 +172,7 @@ class CodeAnalysis:
156
172
 
157
173
  def analyze(
158
174
  self, types: list[CodeAnalysisType]
159
- ) -> dict[CodeAnalysisType, Type[BaseAnalysisResult]]:
175
+ ) -> dict[CodeAnalysisType, BaseAnalysisResult]:
160
176
  """
161
177
  コード解析
162
178
 
@@ -167,10 +183,10 @@ class CodeAnalysis:
167
183
 
168
184
  Returns
169
185
  -------
170
- dict[CodeAnalysisType, Type[BaseAnalysisResult]]
186
+ dict[CodeAnalysisType, BaseAnalysisResult]
171
187
  解析結果の辞書
172
188
  """
173
- result: dict[CodeAnalysisType, Type[BaseAnalysisResult]] = {}
189
+ result: dict[CodeAnalysisType, BaseAnalysisResult] = {}
174
190
  for type in types:
175
191
  config = self._get_config_for_type(type)
176
192
  result[type] = CodeAnalysisType.get_code_analysis_class(
@@ -205,5 +221,7 @@ class CodeAnalysis:
205
221
  CodeAnalysisType.ALGORITHM: self.configs.algorithm,
206
222
  CodeAnalysisType.COMPLEXITY: self.configs.complexity,
207
223
  CodeAnalysisType.QUALITY: self.configs.quality,
224
+ CodeAnalysisType.SECURITY: self.configs.security,
225
+ CodeAnalysisType.VECTOR: self.configs.vector,
208
226
  }
209
227
  return config_map.get(analysis_type)
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  from pathlib import Path
3
- from typing import Any, Iterable, cast
3
+ from typing import Iterable
4
4
 
5
5
  from pydantic import BaseModel
6
6
 
@@ -24,7 +24,7 @@ class FileAnalysisResult(BaseModel):
24
24
  """
25
25
 
26
26
  path: str
27
- results: dict[str, dict[str, Any]]
27
+ results: dict[CodeAnalysisType, BaseAnalysisResult]
28
28
 
29
29
 
30
30
  class AggregateStats(BaseModel):
@@ -46,7 +46,7 @@ class AggregateStats(BaseModel):
46
46
  total_files: int
47
47
  analyzed_files: int
48
48
  errors: list[str]
49
- by_type_avg: dict[str, dict[str, float]]
49
+ by_type_avg: dict[CodeAnalysisType, dict[str, float]]
50
50
 
51
51
 
52
52
  class MultiAnalysisResult(BaseModel):
@@ -169,16 +169,15 @@ def analyze_file(
169
169
  source_code = path.read_text(encoding="utf-8", errors="ignore")
170
170
  analysis = CodeAnalysis(source_code=source_code, configs=configs)
171
171
  result_map = analysis.analyze(types)
172
- as_dict: dict[str, dict[str, Any]] = {}
172
+ as_dict: dict[CodeAnalysisType, BaseAnalysisResult] = {}
173
173
  for t, model in result_map.items():
174
- m = cast(BaseAnalysisResult, model)
175
- as_dict[t.name] = m.model_dump()
174
+ as_dict[t] = model
176
175
  return FileAnalysisResult(path=str(path), results=as_dict)
177
176
 
178
177
 
179
178
  def _aggregate_numeric_means(
180
179
  files: list[FileAnalysisResult],
181
- ) -> dict[str, dict[str, float]]:
180
+ ) -> dict[CodeAnalysisType, dict[str, float]]:
182
181
  """
183
182
  数値メトリクスの平均値を集約
184
183
 
@@ -192,17 +191,17 @@ def _aggregate_numeric_means(
192
191
  dict[str, dict[str, float]]
193
192
  解析タイプ別の平均値辞書
194
193
  """
195
- by_type: dict[str, dict[str, list[float]]] = {}
194
+ by_type: dict[CodeAnalysisType, dict[str, list[float]]] = {}
196
195
 
197
196
  for fa in files:
198
197
  for tname, metrics in fa.results.items():
199
198
  if tname not in by_type:
200
199
  by_type[tname] = {}
201
- for key, val in metrics.items():
200
+ for key, val in metrics.model_dump().items():
202
201
  if isinstance(val, (int, float)):
203
202
  by_type[tname].setdefault(key, []).append(float(val))
204
203
 
205
- avg: dict[str, dict[str, float]] = {}
204
+ avg: dict[CodeAnalysisType, dict[str, float]] = {}
206
205
  for tname, metrics_map in by_type.items():
207
206
  avg[tname] = {}
208
207
  for key, values in metrics_map.items():
@@ -167,7 +167,7 @@ class TrendAnalysis:
167
167
  X_vis[i, 0] + 0.02,
168
168
  X_vis[i, 1] + 0.02,
169
169
  self.code_labels[i],
170
- fontsize=8,
170
+ fontsize=7,
171
171
  )
172
172
 
173
173
  plt.title("Clustering Result")
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codinsight
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Add your description here
5
- Requires-Python: >=3.12
5
+ Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
7
+ Requires-Dist: codetovec
7
8
  Requires-Dist: matplotlib>=3.10.5
8
9
  Requires-Dist: pandas>=2.3.1
9
10
  Requires-Dist: pycodestyle>=2.14.0
@@ -11,8 +11,10 @@ src/code_insight/code_analysis/complexity.py
11
11
  src/code_insight/code_analysis/quality.py
12
12
  src/code_insight/code_analysis/readability.py
13
13
  src/code_insight/code_analysis/redundancy.py
14
+ src/code_insight/code_analysis/security.py
14
15
  src/code_insight/code_analysis/struct.py
15
16
  src/code_insight/code_analysis/style.py
17
+ src/code_insight/code_analysis/vector.py
16
18
  src/code_insight/trend_analysis/__init__.py
17
19
  src/code_insight/trend_analysis/trend_analysis.py
18
20
  src/codinsight.egg-info/PKG-INFO
@@ -1,3 +1,4 @@
1
+ codetovec
1
2
  matplotlib>=3.10.5
2
3
  pandas>=2.3.1
3
4
  pycodestyle>=2.14.0
File without changes
File without changes