codeanalyzer-python 0.1.11__py3-none-any.whl → 0.1.12__py3-none-any.whl

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.
codeanalyzer/__main__.py CHANGED
@@ -7,107 +7,62 @@ from codeanalyzer.core import Codeanalyzer
7
7
  from codeanalyzer.utils import _set_log_level, logger
8
8
  from codeanalyzer.config import OutputFormat
9
9
  from codeanalyzer.schema import model_dump_json
10
+ from codeanalyzer.options import AnalysisOptions
10
11
 
11
12
  def main(
12
- input: Annotated[
13
- Path, typer.Option("-i", "--input", help="Path to the project root directory.")
14
- ],
15
- output: Annotated[
16
- Optional[Path],
17
- typer.Option("-o", "--output", help="Output directory for artifacts."),
18
- ] = None,
19
- format: Annotated[
20
- OutputFormat,
21
- typer.Option(
22
- "-f",
23
- "--format",
24
- help="Output format: json or msgpack.",
25
- case_sensitive=False,
26
- ),
27
- ] = OutputFormat.JSON,
28
- analysis_level: Annotated[
29
- int,
30
- typer.Option("-a", "--analysis-level", help="1: symbol table, 2: call graph."),
31
- ] = 1,
32
- using_codeql: Annotated[
33
- bool, typer.Option("--codeql/--no-codeql", help="Enable CodeQL-based analysis.")
34
- ] = False,
35
- using_ray: Annotated[
36
- bool,
37
- typer.Option(
38
- "--ray/--no-ray", help="Enable Ray for distributed analysis."
39
- ),
40
- ] = False,
41
- rebuild_analysis: Annotated[
42
- bool,
43
- typer.Option(
44
- "--eager/--lazy",
45
- help="Enable eager or lazy analysis. Defaults to lazy.",
46
- ),
47
- ] = False,
48
- skip_tests: Annotated[
49
- bool,
50
- typer.Option(
51
- "--skip-tests/--include-tests",
52
- help="Skip test files in analysis.",
53
- ),
54
- ] = True,
55
- file_name: Annotated[
56
- Optional[Path],
57
- typer.Option(
58
- "--file-name",
59
- help="Analyze only the specified file (relative to input directory).",
60
- ),
61
- ] = None,
62
- cache_dir: Annotated[
63
- Optional[Path],
64
- typer.Option(
65
- "-c",
66
- "--cache-dir",
67
- help="Directory to store analysis cache. Defaults to '.codeanalyzer' in the input directory.",
68
- ),
69
- ] = None,
70
- clear_cache: Annotated[
71
- bool,
72
- typer.Option("--clear-cache/--keep-cache", help="Clear cache after analysis. By default, cache is retained."),
73
- ] = False,
74
- verbosity: Annotated[
75
- int, typer.Option("-v", count=True, help="Increase verbosity: -v, -vv, -vvv")
76
- ] = 0,
13
+ input: Annotated[Path, typer.Option("-i", "--input", help="Path to the project root directory.")],
14
+ output: Optional[Path] = typer.Option(None, "-o", "--output"),
15
+ format: OutputFormat = typer.Option(OutputFormat.JSON, "-f", "--format"),
16
+ analysis_level: int = typer.Option(1, "-a", "--analysis-level"),
17
+ using_codeql: bool = typer.Option(False, "--codeql/--no-codeql"),
18
+ using_ray: bool = typer.Option(False, "--ray/--no-ray"),
19
+ rebuild_analysis: bool = typer.Option(False, "--eager/--lazy"),
20
+ skip_tests: bool = typer.Option(True, "--skip-tests/--include-tests"),
21
+ file_name: Optional[Path] = typer.Option(None, "--file-name"),
22
+ cache_dir: Optional[Path] = typer.Option(None, "-c", "--cache-dir"),
23
+ clear_cache: bool = typer.Option(False, "--clear-cache/--keep-cache"),
24
+ verbosity: int = typer.Option(0, "-v", count=True),
77
25
  ):
