codeanalyzer-python 0.1.10__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
@@ -6,115 +6,71 @@ import typer
6
6
  from codeanalyzer.core import Codeanalyzer
7
7
  from codeanalyzer.utils import _set_log_level, logger
8
8
  from codeanalyzer.config import OutputFormat
9
+ from codeanalyzer.schema import model_dump_json
10
+ from codeanalyzer.options import AnalysisOptions
9
11
 
10
12
  def main(
11
- input: Annotated[
12
- Path, typer.Option("-i", "--input", help="Path to the project root directory.")
13
- ],
14
- output: Annotated[
15
- Optional[Path],
16
- typer.Option("-o", "--output", help="Output directory for artifacts."),
17
- ] = None,
18
- format: Annotated[
19
- OutputFormat,
20
- typer.Option(
21
- "-f",
22
- "--format",
23
- help="Output format: json or msgpack.",
24
- case_sensitive=False,
25
- ),
26
- ] = OutputFormat.JSON,
27
- analysis_level: Annotated[
28
- int,
29
- typer.Option("-a", "--analysis-level", help="1: symbol table, 2: call graph."),
30
- ] = 1,
31
- using_codeql: Annotated[
32
- bool, typer.Option("--codeql/--no-codeql", help="Enable CodeQL-based analysis.")
33
- ] = False,
34
- using_ray: Annotated[
35
- bool,
36
- typer.Option(
37
- "--ray/--no-ray", help="Enable Ray for distributed analysis."
38
- ),
39
- ] = False,
40
- rebuild_analysis: Annotated[
41
- bool,
42
- typer.Option(
43
- "--eager/--lazy",
44
- help="Enable eager or lazy analysis. Defaults to lazy.",
45
- ),
46
- ] = False,
47
- skip_tests: Annotated[
48
- bool,
49
- typer.Option(
50
- "--skip-tests/--include-tests",
51
- help="Skip test files in analysis.",
52
- ),
53
- ] = True,
54
- file_name: Annotated[
55
- Optional[Path],
56
- typer.Option(
57
- "--file-name",
58
- help="Analyze only the specified file (relative to input directory).",
59
- ),
60
- ] = None,
61
- cache_dir: Annotated[
62
- Optional[Path],
63
- typer.Option(
64
- "-c",
65
- "--cache-dir",
66
- help="Directory to store analysis cache. Defaults to '.codeanalyzer' in the input directory.",
67
- ),
68
- ] = None,
69
- clear_cache: Annotated[
70
- bool,
71
- typer.Option("--clear-cache/--keep-cache", help="Clear cache after analysis. By default, cache is retained."),
72
- ] = False,
73
- verbosity: Annotated[
74
- int, typer.Option("-v", count=True, help="Increase verbosity: -v, -vv, -vvv")
75
- ] = 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),
76
25
  ):
77
- """Static Analysis on Python source code using Jedi, Astroid, and Treesitter."""
78
- _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
+ )
79
40
 
80
- if not input.exists():
81
- 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.")
82
44
  raise typer.Exit(code=1)
83
45
 
84
- # Validate file_name if provided
85
- if file_name is not None:
86
- full_file_path = input / file_name
46
+ if options.file_name is not None:
47
+ full_file_path = options.input / options.file_name
87
48
  if not full_file_path.exists():
88
- 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}'.")
89
50
  raise typer.Exit(code=1)
90
51
  if not full_file_path.is_file():
91
- logger.error(f"Specified path '{file_name}' is not a file.")
52
+ logger.error(f"Specified path '{options.file_name}' is not a file.")
92
53
  raise typer.Exit(code=1)
93
- if not str(file_name).endswith('.py'):
94
- 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).")
95
56
  raise typer.Exit(code=1)
96
57
 
97
- with Codeanalyzer(
98
- input, analysis_level, skip_tests, using_codeql, rebuild_analysis, cache_dir, clear_cache, using_ray, file_name
99
- ) as analyzer:
58
+ with Codeanalyzer(options) as analyzer:
100
59
  artifacts = analyzer.analyze()
101
60
 
102
- # Handle output based on format
103
- if output is None:
104
- # Output to stdout (only for JSON)
105
- print(artifacts.json(separators=(",", ":")))
61
+ if options.output is None:
62
+ print(model_dump_json(artifacts, separators=(",", ":")))
106
63
  else:
107
- # Output to file
108
- output.mkdir(parents=True, exist_ok=True)
109
- _write_output(artifacts, output, format)
64
+ options.output.mkdir(parents=True, exist_ok=True)
65
+ _write_output(artifacts, options.output, options.format)
110
66
 
111
67
 
112
68
  def _write_output(artifacts, output_dir: Path, format: OutputFormat):
113
69
  """Write artifacts to file in the specified format."""
114
70
  if format == OutputFormat.JSON:
115
71
  output_file = output_dir / "analysis.json"
116
- # Use Pydantic's json() with separators for compact output
117
- json_str = artifacts.json(indent=None)
72
+ # Use Pydantic's model_dump_json() for compact output
73
+ json_str = model_dump_json(artifacts, indent=None)
118
74
  with output_file.open("w") as f:
119
75
  f.write(json_str)
120
76
  logger.info(f"Analysis saved to {output_file}")
@@ -129,7 +85,6 @@ def _write_output(artifacts, output_dir: Path, format: OutputFormat):
129
85
  f"Compression ratio: {artifacts.get_compression_ratio():.1%} of JSON size"
130
86
  )
131
87
 
132
-
133
88
  app = typer.Typer(
134
89
  callback=main,
135
90
  name="codeanalyzer",
codeanalyzer/core.py CHANGED
@@ -8,12 +8,13 @@ from typing import Any, Dict, Optional, Union, List
8
8
 
9
9
  import ray
10
10
  from codeanalyzer.utils import logger
11
- from codeanalyzer.schema import PyApplication, PyModule
11
+ from codeanalyzer.schema import PyApplication, PyModule, model_dump_json, model_validate_json
12
12
  from codeanalyzer.semantic_analysis.codeql import CodeQLLoader
13
13
  from codeanalyzer.semantic_analysis.codeql.codeql_exceptions import CodeQLExceptions
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(
@@ -408,7 +394,7 @@ class Codeanalyzer:
408
394
  """
409
395
  with cache_file.open('r') as f:
410
396
  data = f.read()
411
- return PyApplication.parse_raw(data)
397
+ return model_validate_json(PyApplication, data)
412
398
 
413
399
  def _save_analysis_cache(self, app: PyApplication, cache_file: Path) -> None:
414
400
  """Save analysis to cache file.
@@ -421,8 +407,8 @@ class Codeanalyzer:
421
407
  cache_file.parent.mkdir(parents=True, exist_ok=True)
422
408
 
423
409
  with cache_file.open('w') as f:
424
- f.write(app.json(indent=2))
425
-
410
+ f.write(model_dump_json(app, indent=2))
411
+
426
412
  logger.info(f"Analysis cached to {cache_file}")
427
413
 
428
414
  def _file_unchanged(self, file_path: Path, cached_module: PyModule) -> bool:
@@ -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,3 +1,6 @@
1
+ from importlib.metadata import version, PackageNotFoundError
2
+ from packaging.version import parse as parse_version
3
+
1
4
  from .py_schema import (
2
5
  PyApplication,
3
6
  PyCallable,
@@ -22,12 +25,48 @@ __all__ = [
22
25
  "PyCallableParameter",
23
26
  ]
24
27
 
25
- # Resolve forward references
26
- PyCallable.update_forward_refs(PyClass=PyClass)
27
- PyClass.update_forward_refs(PyCallable=PyCallable)
28
- PyModule.update_forward_refs(PyCallable=PyCallable, PyClass=PyClass)
29
- PyApplication.update_forward_refs(
30
- PyCallable=PyCallable,
31
- PyClass=PyClass,
32
- PyModule=PyModule
33
- )
28
+ try:
29
+ pydantic_version = version("pydantic")
30
+ except PackageNotFoundError:
31
+ pydantic_version = "0.0.0" # fallback or raise if appropriate
32
+
33
+ PYDANTIC_V2 = parse_version(pydantic_version) >= parse_version("2.0.0")
34
+
35
+ if not PYDANTIC_V2:
36
+ # Safe to pass localns
37
+ PyCallable.update_forward_refs(PyClass=PyClass)
38
+ PyClass.update_forward_refs(PyCallable=PyCallable)
39
+ PyModule.update_forward_refs(PyCallable=PyCallable, PyClass=PyClass)
40
+ PyApplication.update_forward_refs(
41
+ PyCallable=PyCallable,
42
+ PyClass=PyClass,
43
+ PyModule=PyModule
44
+ )
45
+
46
+ # Compatibility helpers for Pydantic v1/v2
47
+ def model_dump_json(model, **kwargs):
48
+ """Compatibility helper for JSON serialization."""
49
+ if PYDANTIC_V2:
50
+ return model.model_dump_json(**kwargs)
51
+ else:
52
+ # Map Pydantic v2 parameters to v1 equivalents
53
+ v1_kwargs = {}
54
+ if 'indent' in kwargs:
55
+ v1_kwargs['indent'] = kwargs['indent']
56
+ if 'separators' in kwargs:
57
+ # In v1, separators is passed to dumps_kwargs
58
+ v1_kwargs['separators'] = kwargs['separators']
59
+ return model.json(**v1_kwargs)
60
+
61
+ def model_validate_json(model_class, json_data):
62
+ """Compatibility helper for JSON deserialization."""
63
+ if PYDANTIC_V2:
64
+ return model_class.model_validate_json(json_data)
65
+ else:
66
+ return model_class.parse_raw(json_data)
67
+
68
+ __all__.extend([
69
+ "PYDANTIC_V2",
70
+ "model_dump_json",
71
+ "model_validate_json"
72
+ ])
@@ -1,22 +1,34 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeanalyzer-python
3
- Version: 0.1.10
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
7
7
  License-File: NOTICE
8
8
  Requires-Python: >=3.9
9
- Requires-Dist: jedi<0.20.0,>=0.18.0
10
- Requires-Dist: msgpack<1.0.7,>=1.0.0
11
- Requires-Dist: networkx<3.2.0,>=2.6.0
12
- Requires-Dist: numpy<1.24.0,>=1.21.0
13
- Requires-Dist: pandas<2.0.0,>=1.3.0
14
- Requires-Dist: pydantic<2.0.0,>=1.8.0
15
- Requires-Dist: ray<3.0.0,>=2.0.0
16
- Requires-Dist: requests<3.0.0,>=2.20.0
17
- Requires-Dist: rich<14.0.0,>=12.6.0
18
- Requires-Dist: typer<1.0.0,>=0.9.0
19
- Requires-Dist: typing-extensions>=4.0.0
9
+ Requires-Dist: jedi<0.20.0,>=0.18.0; python_version < '3.11'
10
+ Requires-Dist: jedi<=0.19.2; python_version >= '3.11'
11
+ Requires-Dist: msgpack<1.0.7,>=1.0.0; python_version < '3.11'
12
+ Requires-Dist: msgpack<2.0.0,>=1.0.7; python_version >= '3.11'
13
+ Requires-Dist: networkx<3.2.0,>=2.6.0; python_version < '3.11'
14
+ Requires-Dist: networkx<4.0.0,>=3.0.0; python_version >= '3.11'
15
+ Requires-Dist: numpy<1.24.0,>=1.21.0; python_version < '3.11'
16
+ Requires-Dist: numpy<2.0.0,>=1.24.0; python_version >= '3.11' and python_version < '3.12'
17
+ Requires-Dist: numpy<2.0.0,>=1.26.0; python_version >= '3.12'
18
+ Requires-Dist: packaging>=25.0
19
+ Requires-Dist: pandas<2.0.0,>=1.3.0; python_version < '3.11'
20
+ Requires-Dist: pandas<3.0.0,>=2.0.0; python_version >= '3.11'
21
+ Requires-Dist: pydantic<2.0.0,>=1.8.0; python_version < '3.11'
22
+ Requires-Dist: pydantic<3.0.0,>=2.0.0; python_version >= '3.11'
23
+ Requires-Dist: ray<3.0.0,>=2.10.0; python_version >= '3.11'
24
+ Requires-Dist: ray==2.0.0; python_version < '3.11'
25
+ Requires-Dist: requests<3.0.0,>=2.20.0; python_version >= '3.11'
26
+ Requires-Dist: rich<14.0.0,>=12.6.0; python_version < '3.11'
27
+ Requires-Dist: rich<15.0.0,>=14.0.0; python_version >= '3.11'
28
+ Requires-Dist: typer<1.0.0,>=0.9.0; python_version < '3.11'
29
+ Requires-Dist: typer<2.0.0,>=0.9.0; python_version >= '3.11'
30
+ Requires-Dist: typing-extensions<5.0.0,>=4.0.0; python_version < '3.11'
31
+ Requires-Dist: typing-extensions<6.0.0,>=4.5.0; python_version >= '3.11'
20
32
  Description-Content-Type: text/markdown
21
33
 
22
34
  ![logo](https://github.com/codellm-devkit/codeanalyzer-python/blob/main/docs/assets/logo.png?raw=true)
@@ -1,12 +1,14 @@
1
1
  codeanalyzer/__init__.py,sha256=BZ3Kuwl-F_F-8H8cepLnVJ4Ku4NNUjjqg0Y6ujPQSsI,108
2
- codeanalyzer/__main__.py,sha256=x2LNDxYndzV2LkauBz7-_0qR58seRx-yJ07-obs4o9Q,4855
3
- codeanalyzer/core.py,sha256=RUJrtmtCFAxBLyI_eHp3Oi0tWAU09DeI7wUD7vfQwi0,24926
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/schema/__init__.py,sha256=k6N1AfXe1J7cSFBdRJlYo1FPVrr4HeXgzEmVy8MUhC4,694
9
+ codeanalyzer/options/__init__.py,sha256=4kkwBiOrGjalrkfFF8EJgfgOtaPtD7HcCYfZgcOgelU,67
10
+ codeanalyzer/options/options.py,sha256=PNdLUCF7kHfscSeeqbV6ZEAu37T1gFoeV9SqJQed7Fw,592
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
12
14
  codeanalyzer/semantic_analysis/codeql/__init__.py,sha256=ODMkdGvs3ebJdfIZle8T4VcHoCBhH_ZehWuWFpNh3NI,1022
@@ -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.10.dist-info/METADATA,sha256=hZtJ0nMdkzF9Bm1JTzaypIFLWpl9bOL1xrtrvdKBjRU,15261
25
- codeanalyzer_python-0.1.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- codeanalyzer_python-0.1.10.dist-info/entry_points.txt,sha256=eUrB7Jq5Oav6RblMX_RYfVLSw_h15NbzC3fNSnGsPuM,59
27
- codeanalyzer_python-0.1.10.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
28
- codeanalyzer_python-0.1.10.dist-info/licenses/NOTICE,sha256=YU0Z9NDWqKY-2jfFcbxeZ6fbnzz0oZeKmnUcO8a-bcQ,901
29
- codeanalyzer_python-0.1.10.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,,