codeanalyzer-python 0.2.0__py3-none-any.whl → 0.3.0__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 +99 -6
- codeanalyzer/core.py +192 -215
- codeanalyzer/neo4j/bolt.py +13 -2
- codeanalyzer/neo4j/catalog.py +2 -2
- codeanalyzer/neo4j/project.py +18 -10
- codeanalyzer/neo4j/rows.py +10 -5
- codeanalyzer/options/__init__.py +2 -2
- codeanalyzer/options/options.py +21 -1
- codeanalyzer/schema/__init__.py +2 -0
- codeanalyzer/schema/py_schema.py +16 -1
- codeanalyzer/semantic_analysis/call_graph.py +24 -3
- codeanalyzer/semantic_analysis/pycg/__init__.py +20 -0
- codeanalyzer/semantic_analysis/pycg/pycg_analysis.py +1054 -0
- codeanalyzer/semantic_analysis/{codeql/__init__.py → pycg/pycg_exceptions.py} +5 -8
- codeanalyzer/semantic_analysis/pycg/shard_planner.py +401 -0
- codeanalyzer/utils/logging.py +5 -2
- codeanalyzer/utils/progress_bar.py +15 -7
- codeanalyzer_python-0.3.0.dist-info/METADATA +550 -0
- codeanalyzer_python-0.3.0.dist-info/RECORD +38 -0
- codeanalyzer/semantic_analysis/codeql/codeql_analysis.py +0 -382
- codeanalyzer/semantic_analysis/codeql/codeql_exceptions.py +0 -12
- codeanalyzer/semantic_analysis/codeql/codeql_loader.py +0 -91
- codeanalyzer/semantic_analysis/codeql/codeql_query_runner.py +0 -185
- codeanalyzer_python-0.2.0.dist-info/METADATA +0 -393
- codeanalyzer_python-0.2.0.dist-info/RECORD +0 -39
- {codeanalyzer_python-0.2.0.dist-info → codeanalyzer_python-0.3.0.dist-info}/WHEEL +0 -0
- {codeanalyzer_python-0.2.0.dist-info → codeanalyzer_python-0.3.0.dist-info}/entry_points.txt +0 -0
- {codeanalyzer_python-0.2.0.dist-info → codeanalyzer_python-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {codeanalyzer_python-0.2.0.dist-info → codeanalyzer_python-0.3.0.dist-info}/licenses/NOTICE +0 -0
codeanalyzer/__main__.py
CHANGED
|
@@ -7,7 +7,7 @@ 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, EmitTarget
|
|
10
|
+
from codeanalyzer.options import AnalysisOptions, EmitTarget, ShardStrategy
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def main(
|
|
@@ -83,9 +83,16 @@ def main(
|
|
|
83
83
|
help="Neo4j database name (default: server default). [env: NEO4J_DATABASE]",
|
|
84
84
|
),
|
|
85
85
|
] = None,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
analysis_level: Annotated[
|
|
87
|
+
int,
|
|
88
|
+
typer.Option(
|
|
89
|
+
"-a",
|
|
90
|
+
"--analysis-level",
|
|
91
|
+
help="Analysis depth: 1=symbol table+Jedi call graph, 2=+PyCG call graph.",
|
|
92
|
+
min=1,
|
|
93
|
+
max=2,
|
|
94
|
+
),
|
|
95
|
+
] = 1,
|
|
89
96
|
using_ray: Annotated[
|
|
90
97
|
bool,
|
|
91
98
|
typer.Option("--ray/--no-ray", help="Enable Ray for distributed analysis."),
|
|
@@ -104,6 +111,14 @@ def main(
|
|
|
104
111
|
help="Skip test files in analysis.",
|
|
105
112
|
),
|
|
106
113
|
] = True,
|
|
114
|
+
no_venv: Annotated[
|
|
115
|
+
bool,
|
|
116
|
+
typer.Option(
|
|
117
|
+
"--no-venv/--venv",
|
|
118
|
+
help="Skip virtualenv creation and dependency installation; resolve "
|
|
119
|
+
"imports against the ambient Python environment instead.",
|
|
120
|
+
),
|
|
121
|
+
] = False,
|
|
107
122
|
file_name: Annotated[
|
|
108
123
|
Optional[Path],
|
|
109
124
|
typer.Option(
|
|
@@ -129,6 +144,78 @@ def main(
|
|
|
129
144
|
verbosity: Annotated[
|
|
130
145
|
int, typer.Option("-v", count=True, help="Increase verbosity: -v, -vv, -vvv")
|
|
131
146
|
] = 0,
|
|
147
|
+
pycg_shard: Annotated[
|
|
148
|
+
bool,
|
|
149
|
+
typer.Option(
|
|
150
|
+
"--pycg-shard/--no-pycg-shard",
|
|
151
|
+
help=(
|
|
152
|
+
"Shard PyCG call-graph analysis by Python package (level 2 only). "
|
|
153
|
+
"When the project exceeds the 500-file ceiling, PyCG is run "
|
|
154
|
+
"independently per top-level package with cross-package imports "
|
|
155
|
+
"treated as ghost nodes. Without this flag, projects over the "
|
|
156
|
+
"ceiling fall back to Jedi-only edges."
|
|
157
|
+
),
|
|
158
|
+
),
|
|
159
|
+
] = False,
|
|
160
|
+
pycg_shard_ceiling: Annotated[
|
|
161
|
+
int,
|
|
162
|
+
typer.Option(
|
|
163
|
+
"--pycg-shard-ceiling",
|
|
164
|
+
help=(
|
|
165
|
+
"Maximum files per shard when --pycg-shard is active (default 100). "
|
|
166
|
+
"Shards exceeding this limit are skipped; their call edges are "
|
|
167
|
+
"omitted from the call graph (Jedi edges for those packages are "
|
|
168
|
+
"still included). Lower values are safer for packages with deep "
|
|
169
|
+
"class hierarchies or heavy import graphs."
|
|
170
|
+
),
|
|
171
|
+
min=1,
|
|
172
|
+
),
|
|
173
|
+
] = 100,
|
|
174
|
+
pycg_shard_timeout: Annotated[
|
|
175
|
+
int,
|
|
176
|
+
typer.Option(
|
|
177
|
+
"--pycg-shard-timeout",
|
|
178
|
+
help=(
|
|
179
|
+
"Per-shard wall-clock timeout in seconds when --pycg-shard is "
|
|
180
|
+
"active (default 120). A shard that exceeds this limit is skipped "
|
|
181
|
+
"gracefully. PyCG's fixpoint is bimodal: it either converges "
|
|
182
|
+
"quickly or diverges indefinitely, so the timeout acts as a final "
|
|
183
|
+
"safety net after the file-count ceiling. Set to 0 to disable. "
|
|
184
|
+
"POSIX only (macOS / Linux); ignored on Windows."
|
|
185
|
+
),
|
|
186
|
+
min=0,
|
|
187
|
+
),
|
|
188
|
+
] = 120,
|
|
189
|
+
pycg_shard_strategy: Annotated[
|
|
190
|
+
ShardStrategy,
|
|
191
|
+
typer.Option(
|
|
192
|
+
"--pycg-shard-strategy",
|
|
193
|
+
help=(
|
|
194
|
+
"How --pycg-shard groups files (level 2 only). 'jedi' (default) "
|
|
195
|
+
"partitions the Jedi module-dependency graph (SCC + Louvain) so "
|
|
196
|
+
"tightly-coupled modules co-compute and few call edges are "
|
|
197
|
+
"severed between shards; import cycles are never split. "
|
|
198
|
+
"'package' uses the legacy one-shard-per-package-directory "
|
|
199
|
+
"grouping."
|
|
200
|
+
),
|
|
201
|
+
),
|
|
202
|
+
] = ShardStrategy.JEDI,
|
|
203
|
+
pycg_max_iter: Annotated[
|
|
204
|
+
int,
|
|
205
|
+
typer.Option(
|
|
206
|
+
"--pycg-max-iter",
|
|
207
|
+
help=(
|
|
208
|
+
"Cap on PyCG's fixpoint passes per shard/project (level 2; "
|
|
209
|
+
"default 50). PyCG iterates until its points-to state stops "
|
|
210
|
+
"changing, but its access-path domain has no convergence bound, "
|
|
211
|
+
"so heavy metaclass/mixin code (e.g. an ORM) can loop with each "
|
|
212
|
+
"pass costing seconds. The cap returns a sound-but-incomplete "
|
|
213
|
+
"call graph instead of looping until the timeout kills it. "
|
|
214
|
+
"Set to -1 for PyCG's unbounded run-to-convergence behaviour."
|
|
215
|
+
),
|
|
216
|
+
min=-1,
|
|
217
|
+
),
|
|
218
|
+
] = 50,
|
|
132
219
|
):
|
|
133
220
|
options = AnalysisOptions(
|
|
134
221
|
input=input,
|
|
@@ -140,14 +227,20 @@ def main(
|
|
|
140
227
|
neo4j_user=neo4j_user,
|
|
141
228
|
neo4j_password=neo4j_password,
|
|
142
229
|
neo4j_database=neo4j_database,
|
|
143
|
-
|
|
230
|
+
analysis_level=analysis_level,
|
|
144
231
|
using_ray=using_ray,
|
|
145
232
|
rebuild_analysis=rebuild_analysis,
|
|
146
233
|
skip_tests=skip_tests,
|
|
234
|
+
no_venv=no_venv,
|
|
147
235
|
file_name=file_name,
|
|
148
236
|
cache_dir=cache_dir,
|
|
149
237
|
clear_cache=clear_cache,
|
|
150
238
|
verbosity=verbosity,
|
|
239
|
+
pycg_shard=pycg_shard,
|
|
240
|
+
pycg_shard_ceiling=pycg_shard_ceiling,
|
|
241
|
+
pycg_shard_timeout=pycg_shard_timeout,
|
|
242
|
+
pycg_shard_strategy=pycg_shard_strategy,
|
|
243
|
+
pycg_max_iter=pycg_max_iter,
|
|
151
244
|
)
|
|
152
245
|
|
|
153
246
|
_set_log_level(options.verbosity)
|
|
@@ -221,7 +314,7 @@ def _write_output(artifacts, output_dir: Path, format: OutputFormat):
|
|
|
221
314
|
app = typer.Typer(
|
|
222
315
|
callback=main,
|
|
223
316
|
name="canpy",
|
|
224
|
-
help="Static Analysis on Python source code using Jedi,
|
|
317
|
+
help="Static Analysis on Python source code using Jedi, PyCG and Tree sitter.",
|
|
225
318
|
invoke_without_command=True,
|
|
226
319
|
no_args_is_help=True,
|
|
227
320
|
add_completion=False,
|