78
- """Static Analysis on Python source code using Jedi, Astroid, and Treesitter."""
79
- _set_log_level(verbosity)
26
+ options = AnalysisOptions(
27
+ input=input,
28
+ output=output,
29
+ format=format,
30
+ analysis_level=analysis_level,
31
+ using_codeql=using_codeql,
32
+ using_ray=using_ray,
33
+ rebuild_analysis=rebuild_analysis,
34
+ skip_tests=skip_tests,
35
+ file_name=file_name,
36
+ cache_dir=cache_dir,
37
+ clear_cache=clear_cache,
38
+ verbosity=verbosity,
39
+ )
80
40
 
81
- if not input.exists():
82
- logger.error(f"Input path '{input}' does not exist.")
41
+ _set_log_level(options.verbosity)
42
+ if not options.input.exists():
43
+ logger.error(f"Input path '{options.input}' does not exist.")
83
44
  raise typer.Exit(code=1)
84
45
 
85
- # Validate file_name if provided
86
- if file_name is not None:
87
- full_file_path = input / file_name
46
+ if options.file_name is not None:
47
+ full_file_path = options.input / options.file_name
88
48
  if not full_file_path.exists():
89
- logger.error(f"Specified file '{file_name}' does not exist in '{input}'.")
49
+ logger.error(f"Specified file '{options.file_name}' does not exist in '{options.input}'.")
90
50
  raise typer.Exit(code=1)
91
51
  if not full_file_path.is_file():
92
- logger.error(f"Specified path '{file_name}' is not a file.")
52
+ logger.error(f"Specified path '{options.file_name}' is not a file.")
93
53
  raise typer.Exit(code=1)
94
- if not str(file_name).endswith('.py'):
95
- logger.error(f"Specified file '{file_name}' is not a Python file (.py).")
54
+ if not str(options.file_name).endswith('.py'):
55
+ logger.error(f"Specified file '{options.file_name}' is not a Python file (.py).")
96
56
  raise typer.Exit(code=1)
97
57
 
98
- with Codeanalyzer(
99
- input, analysis_level, skip_tests, using_codeql, rebuild_analysis, cache_dir, clear_cache, using_ray, file_name
100
- ) as analyzer:
58
+ with Codeanalyzer(options) as analyzer:
101
59
  artifacts = analyzer.analyze()
102
60
 
103
- # Handle output based on format
104
- if output is None:
105
- # Output to stdout (only for JSON)
61
+ if options.output is None:
106
62
  print(model_dump_json(artifacts, separators=(",", ":")))
107
63
  else:
108
- # Output to file
109
- output.mkdir(parents=True, exist_ok=True)
110
- _write_output(artifacts, output, format)
64
+ options.output.mkdir(parents=True, exist_ok=True)
65
+ _write_output(artifacts, options.output, options.format)
111
66
 
112
67
 
113
68
  def _write_output(artifacts, output_dir: Path, format: OutputFormat):
@@ -130,7 +85,6 @@ def _write_output(artifacts, output_dir: Path, format: OutputFormat):
130
85
  f"Compression ratio: {artifacts.get_compression_ratio():.1%} of JSON size"
131
86
  )
132
87
 
