codeanalyzer-python 1.2.0__py3-none-any.whl → 1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: codeanalyzer-python
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Static analysis for Python — canonical schema v2 (symbol table, call graph, and native CFG/PDG/SDG dataflow) as analysis.json or a Neo4j property graph.
5
5
  Author-email: Rahul Krishna <i.m.ralk@gmail.com>
6
6
  License-File: LICENSE
@@ -21,6 +21,7 @@ Requires-Dist: ray==2.0.0; python_version < '3.11'
21
21
  Requires-Dist: requests<3.0.0,>=2.20.0; python_version >= '3.11'
22
22
  Requires-Dist: rich<14.0.0,>=12.6.0; python_version < '3.11'
23
23
  Requires-Dist: rich<15.0.0,>=14.0.0; python_version >= '3.11'
24
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
24
25
  Requires-Dist: typer<1.0.0,>=0.9.0; python_version < '3.11'
25
26
  Requires-Dist: typer<2.0.0,>=0.9.0; python_version >= '3.11'
26
27
  Requires-Dist: typing-extensions<5.0.0,>=4.0.0; python_version < '3.11'
@@ -315,6 +316,16 @@ $ canpy --help
315
316
  │ environment │
316
317
  │ instead. │
317
318
  │ [default: venv] │
319
+ │ --resolve-instal… Additionally │
320
+ │ bind imports via │
321
+ │ the project │
322
+ │ venv's installed │
323
+ │ metadata │
324
+ │ (*.dist-info); │
325
+ │ output becomes │
326
+ │ machine-depende… │
327
+ │ (prov: │
328
+ │ installed-metad… │
318
329
  │ --file-name <path> Analyze only the │
319
330
  │ specified file │
320
331
  │ (relative to │
@@ -346,6 +357,30 @@ $ canpy --help
346
357
  │ shipped rules. A │
347
358
  │ malformed file │
348
359
  │ is an error. │
360
+ │ --artifact-text --no-artifact-… Capture verbatim │
361
+ │ `source` text on │
362
+ │ discovered │
363
+ │ artifacts. │
364
+ │ --no-artifact-t… │
365
+ │ empties `source` │
366
+ │ everywhere │
367
+ │ (inventory │
368
+ │ unchanged). │
369
+ │ [default: │
370
+ │ artifact-text] │
371
+ │ --artifact-text-… <int range> Per-file byte │
372
+ │ [x>=1] cap on captured │
373
+ │ artifact │
374
+ │ `source`; a │
375
+ │ decodable file │
376
+ │ over the cap is │
377
+ │ truncated │
378
+ │ (text_truncated… │
379
+ │ sha256/size_byt… │
380
+ │ always reflect │
381
+ │ the full file. │
382
+ │ [default: │
383
+ │ 262144] │
349
384
  │ --help Show this │
350
385
  │ message and │
351
386
  │ exit. │
@@ -517,14 +552,29 @@ A **callable** (function or method) carries its own CPG, keyed by node id:
517
552
  }
518
553
  ```
519
554
 
555
+ The application envelope also contains three substrate sections:
556
+
557
+ - **`artifacts`** — discovered non-code files (manifests, configs, Docker files, CI workflows,
558
+ packaging files, scripts, docs, and legal files) with extraction status (`none`, `partial`, or
559
+ `full`; default `none`), keyed by relative path; each artifact carries the
560
+ `can://artifact/<app>/<path>` id namespace. Config files carry extracted `config_keys`
561
+ (keys, values, namespaces, and references) and `DEFINES_CONFIG` Neo4j edges.
562
+ - **`dependencies`** — declared packages with kind (`runtime`/`dev`/`optional`/`build`), spec,
563
+ locked version, and provenance (`prov`): where each binding came from (manifest file, lock file,
564
+ installed metadata).
565
+ - **`unresolved_imports`** — modules imported but not resolvable in the declared dependency set,
566
+ one entry per module.
567
+
520
568
  Notable properties:
521
569
 
522
570
  - **Durable `can://` ids** identify every node at callable granularity and above
523
571
  (`can://python/<app>/<file>/<callable-sig>`); nodes below a callable use ordinal ids
524
572
  (`@entry`, `@exit`, `line:col`, `@formal_in:N`, `line:col/actual_in:N`).
525
573
  - **`source` lives once per module**; every node's text is the `module.source[span.bytes]` slice.
526
- - **Cross-function edges** — `call_graph`, `param_in`, `param_out` — live at **application** scope;
574
+ - **Cross-function edges** — `call_graph`, `param_in`, `param_out`, `config_uses` — live at **application** scope;
527
575
  the intraprocedural `cfg`/`cdg`/`ddg` and the `summary` edges live **on the callable**.
576
+ `config_uses` resolve from call-site key arguments to `ConfigKey` nodes; unresolved reads
577
+ go to `config_reads_unresolved`.
528
578
  - **No dangling endpoints** — every `call_graph` `src`/`dst` joins the id space: declared
529
579
  callables by their tree id, imported/builtin targets by a `…/@external/<module>/<name>` id
530
580
  homed in `application.external_symbols`.
@@ -545,9 +595,13 @@ binary format).
545
595
 
546
596
  ### Neo4j graph
547
597
 
548
- `--emit neo4j` projects the same schema v2.0.0 analysis into a labeled property graph. Every node
549
- label is `Py`-prefixed and every relationship type is `PY_`-prefixed (e.g. `:PyClass`, `PY_CALLS`)
550
- so multiple language analyzers can share one database without label or relationship-type collisions.
598
+ `--emit neo4j` projects the same schema v2.0.0 analysis into a labeled property graph. Every
599
+ Python-specific node label is `Py`-prefixed and every Python-specific relationship type is
600
+ `PY_`-prefixed (e.g. `:PyClass`, `PY_CALLS`) so multiple language analyzers can share one database
601
+ without label or relationship-type collisions. The one deliberate exception is the language-neutral
602
+ `Artifact`/`Package` subgraph (non-code files and third-party dependencies) — those nodes carry no
603
+ `Py` prefix, since they are meant as cross-language merge targets: a sibling-language analyzer over
604
+ the same repo should land on the same `Artifact`/`Package` nodes, not a per-language duplicate.
551
605
  Declarations are keyed by their **`can://` id** under a shared `:PySymbol` label; calls, imports,
552
606
  inheritance, decorators, and call sites are relationships. At `-a 3`/`-a 4` the projection gains the
553
607
  **CPG overlay** — `:PyBodyNode` nodes (statements, and at level 4 the parameter vertices) wired by
@@ -608,6 +662,56 @@ runtime (Docker or Podman) and is enabled with an environment variable:
608
662
  RUN_CONTAINER_TESTS=1 uv run pytest test/test_neo4j_bolt.py -s
609
663
  ```
610
664
 
665
+ ## Graph query cookbook
666
+
667
+ Example Cypher over the projected graph (`--emit neo4j`, then load `graph.cypher` or push via Bolt).
668
+
669
+ ```cypher
670
+ // who calls this function? (direct callers)
671
+ MATCH (c:PyCallable)-[:PY_CALLS]->(t:PyCallable {name: "process_payment"})
672
+ RETURN c.id
673
+
674
+ // every callable that reaches a given library, via the external ghosts
675
+ MATCH (c:PyCallable)-[:PY_CALLS]->(e:PyExternal)
676
+ WHERE e.id CONTAINS "/@external/requests/"
677
+ RETURN DISTINCT c.id
678
+
679
+ // entrypoints and the frameworks that invoke them
680
+ MATCH (m:PyCallable {is_entrypoint: true})
681
+ RETURN m.id, m.entrypoint_frameworks
682
+
683
+ // data dependences into one statement (level 3+)
684
+ MATCH (s:PyBodyNode {id: $stmt})<-[d:PY_DDG]-(src:PyBodyNode)
685
+ RETURN src.id, d.var, d.prov
686
+
687
+ // interprocedural flow through a parameter (level 4)
688
+ MATCH (a:PyBodyNode)-[:PY_PARAM_IN]->(f:PyBodyNode)
689
+ WHERE f.id STARTS WITH "can://python/myapp/src/api.py"
690
+ RETURN a.id, f.id
691
+ ```
692
+
693
+ Artifact and dependency queries (1.3.0+):
694
+
695
+ ```cypher
696
+ // all container/orchestration configs in the app
697
+ MATCH (a:PyApplication)-[:HAS_ARTIFACT]->(f:Artifact)
698
+ WHERE any(r IN f.roles WHERE r IN ["service-topology", "container-image"])
699
+ RETURN f.id, f.format
700
+
701
+ // every callable that reaches code from a declared package
702
+ MATCH (c:PyCallable)-[:PY_CALLS]->(:PyExternal)<-[:PY_PROVIDES]-(p:Package {id: "pkg:pypi/requests"})
703
+ RETURN c.id
704
+
705
+ // undeclared imports (dependency hygiene)
706
+ MATCH (a:PyApplication)-[u:PY_UNRESOLVED_IMPORT]->(e:PyExternal)
707
+ WHERE NOT (e)<-[:PY_PROVIDES]-(:Package)
708
+ RETURN e.id, u.prov
709
+
710
+ // which lock file pins this package, and to what
711
+ MATCH (f:Artifact)-[l:LOCKS]->(p:Package {id: "pkg:pypi/numpy"})
712
+ RETURN f.id, l.version
713
+ ```
714
+
611
715
  ## License
612
716
 
613
717
  Apache 2.0 — see [LICENSE](./LICENSE).
@@ -1,12 +1,19 @@
1
1
  codeanalyzer/__init__.py,sha256=BZ3Kuwl-F_F-8H8cepLnVJ4Ku4NNUjjqg0Y6ujPQSsI,108
2
- codeanalyzer/__main__.py,sha256=hmuIKxpW7TFfGaBGb1HiHCFS02dCQxVhG7FkruXECWQ,14954
3
- codeanalyzer/core.py,sha256=vkU8djqKmyX4mXmb5rOz3oRjlMWYjMB7dWB_toIHnu4,42140
2
+ codeanalyzer/__main__.py,sha256=VVGmkwLGkcfCdbikp2FdXzpGZEA1md8n8WhNSxJWz1E,16080
3
+ codeanalyzer/core.py,sha256=yJ14_jL2V4qDD6pEMEltfeOLcFYTMRkRnh0dyP6RVR4,46715
4
4
  codeanalyzer/provenance.py,sha256=DT-DqwdVwO7g-GyK3sC0_4vDNCUjZYEY1fyYCt-7VRM,2306
5
5
  codeanalyzer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ codeanalyzer/artifacts/__init__.py,sha256=317sEeZLS1AYsfYDYIk7tLR3AdDFwWw_cmkeZDabkJo,906
7
+ codeanalyzer/artifacts/config_keys.py,sha256=ovwptAErLYutlCzUwvyim2kH6-6vUKxuRcBV2KJS71Y,26843
8
+ codeanalyzer/artifacts/config_use.py,sha256=1-jGacW249dwF7b6DOrTFuJ7sA5Ho9khgrgZqND0nro,27173
9
+ codeanalyzer/artifacts/config_use_rules.yml,sha256=fNgI6IceOsj78LYmgoDH-vQ4-Mfb2ZFGKIQhLuoL4ik,1483
10
+ codeanalyzer/artifacts/dependencies.py,sha256=bURMUbBzHaF8bInRk-INp1GaGujsUSXnFqLtNyECUMI,10888
11
+ codeanalyzer/artifacts/discovery.py,sha256=8as4RD5oNgZM2qyg19HUqsdq9eETwPDGQUOn4fZLOsQ,7427
12
+ codeanalyzer/artifacts/parsers.py,sha256=xOjC53tT0Mv0k7ONGwzl5XUvpBLnZlEcGmyrX5DS45k,9035
6
13
  codeanalyzer/dataflow/__init__.py,sha256=HCNOH24rAGLUMG94N9aC-2SsAWSntk3uys7e39ipU5o,1725
7
14
  codeanalyzer/dataflow/access_paths.py,sha256=wC8Q9qD-RZzkoFWMVvu_6uNNmYP8z48OGp9h9v3F1d4,23623
8
15
  codeanalyzer/dataflow/alias.py,sha256=ZYKY0DiR3GHv6YJr7C001Lode9rZsk6-gxdomUyIvwg,3928
9
- codeanalyzer/dataflow/builder.py,sha256=sCngN3MWfCbHteSHiue8l7AUVQNttETJInsgVMy_xkY,31317
16
+ codeanalyzer/dataflow/builder.py,sha256=OOj5J-O0WgBnLMSu6hhixkM8dVkUopwBR-wlQq0INhE,32463
10
17
  codeanalyzer/dataflow/cfg.py,sha256=u5YXJjAaDshdgv-9kWWITbBJFP-MwXKq6hbNh0fhDm8,25307
11
18
  codeanalyzer/dataflow/defuse.py,sha256=LrX1ToOZzR79NlAGg5T647UAFkpiEqEnT7t0K5RXOiI,4377
12
19
  codeanalyzer/dataflow/dominance.py,sha256=X7Ki1QRdVqaseYsJtiUL7m9MbcC2WHEv9b8bczSLFUA,5086
@@ -41,31 +48,31 @@ codeanalyzer/neo4j/__init__.py,sha256=8Ei6uThTOqmul6GhnKoGOjLXJZbyDwF5Jo-8I8NxnA
41
48
  codeanalyzer/neo4j/bolt.py,sha256=qEBtQlBjaMPpLQPUCOAR9jEqLxpXYJG5mtxdYAhjtMI,10134
42
49
  codeanalyzer/neo4j/cypher.py,sha256=a7YSmxxDbPcYc5gI4TW9sM6_WOE3RgLXTGBR9K8W8kw,5321
43
50
  codeanalyzer/neo4j/emit.py,sha256=QdrZWG3_IQHMcKCX4bFINpXvqZU2Qfsi9beIJovC2p4,3493
44
- codeanalyzer/neo4j/project.py,sha256=LFj8O_2DYA_dvMTbSYK_PYxmnP2UJ4h_3J8rCIoK1w8,25927
51
+ codeanalyzer/neo4j/project.py,sha256=DcymijKIy9TvBUhzlB_0fDmoSA8WRufJU-1oVP1hH1w,35102
45
52
  codeanalyzer/neo4j/rows.py,sha256=med0XFa63PPUVUhJMq1TtVcJxgaVW39sgxBRZJDIJ_w,7811
46
- codeanalyzer/neo4j/schema.py,sha256=kx134hxG_Uv6b5PkHFfkX7L1STpxSaW4pGTDe_Wruf0,10768
53
+ codeanalyzer/neo4j/schema.py,sha256=HXM-rG-NBuujXU2fXGAUb65sGRKKTXDGB7jr360qEYQ,14330
47
54
  codeanalyzer/options/__init__.py,sha256=6NewN0a-1HAEgKcxQjYyVn2WEDLrhax8h3WLgZddeqI,94
48
- codeanalyzer/options/options.py,sha256=5gmKNxb51vWmIknE4e8kyT9nJRchL0VYspZxe_PAsws,1389
55
+ codeanalyzer/options/options.py,sha256=3GF1m4AI-9NN2sWmhl8wrvi2HjMwofec-We3WqKs_HA,1642
49
56
  codeanalyzer/schema/__init__.py,sha256=hIyz02abUJNPW8yUgguaeKflZjTmOZWbDiKICNBD7yk,4141
50
57
  codeanalyzer/schema/assign_ids.py,sha256=pQHluLaO5a6hFREp3vjVRsUeAfTYSJ95RXS9TZvnoys,1590
51
58
  codeanalyzer/schema/call_graph_ids.py,sha256=mWAhJDBsW8i-siJiINOHCXEm4qM6F_5se9-HuHWIFqU,568
52
- codeanalyzer/schema/ids.py,sha256=G5NGPjJ3UdJ1k0opy2MrvgWRWNBZgKU1PwY1MR6hDT4,829
59
+ codeanalyzer/schema/ids.py,sha256=aOzsgVaOo6x72dnRgNxlnt6b5wbZzhTm0JOz6pdI57I,1672
53
60
  codeanalyzer/schema/l1_body.py,sha256=5Su347kwAPNflJDf7SvBR3sNXx9PVdGEml2pOpQCwo0,1684
54
61
  codeanalyzer/schema/l2_callees.py,sha256=CTcBeGoO04DstDC6h-1sHMbbIrHDEOMxP01U9VLBcfc,2327
55
- codeanalyzer/schema/py_schema.py,sha256=VwwRqr_jFolkbRKi8ytiwUScKZmscJBZvh16Y2tDbII,18449
62
+ codeanalyzer/schema/py_schema.py,sha256=pN03WJcb96LegGSFs7HIBWzTE8bnn8T2adH9M9FJGgQ,23966
56
63
  codeanalyzer/semantic_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
64
  codeanalyzer/semantic_analysis/call_graph.py,sha256=6YEB_wTn5-oQYLrbIhJYE0BsHl4fpWPoy5Hwd9mTnGc,11918
58
65
  codeanalyzer/semantic_analysis/defuse_linker.py,sha256=bSn1rCun28OQqSd2NOc9aVBSm47Gt9Iul4r_gwfqO1w,64930
59
66
  codeanalyzer/syntactic_analysis/__init__.py,sha256=EUQkJEh6wHjWx2qTTKbTbUgwSbfKeNieKHNy7RknVXA,476
60
67
  codeanalyzer/syntactic_analysis/exceptions.py,sha256=whs_n0vIu655Jkk1a7iOoXY6iIca4pZqJnU40V9Ejaw,537
61
68
  codeanalyzer/syntactic_analysis/import_resolver.py,sha256=Q8noZwSdDNt4P4NMqDTB9FaS-M0_d4ASK9GnJop9MDI,2751
62
- codeanalyzer/syntactic_analysis/symbol_table_builder.py,sha256=2bp9S5Z9Atx0Ovd70nMzAbutoNx_FD0VYBY397GAAVo,47343
69
+ codeanalyzer/syntactic_analysis/symbol_table_builder.py,sha256=eTknBDlUuuQd3JEwbRtJt5pGVU89kOnzfmT4D5J_GKQ,47917
63
70
  codeanalyzer/utils/__init__.py,sha256=hC6VWdR5rerSqBxzu9KQHTASWqwrrYJv-CMDwrTlzkc,137
64
71
  codeanalyzer/utils/logging.py,sha256=Fw0tattPAOMs3o0JMjjXhRVLIF64f-SCcygUXF9jqeg,904
65
72
  codeanalyzer/utils/progress_bar.py,sha256=C9JtzVdd10lIxTv-KA6PebqjKWueC_vMGwVzAtHuHIw,2818
66
- codeanalyzer_python-1.2.0.dist-info/METADATA,sha256=Un9ZuyLWDpzfB_CNuAXiXGvM8eN0HQmr_0r2jGsBqqA,36319
67
- codeanalyzer_python-1.2.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
68
- codeanalyzer_python-1.2.0.dist-info/entry_points.txt,sha256=v4Vux0Nnx7sOntVk_CH7W9RX6SkIkvR1FQYq73oVlCQ,105
69
- codeanalyzer_python-1.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
70
- codeanalyzer_python-1.2.0.dist-info/licenses/NOTICE,sha256=MdVkNYqHJ20on2FmWvgD4WpMsX5mir33xdyPvTXd0A0,1223
71
- codeanalyzer_python-1.2.0.dist-info/RECORD,,
73
+ codeanalyzer_python-1.3.0.dist-info/METADATA,sha256=QAUQ1DK8997qsSI8-g4d294RfI7wLPljdOL4gi42Ofo,42305
74
+ codeanalyzer_python-1.3.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
75
+ codeanalyzer_python-1.3.0.dist-info/entry_points.txt,sha256=v4Vux0Nnx7sOntVk_CH7W9RX6SkIkvR1FQYq73oVlCQ,105
76
+ codeanalyzer_python-1.3.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
77
+ codeanalyzer_python-1.3.0.dist-info/licenses/NOTICE,sha256=MdVkNYqHJ20on2FmWvgD4WpMsX5mir33xdyPvTXd0A0,1223
78
+ codeanalyzer_python-1.3.0.dist-info/RECORD,,