codeanalyzer-python 0.2.1__py3-none-any.whl → 0.3.1__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 +116 -6
- codeanalyzer/core.py +139 -213
- codeanalyzer/neo4j/__init__.py +1 -1
- codeanalyzer/neo4j/emit.py +2 -2
- codeanalyzer/neo4j/project.py +84 -18
- codeanalyzer/neo4j/schema.py +261 -15
- codeanalyzer/options/__init__.py +2 -2
- codeanalyzer/options/options.py +20 -1
- codeanalyzer/provenance.py +61 -0
- codeanalyzer/schema/py_schema.py +39 -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/syntactic_analysis/import_resolver.py +67 -0
- codeanalyzer/syntactic_analysis/symbol_table_builder.py +74 -10
- codeanalyzer/utils/logging.py +5 -2
- codeanalyzer/utils/progress_bar.py +15 -7
- codeanalyzer_python-0.3.1.dist-info/METADATA +553 -0
- codeanalyzer_python-0.3.1.dist-info/RECORD +39 -0
- {codeanalyzer_python-0.2.1.dist-info → codeanalyzer_python-0.3.1.dist-info}/WHEEL +1 -1
- codeanalyzer/neo4j/catalog.py +0 -245
- 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.1.dist-info/METADATA +0 -415
- codeanalyzer_python-0.2.1.dist-info/RECORD +0 -39
- {codeanalyzer_python-0.2.1.dist-info → codeanalyzer_python-0.3.1.dist-info}/entry_points.txt +0 -0
- {codeanalyzer_python-0.2.1.dist-info → codeanalyzer_python-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {codeanalyzer_python-0.2.1.dist-info → codeanalyzer_python-0.3.1.dist-info}/licenses/NOTICE +0 -0
codeanalyzer/schema/py_schema.py
CHANGED
|
@@ -176,6 +176,7 @@ class PyImport(BaseModel):
|
|
|
176
176
|
module: str
|
|
177
177
|
name: str
|
|
178
178
|
alias: Optional[str] = None
|
|
179
|
+
resolved_module: Optional[str] = None
|
|
179
180
|
start_line: int = -1
|
|
180
181
|
end_line: int = -1
|
|
181
182
|
start_column: int = -1
|
|
@@ -240,6 +241,19 @@ class PyCallableParameter(BaseModel):
|
|
|
240
241
|
end_column: int = -1
|
|
241
242
|
|
|
242
243
|
|
|
244
|
+
@builder
|
|
245
|
+
@msgpk
|
|
246
|
+
class PyCallArgument(BaseModel):
|
|
247
|
+
"""One call-site argument: AST category + inferred type, kept separate.
|
|
248
|
+
|
|
249
|
+
The legacy ``PyCallsite.argument_types`` mixed these two vocabularies
|
|
250
|
+
in one list; this model is the disambiguated replacement (#86).
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
ast_kind: str
|
|
254
|
+
inferred_type: Optional[str] = None
|
|
255
|
+
|
|
256
|
+
|
|
243
257
|
@builder
|
|
244
258
|
@msgpk
|
|
245
259
|
class PyCallsite(BaseModel):
|
|
@@ -249,6 +263,7 @@ class PyCallsite(BaseModel):
|
|
|
249
263
|
receiver_expr: Optional[str] = None
|
|
250
264
|
receiver_type: Optional[str] = None
|
|
251
265
|
argument_types: List[str] = []
|
|
266
|
+
arguments: List[PyCallArgument] = []
|
|
252
267
|
return_type: Optional[str] = None
|
|
253
268
|
callee_signature: Optional[str] = None
|
|
254
269
|
is_constructor_call: bool = False
|
|
@@ -295,6 +310,7 @@ class PyClassAttribute(BaseModel):
|
|
|
295
310
|
|
|
296
311
|
name: str
|
|
297
312
|
type: Optional[str] = None
|
|
313
|
+
initializer: Optional[str] = None
|
|
298
314
|
comments: List[PyComment] = []
|
|
299
315
|
start_line: int = -1
|
|
300
316
|
end_line: int = -1
|
|
@@ -355,7 +371,7 @@ class PyCallEdge(BaseModel):
|
|
|
355
371
|
target: str # callee's PyCallable.signature
|
|
356
372
|
type: Literal["CALL_DEP"] = "CALL_DEP"
|
|
357
373
|
weight: int = 1
|
|
358
|
-
provenance: List[Literal["jedi", "
|
|
374
|
+
provenance: List[Literal["jedi", "pycg", "joern"]] = []
|
|
359
375
|
|
|
360
376
|
|
|
361
377
|
@builder
|
|
@@ -369,6 +385,26 @@ class PyExternalSymbol(BaseModel):
|
|
|
369
385
|
module: Optional[str] = None # best-effort owning module, e.g. "requests"
|
|
370
386
|
|
|
371
387
|
|
|
388
|
+
@builder
|
|
389
|
+
@msgpk
|
|
390
|
+
class PyRepositoryInfo(BaseModel):
|
|
391
|
+
"""Where the analyzed source came from: git provenance captured at analysis time."""
|
|
392
|
+
|
|
393
|
+
uri: Optional[str] = None
|
|
394
|
+
revision: str
|
|
395
|
+
dirty: bool = False
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@builder
|
|
399
|
+
@msgpk
|
|
400
|
+
class PyAnalyzerInfo(BaseModel):
|
|
401
|
+
"""Which analyzer produced this snapshot, and how it was configured."""
|
|
402
|
+
|
|
403
|
+
name: str
|
|
404
|
+
version: str
|
|
405
|
+
config: Dict[str, Any] = {}
|
|
406
|
+
|
|
407
|
+
|
|
372
408
|
@builder
|
|
373
409
|
@msgpk
|
|
374
410
|
class PyApplication(BaseModel):
|
|
@@ -380,3 +416,5 @@ class PyApplication(BaseModel):
|
|
|
380
416
|
# builtin members), keyed by signature. Populated by the analyzer so every
|
|
381
417
|
# backend (JSON and Neo4j) shares one authoritative external-symbol set.
|
|
382
418
|
external_symbols: Dict[str, PyExternalSymbol] = {}
|
|
419
|
+
analyzer: Optional[PyAnalyzerInfo] = None
|
|
420
|
+
repository: Optional[PyRepositoryInfo] = None
|
|
@@ -173,7 +173,7 @@ def jedi_call_graph_edges(
|
|
|
173
173
|
|
|
174
174
|
Edges are coalesced on ``(source, target)``: ``weight`` is the count of
|
|
175
175
|
matching sites. Provenance is always ``["jedi"]``; combine with
|
|
176
|
-
|
|
176
|
+
PyCG-derived edges via ``merge_edges``.
|
|
177
177
|
"""
|
|
178
178
|
counts: Counter = Counter()
|
|
179
179
|
for caller in iter_callables_in_symbol_table(symbol_table):
|
|
@@ -191,7 +191,7 @@ def jedi_call_graph_edges(
|
|
|
191
191
|
def resolve_unresolved_constructors(symbol_table: Dict[str, PyModule]) -> int:
|
|
192
192
|
"""Fill in ``PyCallsite.callee_signature`` for unresolved constructor sites.
|
|
193
193
|
|
|
194
|
-
When
|
|
194
|
+
When Jedi fails to resolve a constructor call (commonly
|
|
195
195
|
for classes nested inside functions or methods, where static-analysis
|
|
196
196
|
points-to is weakest), Jedi still flags the site as
|
|
197
197
|
``is_constructor_call=True`` with ``method_name`` set to the class's
|
|
@@ -246,12 +246,33 @@ def resolve_unresolved_constructors(symbol_table: Dict[str, PyModule]) -> int:
|
|
|
246
246
|
return resolved
|
|
247
247
|
|
|
248
248
|
|
|
249
|
+
def filter_external_edges(
|
|
250
|
+
edges: List[PyCallEdge],
|
|
251
|
+
symbol_table: Dict[str, PyModule],
|
|
252
|
+
) -> List[PyCallEdge]:
|
|
253
|
+
"""Remove edges where both source and target are outside the app namespace.
|
|
254
|
+
|
|
255
|
+
Edges where an app callable calls a library function (or vice-versa) are
|
|
256
|
+
retained; only lib→lib edges are dropped. The app symbol set is built by
|
|
257
|
+
walking every callable in the symbol table recursively (including nested
|
|
258
|
+
functions and closures via ``inner_callables``) plus every class, so
|
|
259
|
+
PyCG-discovered closure nodes are correctly recognised as app symbols.
|
|
260
|
+
"""
|
|
261
|
+
app_symbols: set = {c.signature for c in iter_callables_in_symbol_table(symbol_table)}
|
|
262
|
+
app_symbols.update(cls.signature for cls in iter_classes_in_symbol_table(symbol_table))
|
|
263
|
+
|
|
264
|
+
return [
|
|
265
|
+
e for e in edges
|
|
266
|
+
if e.source in app_symbols or e.target in app_symbols
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
|
|
249
270
|
def merge_edges(*edge_lists: list) -> list:
|
|
250
271
|
"""Merge multiple ``List[PyCallEdge]`` into one.
|
|
251
272
|
|
|
252
273
|
Edges with the same ``(source, target)`` are coalesced: weights sum,
|
|
253
274
|
provenance is the sorted union. Useful for combining edges produced
|
|
254
|
-
by different backends (e.g. Jedi +
|
|
275
|
+
by different backends (e.g. Jedi + PyCG).
|
|
255
276
|
"""
|
|
256
277
|
by_key: Dict[Tuple[str, str], PyCallEdge] = {}
|
|
257
278
|
for edges in edge_lists:
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
# Copyright IBM Corporation 2025
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
################################################################################
|
|
16
|
+
|
|
17
|
+
from codeanalyzer.semantic_analysis.pycg.pycg_analysis import PyCG
|
|
18
|
+
from codeanalyzer.semantic_analysis.pycg.pycg_exceptions import PyCGExceptions
|
|
19
|
+
|
|
20
|
+
__all__ = ["PyCG", "PyCGExceptions"]
|