133
-
134
88
  app = typer.Typer(
135
89
  callback=main,
136
90
  name="codeanalyzer",
codeanalyzer/core.py CHANGED
@@ -14,6 +14,7 @@ from codeanalyzer.semantic_analysis.codeql.codeql_exceptions import CodeQLExcept
14
14
  from codeanalyzer.syntactic_analysis.exceptions import SymbolTableBuilderRayError
15
15
  from codeanalyzer.syntactic_analysis.symbol_table_builder import SymbolTableBuilder
16
16
  from codeanalyzer.utils import ProgressBar
17
+ from codeanalyzer.options import AnalysisOptions
17
18
 
18
19
  @ray.remote
19
20
  def _process_file_with_ray(py_file: Union[Path, str], project_dir: Union[Path, str], virtualenv: Union[Path, str, None]) -> Dict[str, PyModule]:
@@ -43,40 +44,25 @@ class Codeanalyzer:
43
44
  """Core functionality for CodeQL analysis.
44
45
 
45
46
  Args:
46
- project_dir (Union[str, Path]): The root directory of the project to analyze.
47
- virtualenv (Optional[Path]): Path to the virtual environment directory.
48
- using_codeql (bool): Whether to use CodeQL for analysis.
49
- rebuild_analysis (bool): Whether to force rebuild the database.
50
- clear_cache (bool): Whether to delete the cached directory after analysis.
51
- analysis_depth (int): Depth of analysis (reserved for future use).
47
+ options (AnalysisOptions): Analysis configuration options containing all necessary parameters.
52
48
  """
53
49
 
54
- def __init__(
55
- self,
56
- project_dir: Union[str, Path],
57
- analysis_depth: int,
58
- skip_tests: bool,
59
- using_codeql: bool,
60
- rebuild_analysis: bool,
61
- cache_dir: Optional[Path],
62
- clear_cache: bool,
63
- using_ray: bool,
64
- file_name: Optional[Path] = None,
65
- ) -> None:
66
- self.analysis_depth = analysis_depth
67
- self.project_dir = Path(project_dir).resolve()
68
- self.skip_tests = skip_tests
69
- self.using_codeql = using_codeql
70
- self.rebuild_analysis = rebuild_analysis
50
+ def __init__(self, options: AnalysisOptions) -> None:
51
+ self.options = options
52
+ self.analysis_depth = options.analysis_level
53
+ self.project_dir = Path(options.input).resolve()
54
+ self.skip_tests = options.skip_tests
55
+ self.using_codeql = options.using_codeql
56
+ self.rebuild_analysis = options.rebuild_analysis
71
57
  self.cache_dir = (
72
- cache_dir.resolve() if cache_dir is not None else self.project_dir
58
+ options.cache_dir.resolve() if options.cache_dir is not None else self.project_dir
73
59
  ) / ".codeanalyzer"
74
- self.clear_cache = clear_cache
60
+ self.clear_cache = options.clear_cache
75
61
  self.db_path: Optional[Path] = None
76
62
  self.codeql_bin: Optional[Path] = None
77
63
  self.virtualenv: Optional[Path] = None
78
- self.using_ray: bool = using_ray
79
- self.file_name: Optional[Path] = file_name
64
+ self.using_ray: bool = options.using_ray
65
+ self.file_name: Optional[Path] = options.file_name
80
66
 
81
67
  @staticmethod
82
68
  def _cmd_exec_helper(
@@ -0,0 +1,3 @@
1
+ from .options import AnalysisOptions
2
+
3
+ __all__ = ["AnalysisOptions"]
@@ -0,0 +1,25 @@
1
+ from dataclasses import dataclass
2
+ from pathlib import Path
3
+ from typing import Optional
4
+ from enum import Enum
5
+
6
+
7
+ class OutputFormat(str, Enum):
8
+ JSON = "json"
9
+ MSGPACK = "msgpack"
10
+
11
+
12
+ @dataclass
13
+ class AnalysisOptions:
14
+ input: Path
15
+ output: Optional[Path] = None
16
+ format: OutputFormat = OutputFormat.JSON
17
+ analysis_level: int = 1
18
+ using_codeql: bool = False
19
+ using_ray: bool = False
20
+ rebuild_analysis: bool = False
21
+ skip_tests: bool = True
22
+ file_name: Optional[Path] = None
23
+ cache_dir: Optional[Path] = None
24
+ clear_cache: bool = False
25
+ verbosity: int = 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeanalyzer-python
3
- Version: 0.1.11
3
+ Version: 0.1.12
4
4
  Summary: Static Analysis on Python source code using Jedi, CodeQL and Treesitter.
5
5
  Author-email: Rahul Krishna <i.m.ralk@gmail.com>
6
6
  License-File: LICENSE
@@ -1,11 +1,13 @@
1
1
  codeanalyzer/__init__.py,sha256=BZ3Kuwl-F_F-8H8cepLnVJ4Ku4NNUjjqg0Y6ujPQSsI,108
2
- codeanalyzer/__main__.py,sha256=e-AMzR5uR1IsUKhsfk17_qPJkwORRqe9tBxCXwwlxBY,4922
3
- codeanalyzer/core.py,sha256=swFc-6ICs_rijj7P0bbJv3um2Gr-R8_roGK0P3qo1xc,24979
2
+ codeanalyzer/__main__.py,sha256=DLyvuQXnzwx5wIb2xvLg5qzmzOyvLz4Uxiz_Y8bbfHs,3938
3
+ codeanalyzer/core.py,sha256=P43skGiKUnQtkSQGHpPDgZrftAKP2HHh52g1N1Ww1S0,24503
4
4
  codeanalyzer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  codeanalyzer/config/__init__.py,sha256=9XBxAn1oWGRuhg3bEBUuVGs3hFNXEAKrr-Ce7tq9a2k,61
6
6
  codeanalyzer/config/config.py,sha256=ZiKzc5uEUCIvih58-6BDtLLI1hPij41wGQjBcj9KNQM,188
7
7
  codeanalyzer/jedi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  codeanalyzer/jedi/jedi.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ codeanalyzer/options/__init__.py,sha256=4kkwBiOrGjalrkfFF8EJgfgOtaPtD7HcCYfZgcOgelU,67
10
+ codeanalyzer/options/options.py,sha256=PNdLUCF7kHfscSeeqbV6ZEAu37T1gFoeV9SqJQed7Fw,592
9
11
  codeanalyzer/schema/__init__.py,sha256=HB7y4y-49dkEo-H9GREam1_9Cr1N-GF6MYwx9yoU878,1978
10
12
  codeanalyzer/schema/py_schema.py,sha256=04K19tDtmg2tPXjwu_8BcmVpenk1ibVwNv6bHWZHOLY,10851
11
13
  codeanalyzer/semantic_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -21,9 +23,9 @@ codeanalyzer/syntactic_analysis/symbol_table_builder.py,sha256=0FE_ZdlyP77P1B70Q
21
23
  codeanalyzer/utils/__init__.py,sha256=hC6VWdR5rerSqBxzu9KQHTASWqwrrYJv-CMDwrTlzkc,137
22
24
  codeanalyzer/utils/logging.py,sha256=0vTkGSl5EZN8yhhWa_5Mrn1n_twRCSW53rNwjzQ9RbI,601
23
25
  codeanalyzer/utils/progress_bar.py,sha256=ZHJzGiCo5q4dyXq4CtsrJeq9Ip7sD84T3yZjNX7TBys,2443
24
- codeanalyzer_python-0.1.11.dist-info/METADATA,sha256=yqQiVgSsG1k262sigcRo51BScmuVQywLOtL9rEX5ATI,16283
25
- codeanalyzer_python-0.1.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- codeanalyzer_python-0.1.11.dist-info/entry_points.txt,sha256=eUrB7Jq5Oav6RblMX_RYfVLSw_h15NbzC3fNSnGsPuM,59
27
- codeanalyzer_python-0.1.11.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
28
- codeanalyzer_python-0.1.11.dist-info/licenses/NOTICE,sha256=YU0Z9NDWqKY-2jfFcbxeZ6fbnzz0oZeKmnUcO8a-bcQ,901
29
- codeanalyzer_python-0.1.11.dist-info/RECORD,,
26
+ codeanalyzer_python-0.1.12.dist-info/METADATA,sha256=6WbhPKR5AWghHHtngmhf2pMNa35-d-C-d_rIRd5AD7E,16283
27
+ codeanalyzer_python-0.1.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
28
+ codeanalyzer_python-0.1.12.dist-info/entry_points.txt,sha256=eUrB7Jq5Oav6RblMX_RYfVLSw_h15NbzC3fNSnGsPuM,59
29
+ codeanalyzer_python-0.1.12.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
30
+ codeanalyzer_python-0.1.12.dist-info/licenses/NOTICE,sha256=YU0Z9NDWqKY-2jfFcbxeZ6fbnzz0oZeKmnUcO8a-bcQ,901
31
+ codeanalyzer_python-0.1.12.dist-info/RECORD,,