From b9f27593597e5a8545713dcdd2df5a6563541dc4 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 10:33:16 -0400 Subject: [PATCH 01/14] docs(spec): reshape analysis levels as a fluent pass pipeline Design for refactoring Codeanalyzer.analyze() into a fluent AnalysisPipeline of four subsystem-grouped passes over a shared AnalysisContext. Passes self-gate on an intrinsic min_level (no explicit skip arg); caching/venv stay at the edge; three helpers move off Codeanalyzer. Behavior-preserving: byte-identical output at every level, monotonicity preserved. Includes the four-layer testing plan. --- ...-analysis-pipeline-fluent-passes-design.md | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-22-analysis-pipeline-fluent-passes-design.md diff --git a/docs/superpowers/specs/2026-07-22-analysis-pipeline-fluent-passes-design.md b/docs/superpowers/specs/2026-07-22-analysis-pipeline-fluent-passes-design.md new file mode 100644 index 0000000..31c4d64 --- /dev/null +++ b/docs/superpowers/specs/2026-07-22-analysis-pipeline-fluent-passes-design.md @@ -0,0 +1,279 @@ +# Reshaping the analysis levels as a fluent pass pipeline + +- **Date:** 2026-07-22 +- **Status:** Design approved; ready for an implementation plan +- **Scope:** Internal refactor of `Codeanalyzer.analyze()` orchestration. No + schema change, no CLI change, no output change. + +## Motivation + +`Codeanalyzer.analyze()` (`codeanalyzer/core.py:560-691`) is a single ~130-line +procedural method that grows the canonical schema-v2 CPG tree one level at a +time. The level gating is threaded through the body as scattered +`if self.analysis_level >= 2 / 3 / 4:` checks, and the transient artifacts that +one step produces and a later step consumes (`sig_to_id`, the L3 PDGs `infos`, +the L4 program graphs `ir`) are ordinary locals with no named home. The method +is hard to read as "the four levels," the per-level work is not independently +testable, adding or reordering a step means editing the monolith, and +per-step observability is hand-rolled (only the Jedi and symbol-table timings +are instrumented today). + +## Goals + +- **Readability / maintainability** — the orchestration reads as a linear, + self-documenting chain that mirrors the four analysis levels. +- **Testable passes in isolation** — each pass is a `context -> context` + function that can be run and asserted against a hand-built context, not only + through the full pipeline. +- **Extensibility** — inserting, reordering, or adding a pass is a local edit, + not surgery on a monolith. +- **Per-pass observability** — uniform timing, logging, and gate reporting for + every pass, replacing the ad-hoc instrumentation. + +All of this while keeping output **byte-identical at every level** — this is a +behavior-preserving refactor. + +## Non-goals + +- No change to the emitted schema (`schema_version` stays `2.0.0`), the + `Analysis` envelope, the `-a`/`--graphs`/`--graph-field-depth` CLI surface, or + the Neo4j projection. +- No change to the caching format, the venv provisioning, or the + degradation semantics (PyCG failure → Jedi-only; Scalpel absent → type-based + fallback). +- No new analysis capability. Passes wrap exactly the work `analyze()` does + today, in the same order. + +## The pattern: a fluent analysis-pass pipeline + +One architectural pattern: **analysis passes composed through a fluent +`.with_*()` chain over a shared context.** The chain replaces the procedural +body of `analyze()`; the scattered level gates are replaced by each pass +self-gating on the level it belongs to. + +The chain groups by subsystem (mirroring the package layout +`syntactic_analysis/` → `semantic_analysis/` → `dataflow/`), giving **four +passes**: + +```python +analysis = ( + AnalysisPipeline(ctx) + .with_symbol_table() # syntactic_analysis/ (L1 tree) + .with_call_graph() # semantic_analysis/ + identity (L1 + L2) + .with_intraproc_dataflow() # dataflow/ L3 + .with_interproc_dataflow() # dataflow/ L4 + .build() # -> Analysis +) +``` + +## Architecture + +### New module: `codeanalyzer/pipeline.py` + +Holds two types; `Codeanalyzer` remains the engine that owns the process/ +lifecycle concerns. + +### `AnalysisContext` + +A plain dataclass — the single carrier threaded through the chain. Inputs set at +construction; produced artifacts start empty and are filled in chain order. + +``` +# inputs +options # AnalysisOptions +project_dir # Path +virtualenv # Optional[Path] +analysis_level # int (== options.analysis_level, hoisted for the gates) +app_name # str +cached_symbol_table # Dict[str, PyModule] (from cache, for per-file reuse) + +# produced by passes (empty/None until their pass runs) +symbol_table # Dict[str, PyModule] <- with_symbol_table +app # PyApplication <- with_call_graph +sig_to_id # dict <- with_call_graph (consumed by both dataflow passes) +infos # L3 PDGs <- with_intraproc_dataflow (REUSED by L4) +ir # L4 program graphs <- with_interproc_dataflow +``` + +### `AnalysisPipeline` + +Holds the context; each `.with_*()` mutates it and returns `self`; `.build()` +assembles and returns the `Analysis` envelope. Gating, timing, and logging are +folded into one shared runner so every pass is instrumented identically: + +```python +class AnalysisPipeline: + def __init__(self, ctx: AnalysisContext): + self.ctx = ctx + + def with_symbol_table(self): return self._run("symbol_table", 1, _pass_symbol_table) + def with_call_graph(self): return self._run("call_graph", 1, _pass_call_graph) + def with_intraproc_dataflow(self): return self._run("intraproc_dataflow", 3, _pass_intraproc_dataflow) + def with_interproc_dataflow(self): return self._run("interproc_dataflow", 4, _pass_interproc_dataflow) + + def _run(self, name, min_level, fn): + if self.ctx.analysis_level < min_level: + logger.info("⏭️ %s: skipped (level %d < %d)", name, self.ctx.analysis_level, min_level) + return self + t0 = time.perf_counter() + fn(self.ctx) # pure context -> context mutation + logger.info("✅ %s: %.1fs", name, time.perf_counter() - t0) + return self + + def build(self) -> Analysis: + return Analysis( + max_level=self.ctx.analysis_level, + k_limit=self.ctx.options.graph_field_depth if self.ctx.analysis_level >= 3 else None, + analyzer=analyzer_info(self.ctx.analysis_level), + application=self.ctx.app, + ) +``` + +Each pass is a module-level function `_pass_*(ctx)` — that is the isolated, +testable unit. Every pass asserts its **preconditions** (e.g. +`_pass_interproc_dataflow` asserts `ctx.app` and `ctx.infos` are populated) so a +misordered chain fails loudly instead of dereferencing `None`. + +### The four passes + +| Pass | `min_level` | Wraps (from today's `analyze()`) | Reads → writes | +| --- | --- | --- | --- | +| `_pass_symbol_table` | 1 | `_build_symbol_table`, `resolve_unresolved_constructors` | `cached_symbol_table` → `symbol_table` | +| `_pass_call_graph` | 1 | `jedi_call_graph_edges`; **(≥2)** `_get_pycg_call_graph` + `merge_edges`; `filter_external_edges`; edge sort; `PyApplication.builder()`; `resolve_imports`; `repository_info`; `assign_ids`; `_home_external_symbols`; `populate_l1_body`; **(≥2)** `backfill_callees`; `reidentify_call_graph` | `symbol_table` → `app`, `sig_to_id` | +| `_pass_intraproc_dataflow` | 3 | `build_function_pdgs` (`SyntacticOracle`), `emit_l3_body` (with `set(options.graphs.split(","))`) | `app`, `sig_to_id` → `infos` | +| `_pass_interproc_dataflow` | 4 | `build_program_graphs` (`make_alias_oracle`/Scalpel), `emit_l4`, `emit_ddg_pointsto_delta` (reusing `ctx.infos`) | `app`, `sig_to_id`, **`infos`** → `ir` | + +### `Codeanalyzer.analyze()` becomes a thin wrapper + +It keeps what it already owns — venv lifecycle (`__enter__`/`__exit__`), cache +load/save, the analyzer-version check — and delegates the build: + +```python +def analyze(self) -> Analysis: + cache_file = self.cache_dir / "analysis_cache.json" + cached = self._maybe_load_cache(cache_file) # existing logic, unchanged + seed = cached.application.symbol_table if cached else {} + ctx = AnalysisContext( + options=self.options, project_dir=self.project_dir, + virtualenv=self.virtualenv, analysis_level=self.analysis_level, + app_name=self.options.app_name or self.project_dir.name, + cached_symbol_table=seed, + ) + analysis = ( + AnalysisPipeline(ctx) + .with_symbol_table() + .with_call_graph() + .with_intraproc_dataflow() + .with_interproc_dataflow() + .build() + ) + self._save_analysis_cache(analysis, cache_file) + return analysis +``` + +## Key decisions + +### No explicit `skip` argument — passes self-gate on `min_level` + +The pipeline holds the context, which already carries `analysis_level`, so a +caller-supplied `skip=level < 3` re-derives a fact the pass already has and +invites the wrong-predicate bug (`skip=level < 2` on an L3 pass). The level a +pass belongs to is an **intrinsic property of the pass**, declared once as the +`min_level` argument to `_run` (greppable, one line per pass). The chain reads +as a clean declarative list with no predicates, and the `⏭️ skipped +(level 2 < 3)` log makes the runtime gate observable. + +### Internal L2 gates stay inside `_pass_call_graph` + +The call graph always runs Jedi at L1 and only *adds* PyCG at ≥2; likewise +`backfill_callees` is L2-only. These are sub-steps of one pass, not whole +passes, so they remain plain `if ctx.analysis_level >= 2:` branches in the pass +body. There is nothing to hoist to the chain level. + +### Caching and venv stay at the edge, not as passes + +Cache load/save and venv provisioning are process/lifecycle concerns. Keeping +them in `Codeanalyzer` (out of the pipeline) keeps every pass a pure +`context -> context` function testable with a hand-built context. The pipeline +consumes the cached symbol table via `ctx.cached_symbol_table` and produces the +`Analysis` the wrapper then persists. + +### The three helpers move out of `Codeanalyzer` + +`_build_symbol_table`, `_get_pycg_call_graph`, and `_home_external_symbols` +become plain functions (in the pipeline module or their home +`syntactic_`/`semantic_` modules) taking explicit arguments, so a pass has no +hidden dependency back on the `Codeanalyzer` instance. This is the bulk of the +mechanical churn. Callers are **updated** rather than shimmed (see Testing) — no +dead compatibility surface. + +## Behavior-preservation guarantee + +The refactor moves code without changing what it produces. Output is already +deterministic — the `PYTHONHASHSEED=0` re-exec (`__main__._pin_hash_seed`) and +the sorted call graph (issue #99) — so **byte-identical `analysis.json` at every +level** is a legitimate, non-flaky gate, and the existing monotonicity invariant +(`L1 ⊆ L2 ⊆ L3 ⊆ L4`) must continue to hold. + +## Testing plan + +Four layers, built in TDD order. + +1. **Characterization snapshot — the pin (written & committed FIRST, against + current code).** New `test/test_pipeline_equivalence.py`. Capture + `analysis.json` for ~4 fast `single_functionalities/*` fixtures at `-a 1,2,3,4` + (`--no-venv`) from today's `analyze()`, commit as golden. After the refactor + the same runs must be **byte-identical**. Catches any reordering the + structural asserts would wave through (e.g. `resolve_imports` / provenance / + `assign_ids` landing in a different order, or L4 recomputing PDGs instead of + reusing `ctx.infos`). One fixture's `graph.cypher` is snapshotted too, to pin + the Neo4j projection. + +2. **The existing suite = the broad net (runs unchanged, zero output-changing + edits).** `test_v2_superset` (monotonicity), `conftest_v2.assert_conformant` + (keystone), `test_v2_l1_body`/`l2`/`l3`/`l4`, `test_dataflow_*`, + `test_v2_cache`, `test_v2_two_projection_agreement`, `test_neo4j_*`, + `test_cli` all drive through `analyze()`/the CLI, so they exercise the new + pipeline automatically. All green with no edits ⇒ behavior preserved. + +3. **New unit tests for the genuinely-new mechanics — `test_pipeline.py` (the + RED tests, written against the new API before it exists).** + - **`_run` gating:** a pass below its `min_level` is a no-op — its context + field stays empty, the skip line is logged, and it returns `self`. + - **Each `_pass_*(ctx)` in isolation** against a hand-built context: + populates only its output field, and asserts its precondition so a + misordered chain fails loudly. + - **Self-gating reproduces the old gates:** a context at level 2 → full chain + → `infos`/`ir` empty and `app` carries no `cfg`/`ddg`. + - **L3→L4 reuse ordering:** at level 4, `ctx.infos` is already populated when + the interproc pass runs. + +4. **Helper-move regression (mechanical).** Moving the three helpers off + `Codeanalyzer`. Known impact: **update `test/test_env_interpreter.py:151`** + (it calls `analyzer._build_symbol_table(cached_symbol_table={})`) to the new + location, and update the two prose comment references in + `dataflow/scalpel_oracle.py` and `semantic_analysis/pycg/pycg_analysis.py` if + the names change. + +**Process:** (1) commit golden snapshots against current code → (2) write +`test_pipeline.py` (fails, RED) → (3) implement the pipeline + move helpers → +(4) green: unit tests pass, equivalence is byte-identical, existing suite +passes, fix the one `test_env_interpreter` call → (5) verify by running the CLI +at each level on a fixture and eyeballing the `⏭️/✅` per-pass logs. + +## Risks and rollback + +- **Hidden ordering dependency** — the byte-identical snapshot is the specific + guard; any reorder that changes output fails it immediately. +- **L4 losing the `infos` reuse** — the chain guarantees the L3 pass runs before + the L4 pass at level 4; the reuse-ordering unit test and the equivalence + snapshot both cover it. +- **Rollback** is trivial: the change is isolated to `core.py` plus the new + `pipeline.py`; reverting the commit restores the procedural `analyze()`. + +## Out of scope + +- Splitting the call graph into finer passes, or making passes registerable / + data-driven — deliberately deferred; four subsystem-grouped passes is the + chosen granularity. +- Any change to Ray distribution, sharding, or the dataflow kernels themselves. From d98403734536f4ca753953886998364a1327bb71 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 10:47:46 -0400 Subject: [PATCH 02/14] docs(plan): implementation plan for the fluent analysis-pass pipeline --- ...6-07-22-analysis-pipeline-fluent-passes.md | 975 ++++++++++++++++++ 1 file changed, 975 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-22-analysis-pipeline-fluent-passes.md diff --git a/docs/superpowers/plans/2026-07-22-analysis-pipeline-fluent-passes.md b/docs/superpowers/plans/2026-07-22-analysis-pipeline-fluent-passes.md new file mode 100644 index 0000000..db7a1c1 --- /dev/null +++ b/docs/superpowers/plans/2026-07-22-analysis-pipeline-fluent-passes.md @@ -0,0 +1,975 @@ +# Analysis Pipeline (Fluent Passes) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Refactor `Codeanalyzer.analyze()` from a ~130-line procedural method with scattered `if analysis_level >= N` gates into a fluent `AnalysisPipeline` of four subsystem-grouped passes over a shared `AnalysisContext`, with no change to emitted output. + +**Architecture:** A new `codeanalyzer/pipeline/` package holds an `AnalysisContext` dataclass (the carrier threaded through the chain), four `_pass_*(ctx)` functions (symbol table → call graph → intraprocedural dataflow → interprocedural dataflow), and an `AnalysisPipeline` whose `.with_*()` methods self-gate on an intrinsic `min_level` via one shared runner. `Codeanalyzer` keeps the venv/cache lifecycle and delegates the build to the pipeline. Three helper methods move off `Codeanalyzer` into the package so passes have no hidden dependency on the engine instance. + +**Tech Stack:** Python 3.9+, Pydantic v2 schema models, Typer CLI, Ray (optional parallelism), pytest, jedi/PyCG/tree-sitter, optional `python-scalpel`. + +## Global Constraints + +- **Conventional Commits** for every commit (`type(scope): summary`). +- **Never add AI/Claude authorship** anywhere — no `Co-Authored-By`, no "Generated with", no 🤖 trailer, in commits, PRs, code, or docs. +- **Behavior-preserving refactor:** emitted `analysis.json` and `graph.cypher` must be unchanged at every level. `schema_version` stays `2.0.0`. +- **Monotonicity invariant** `L1 ⊆ L2 ⊆ L3 ⊆ L4` must continue to hold (existing CI gate `test/test_v2_superset.py`). +- **Determinism** is already guaranteed by `PYTHONHASHSEED=0` (re-exec in `__main__._pin_hash_seed`) and the sorted call graph — do not break it. +- **No new dependencies.** `python-scalpel` remains an optional soft dependency (absent → type-based fallback, never a hard failure). +- **No circular imports:** the `pipeline` package may import from `schema/`, `semantic_analysis/`, `syntactic_analysis/`, `dataflow/`, `provenance`, `options`, `utils` — but **never** from `core`. `core` imports from `pipeline`. + +## File Structure + +- Create `codeanalyzer/pipeline/__init__.py` — re-exports `AnalysisContext`, `AnalysisPipeline`. +- Create `codeanalyzer/pipeline/context.py` — the `AnalysisContext` dataclass. +- Create `codeanalyzer/pipeline/symbol_table.py` — `build_symbol_table` + Ray helpers (`_ensure_ray`, `_process_file_with_ray`) + `_file_unchanged`, all moved verbatim from `core.py`. +- Create `codeanalyzer/pipeline/passes.py` — `home_external_symbols`, `pycg_call_graph_edges`, and the four `_pass_*` functions. +- Create `codeanalyzer/pipeline/pipeline.py` — the `AnalysisPipeline` fluent class. +- Modify `codeanalyzer/core.py` — slim `analyze()`; move the three helpers out; delete dead code. +- Modify `test/test_env_interpreter.py:151` — call the relocated `build_symbol_table`. +- Create `test/test_pipeline.py` — unit tests for the pipeline mechanics. +- Create `test/test_pipeline_equivalence.py` + `test/golden/pipeline_equivalence/*.json` — byte-for-byte characterization gate. + +--- + +### Task 1: Characterization golden pin (against current code) + +Locks current output BEFORE any refactor. Generated from the unmodified `analyze()`, committed, and asserted unchanged for the rest of the plan. + +**Files:** +- Create: `test/test_pipeline_equivalence.py` +- Create: `test/golden/pipeline_equivalence/*.json` (generated) + +**Interfaces:** +- Produces: nothing consumed by later tasks — it is a standalone regression gate that must stay green through Task 7. + +- [ ] **Step 1: Write the equivalence test** + +Create `test/test_pipeline_equivalence.py`: + +```python +"""Byte-for-byte characterization gate for the analysis pipeline refactor. + +Runs the CLI on copies of fixtures placed OUTSIDE any git tree (so +`repository_info` returns None and the output is deterministic), normalizes the +one volatile field (`analyzer.version`), and compares against committed goldens. +Regenerate with `REGEN=1 pytest test/test_pipeline_equivalence.py`. +""" +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + +import pytest + +FIXTURES = [ + "class_hierarchy", + "decorators_and_hof", + "async_patterns", + "method_call_resolution", +] +GOLDEN_DIR = Path(__file__).parent / "golden" / "pipeline_equivalence" +FIXTURE_ROOT = Path(__file__).parent / "fixtures" / "single_functionalities" + + +def _scalpel_available() -> bool: + try: + import scalpel # noqa: F401 + return True + except Exception: + return False + + +def _normalize(payload: dict) -> dict: + """Drop the only environment-volatile fields so the gate is stable across + version bumps and non-git run locations.""" + payload.get("application", {}).pop("repository", None) + analyzer = payload.get("analyzer") + if isinstance(analyzer, dict): + analyzer.pop("version", None) + return payload + + +def _run(proj: Path, level: int) -> dict: + out = subprocess.run( + [sys.executable, "-m", "codeanalyzer", "-i", str(proj), "-a", str(level), "--no-venv"], + capture_output=True, text=True, check=True, + ).stdout + return _normalize(json.loads(out)) + + +@pytest.mark.parametrize("fixture", FIXTURES) +@pytest.mark.parametrize("level", [1, 2, 3, 4]) +def test_pipeline_output_matches_golden(tmp_path, fixture, level): + if level == 4 and not _scalpel_available(): + pytest.skip("L4 golden requires python-scalpel (optional soft dependency)") + proj = tmp_path / fixture + shutil.copytree(FIXTURE_ROOT / fixture, proj) + got = _run(proj, level) + golden_path = GOLDEN_DIR / f"{fixture}.a{level}.json" + if os.environ.get("REGEN"): + GOLDEN_DIR.mkdir(parents=True, exist_ok=True) + golden_path.write_text(json.dumps(got, indent=2, sort_keys=True), encoding="utf-8") + return + assert golden_path.exists(), f"missing golden {golden_path}; regenerate with REGEN=1" + want = _normalize(json.loads(golden_path.read_text(encoding="utf-8"))) + assert got == want, f"{fixture} @ -a {level} diverged from golden" +``` + +- [ ] **Step 2: Generate the goldens from current code** + +Run: `REGEN=1 python -m pytest test/test_pipeline_equivalence.py -q` +Expected: PASS (regen branch returns before asserting); `test/golden/pipeline_equivalence/` now holds `*.a1.json … *.a4.json` (a4 files only if scalpel is installed). + +- [ ] **Step 3: Verify the gate passes against current code without REGEN** + +Run: `python -m pytest test/test_pipeline_equivalence.py -q` +Expected: PASS (a4 cases PASS or SKIP depending on scalpel). + +- [ ] **Step 4: Commit** + +```bash +git add test/test_pipeline_equivalence.py test/golden/pipeline_equivalence +git commit -m "test(pipeline): pin current analysis output as characterization goldens" +``` + +--- + +### Task 2: `AnalysisContext` dataclass + +**Files:** +- Create: `codeanalyzer/pipeline/__init__.py` +- Create: `codeanalyzer/pipeline/context.py` +- Test: `test/test_pipeline.py` + +**Interfaces:** +- Produces: `AnalysisContext(options, project_dir, virtualenv, analysis_level, app_name, cached_symbol_table={})` with mutable produced fields `symbol_table`, `app`, `sig_to_id`, `infos`, `ir` (all default `None`, except `cached_symbol_table` defaults to `{}`). + +- [ ] **Step 1: Write the failing test** + +Create `test/test_pipeline.py`: + +```python +from pathlib import Path + +from codeanalyzer.options import AnalysisOptions +from codeanalyzer.config import OutputFormat +from codeanalyzer.pipeline import AnalysisContext + + +def _opts(tmp_path): + return AnalysisOptions( + input=tmp_path, output=None, format=OutputFormat.JSON, + analysis_level=1, skip_tests=True, no_venv=True, + ) + + +def test_context_defaults(tmp_path): + ctx = AnalysisContext( + options=_opts(tmp_path), project_dir=Path(tmp_path), + virtualenv=None, analysis_level=1, app_name="proj", + ) + assert ctx.cached_symbol_table == {} + assert ctx.symbol_table is None + assert ctx.app is None + assert ctx.sig_to_id is None + assert ctx.infos is None + assert ctx.ir is None +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `python -m pytest test/test_pipeline.py::test_context_defaults -v` +Expected: FAIL with `ModuleNotFoundError: No module named 'codeanalyzer.pipeline'`. + +- [ ] **Step 3: Write the context and package exports** + +Create `codeanalyzer/pipeline/context.py`: + +```python +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Dict, Optional + +from codeanalyzer.options import AnalysisOptions +from codeanalyzer.schema import PyApplication, PyModule + + +@dataclass +class AnalysisContext: + """Mutable carrier threaded through the AnalysisPipeline passes. + + Inputs are set at construction; produced artifacts start as ``None`` and are + filled in chain order: ``symbol_table`` -> ``app``/``sig_to_id`` -> + ``infos`` -> ``ir``. ``infos`` (L3 PDGs) is deliberately reused by the L4 + pass, so its ordering in the chain matters. + """ + + # inputs + options: AnalysisOptions + project_dir: Path + virtualenv: Optional[Path] + analysis_level: int + app_name: str + cached_symbol_table: Dict[str, PyModule] = field(default_factory=dict) + + # produced by passes (loosely typed to avoid importing dataflow types here) + symbol_table: Optional[Dict[str, PyModule]] = None + app: Optional[PyApplication] = None + sig_to_id: Optional[Dict[str, str]] = None + infos: Optional[Dict[str, Any]] = None # Dict[str, FunctionInfo] + ir: Optional[Any] = None # ProgramGraphsIR +``` + +Create `codeanalyzer/pipeline/__init__.py`: + +```python +from codeanalyzer.pipeline.context import AnalysisContext +from codeanalyzer.pipeline.pipeline import AnalysisPipeline + +__all__ = ["AnalysisContext", "AnalysisPipeline"] +``` + +> Note: `__init__.py` imports `AnalysisPipeline` (created in Task 6). Until then this import fails, so for Task 2 temporarily export only `AnalysisContext`: +> +> ```python +> from codeanalyzer.pipeline.context import AnalysisContext +> +> __all__ = ["AnalysisContext"] +> ``` +> +> Task 6 restores the full `__init__.py` shown above. + +- [ ] **Step 4: Run test to verify it passes** + +Run: `python -m pytest test/test_pipeline.py::test_context_defaults -v` +Expected: PASS + +- [ ] **Step 5: Commit** + +```bash +git add codeanalyzer/pipeline/__init__.py codeanalyzer/pipeline/context.py test/test_pipeline.py +git commit -m "feat(pipeline): add AnalysisContext carrier for the pass chain" +``` + +--- + +### Task 3: Move the symbol-table builder out of `Codeanalyzer` + +Extract the 140-line `_build_symbol_table` (and its Ray helpers and `_file_unchanged`) into `pipeline/symbol_table.py` as free functions. `Codeanalyzer._build_symbol_table` becomes a one-line delegator so the existing `analyze()` and `test_env_interpreter.py` stay green. + +**Files:** +- Create: `codeanalyzer/pipeline/symbol_table.py` +- Modify: `codeanalyzer/core.py` (remove `_ensure_ray`, `_process_file_with_ray`, `_file_unchanged`; make `_build_symbol_table` delegate; drop `import ray`) +- Test: `test/test_pipeline.py` + +**Interfaces:** +- Produces: `build_symbol_table(project_dir: Path, virtualenv: Optional[Path], options: AnalysisOptions, cached_symbol_table: Dict[str, PyModule]) -> Dict[str, PyModule]` + +- [ ] **Step 1: Write the failing test** + +Append to `test/test_pipeline.py`: + +```python +from codeanalyzer.pipeline.symbol_table import build_symbol_table + + +def test_build_symbol_table_free_function(tmp_path): + proj = tmp_path / "proj" + proj.mkdir() + (proj / "m.py").write_text("def f():\n return 1\n", encoding="utf-8") + opts = AnalysisOptions( + input=proj, output=None, format=OutputFormat.JSON, + analysis_level=1, skip_tests=True, no_venv=True, + ) + table = build_symbol_table(proj, None, opts, cached_symbol_table={}) + assert "m.py" in table +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `python -m pytest test/test_pipeline.py::test_build_symbol_table_free_function -v` +Expected: FAIL with `ModuleNotFoundError: No module named 'codeanalyzer.pipeline.symbol_table'`. + +- [ ] **Step 3: Create `pipeline/symbol_table.py` by moving code from `core.py`** + +Move `_ensure_ray` (core.py:40-53), `_process_file_with_ray` (core.py:56-77), the body of `_build_symbol_table` (core.py:802-938), and `_file_unchanged` (core.py:758-785) into a new `codeanalyzer/pipeline/symbol_table.py`. Apply this substitution table to the moved `_build_symbol_table` body: + +| In `core._build_symbol_table` | In `build_symbol_table` | +| --- | --- | +| `self.file_name` | `options.file_name` | +| `self.project_dir` | `project_dir` | +| `self.virtualenv` | `virtualenv` | +| `self.rebuild_analysis` | `options.rebuild_analysis` | +| `self.skip_tests` | `options.skip_tests` | +| `self.using_ray` | `options.using_ray` | +| `self._file_unchanged(a, b)` | `_file_unchanged(a, b)` | + +`_ensure_ray` and `_process_file_with_ray` move verbatim (no `self`). `_file_unchanged` moves with its signature changing from `(self, file_path, cached_module)` to `(file_path, cached_module)`. + +The new module header and function signature: + +```python +import time +from pathlib import Path +from typing import Dict, Optional, Union + +import ray + +from codeanalyzer.options import AnalysisOptions +from codeanalyzer.schema import PyModule +from codeanalyzer.syntactic_analysis.exceptions import SymbolTableBuilderRayError +from codeanalyzer.syntactic_analysis.symbol_table_builder import SymbolTableBuilder +from codeanalyzer.utils import ProgressBar, logger + + +def _ensure_ray() -> None: + ... # moved verbatim from core.py:40-53 + + +@ray.remote +def _process_file_with_ray(py_file, project_dir, virtualenv) -> Dict[str, PyModule]: + ... # moved verbatim from core.py:56-77 + + +def _file_unchanged(file_path: Path, cached_module: PyModule) -> bool: + ... # moved from core.py:758-785, `self.` prefix removed + + +def build_symbol_table( + project_dir: Path, + virtualenv: Optional[Path], + options: AnalysisOptions, + cached_symbol_table: Optional[Dict[str, PyModule]] = None, +) -> Dict[str, PyModule]: + """Build the symbol table for the project (moved from Codeanalyzer).""" + if cached_symbol_table is None: + cached_symbol_table = {} + ... # moved body from core.py:815-938 with the substitution table applied +``` + +- [ ] **Step 4: Replace `core._build_symbol_table` with a delegator and delete the moved helpers** + +In `codeanalyzer/core.py`: delete the module-level `_ensure_ray` and `_process_file_with_ray`; delete the `_file_unchanged` method; delete `import ray` (now unused in core); replace the whole `_build_symbol_table` method body with: + +```python +def _build_symbol_table(self, cached_symbol_table=None): + from codeanalyzer.pipeline.symbol_table import build_symbol_table + return build_symbol_table( + self.project_dir, self.virtualenv, self.options, cached_symbol_table or {} + ) +``` + +- [ ] **Step 5: Run the moved-code and regression tests** + +Run: `python -m pytest test/test_pipeline.py::test_build_symbol_table_free_function test/test_symbol_table_builder.py test/test_env_interpreter.py test/test_pipeline_equivalence.py -q` +Expected: PASS (equivalence still green — behavior is unchanged; `test_env_interpreter` still calls `analyzer._build_symbol_table`, which now delegates). + +- [ ] **Step 6: Commit** + +```bash +git add codeanalyzer/pipeline/symbol_table.py codeanalyzer/core.py test/test_pipeline.py +git commit -m "refactor(pipeline): extract build_symbol_table as a free function" +``` + +--- + +### Task 4: Move the call-graph helpers out of `Codeanalyzer` + +Extract `_get_pycg_call_graph` and `_home_external_symbols` into `pipeline/passes.py` as free functions; the `Codeanalyzer` methods become delegators. + +**Files:** +- Create: `codeanalyzer/pipeline/passes.py` +- Modify: `codeanalyzer/core.py` (make `_get_pycg_call_graph` and `_home_external_symbols` delegate) +- Test: `test/test_pipeline.py` + +**Interfaces:** +- Produces: + - `home_external_symbols(app: PyApplication, app_id: str, sig_to_id: Dict[str, str]) -> Dict[str, PyExternalSymbol]` + - `pycg_call_graph_edges(project_dir: Path, symbol_table, jedi_edges, options: AnalysisOptions) -> List[PyCallEdge]` + +- [ ] **Step 1: Write the failing test** + +Append to `test/test_pipeline.py`: + +```python +from codeanalyzer.pipeline.passes import home_external_symbols +from codeanalyzer.schema import PyApplication + + +def test_home_external_symbols_homes_undeclared_endpoints(): + app = PyApplication.builder().symbol_table({}).call_graph([]).build() + app.id = "can://python/proj" + # a call edge whose endpoints are not declared callables + from codeanalyzer.schema.py_schema import PyCallEdge + app.call_graph = [PyCallEdge(src="a.b", dst="os.getcwd", prov=["jedi"], weight=1)] + sig_to_id = {} + externals = home_external_symbols(app, app.id, sig_to_id) + assert "can://python/proj/@external/os/getcwd" in externals + assert sig_to_id["os.getcwd"] == "can://python/proj/@external/os/getcwd" +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `python -m pytest test/test_pipeline.py::test_home_external_symbols_homes_undeclared_endpoints -v` +Expected: FAIL with `ModuleNotFoundError: No module named 'codeanalyzer.pipeline.passes'`. + +- [ ] **Step 3: Create `pipeline/passes.py` with the two helpers** + +Create `codeanalyzer/pipeline/passes.py`: + +```python +from pathlib import Path +from typing import Dict, List + +from codeanalyzer.options import AnalysisOptions +from codeanalyzer.schema import PyApplication, PyExternalSymbol +from codeanalyzer.schema.py_schema import PyCallEdge +from codeanalyzer.semantic_analysis.pycg import PyCG, PyCGExceptions +from codeanalyzer.utils import logger + + +def home_external_symbols(app, app_id, sig_to_id) -> Dict[str, PyExternalSymbol]: + """Home every call-graph endpoint that is not a declared callable onto a + ``can://…/@external//`` id. Moved verbatim from + ``Codeanalyzer._home_external_symbols`` (static method, no ``self``).""" + externals: Dict[str, PyExternalSymbol] = {} + for edge in app.call_graph: + for sig in (edge.src, edge.dst): + if sig in sig_to_id: + continue + module, name = sig.rsplit(".", 1) if "." in sig else (None, sig) + ext_id = f"{app_id}/@external/{module}/{name}" if module else \ + f"{app_id}/@external/{name}" + sig_to_id[sig] = ext_id + externals[ext_id] = PyExternalSymbol(id=ext_id, name=name, module=module) + return externals + + +def pycg_call_graph_edges(project_dir, symbol_table, jedi_edges, options) -> List[PyCallEdge]: + """Build PyCG-resolved call edges, degrading to Jedi-only on failure. Moved + from ``Codeanalyzer._get_pycg_call_graph`` (``self.X`` -> ``options.X`` / + ``project_dir``).""" + try: + pycg = PyCG( + project_dir, + skip_tests=options.skip_tests, + shard=options.pycg_shard, + shard_ceiling=options.pycg_shard_ceiling, + shard_timeout=options.pycg_shard_timeout, + shard_strategy=options.pycg_shard_strategy, + max_iter=options.pycg_max_iter, + using_ray=options.using_ray, + ) + return pycg.build_call_graph_edges(symbol_table, jedi_edges=jedi_edges) + except PyCGExceptions.PyCGImportError as exc: + logger.warning(f"PyCG not installed — level 2 edges will be Jedi-only: {exc}") + return [] + except PyCGExceptions.PyCGAnalysisError as exc: + logger.warning(f"PyCG analysis failed — level 2 edges will be Jedi-only: {exc}") + logger.debug("PyCG full traceback:", exc_info=True) + return [] +``` + +- [ ] **Step 4: Make the `core.py` methods delegate** + +In `codeanalyzer/core.py`, replace the bodies of `_get_pycg_call_graph` and `_home_external_symbols`: + +```python +def _get_pycg_call_graph(self, symbol_table, jedi_edges): + from codeanalyzer.pipeline.passes import pycg_call_graph_edges + return pycg_call_graph_edges(self.project_dir, symbol_table, jedi_edges, self.options) + +@staticmethod +def _home_external_symbols(app, app_id, sig_to_id): + from codeanalyzer.pipeline.passes import home_external_symbols + return home_external_symbols(app, app_id, sig_to_id) +``` + +- [ ] **Step 5: Run tests** + +Run: `python -m pytest test/test_pipeline.py test/test_pipeline_equivalence.py test/test_pycg_sharding.py -q` +Expected: PASS (equivalence still green). + +- [ ] **Step 6: Commit** + +```bash +git add codeanalyzer/pipeline/passes.py codeanalyzer/core.py test/test_pipeline.py +git commit -m "refactor(pipeline): extract pycg + external-symbol helpers as free functions" +``` + +--- + +### Task 5: The four pass functions + +Add the four `_pass_*(ctx)` functions to `pipeline/passes.py`. Each mutates the context, asserts its preconditions, and mirrors exactly the work today's `analyze()` does for that subsystem. + +**Files:** +- Modify: `codeanalyzer/pipeline/passes.py` +- Test: `test/test_pipeline.py` + +**Interfaces:** +- Consumes: `AnalysisContext`; `build_symbol_table` (Task 3); `home_external_symbols`, `pycg_call_graph_edges` (Task 4). +- Produces: `_pass_symbol_table(ctx)`, `_pass_call_graph(ctx)`, `_pass_intraproc_dataflow(ctx)`, `_pass_interproc_dataflow(ctx)` — each returns `None` and mutates `ctx`. + +- [ ] **Step 1: Write the failing tests** + +Append to `test/test_pipeline.py`: + +```python +from pathlib import Path + +from codeanalyzer.pipeline import AnalysisContext +from codeanalyzer.pipeline.passes import ( + _pass_symbol_table, _pass_call_graph, + _pass_intraproc_dataflow, _pass_interproc_dataflow, +) + + +def _ctx(tmp_path, level, src="def g():\n return 1\ndef f():\n return g()\n"): + proj = tmp_path / "proj" + proj.mkdir() + (proj / "m.py").write_text(src, encoding="utf-8") + opts = AnalysisOptions( + input=proj, output=None, format=OutputFormat.JSON, + analysis_level=level, skip_tests=True, no_venv=True, + ) + return AnalysisContext( + options=opts, project_dir=proj, virtualenv=None, + analysis_level=level, app_name="proj", + ) + + +def test_pass_symbol_table_populates_symbol_table(tmp_path): + ctx = _ctx(tmp_path, 1) + _pass_symbol_table(ctx) + assert ctx.symbol_table is not None and "m.py" in ctx.symbol_table + + +def test_pass_call_graph_populates_app_and_ids(tmp_path): + ctx = _ctx(tmp_path, 1) + _pass_symbol_table(ctx) + _pass_call_graph(ctx) + assert ctx.app is not None and ctx.sig_to_id is not None + assert ctx.app.id.startswith("can://python/proj") + + +def test_pass_call_graph_requires_symbol_table(tmp_path): + ctx = _ctx(tmp_path, 1) + with pytest.raises(AssertionError): + _pass_call_graph(ctx) + + +def test_pass_interproc_requires_infos(tmp_path): + ctx = _ctx(tmp_path, 4) + _pass_symbol_table(ctx) + _pass_call_graph(ctx) + # intraproc pass deliberately skipped -> infos is still None + with pytest.raises(AssertionError): + _pass_interproc_dataflow(ctx) + + +def test_pass_intraproc_populates_infos(tmp_path): + ctx = _ctx(tmp_path, 3, src="def g(x):\n return x\ndef f(a):\n b = a\n g(b)\n return b\n") + _pass_symbol_table(ctx) + _pass_call_graph(ctx) + _pass_intraproc_dataflow(ctx) + assert ctx.infos is not None +``` + +Add `import pytest` at the top of `test/test_pipeline.py` if not already present. + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `python -m pytest test/test_pipeline.py -k "pass_" -v` +Expected: FAIL with `ImportError: cannot import name '_pass_symbol_table'`. + +- [ ] **Step 3: Add the four pass functions** + +Append to `codeanalyzer/pipeline/passes.py` (add these imports at the top of the file alongside the existing ones): + +```python +from codeanalyzer.pipeline.context import AnalysisContext +from codeanalyzer.pipeline.symbol_table import build_symbol_table +from codeanalyzer.provenance import repository_info +from codeanalyzer.schema.assign_ids import assign_ids +from codeanalyzer.schema.call_graph_ids import reidentify_call_graph +from codeanalyzer.schema.l1_body import populate_l1_body +from codeanalyzer.schema.l2_callees import backfill_callees +from codeanalyzer.semantic_analysis.call_graph import ( + filter_external_edges, jedi_call_graph_edges, merge_edges, + resolve_unresolved_constructors, +) +from codeanalyzer.syntactic_analysis.import_resolver import resolve_imports +``` + +Then the pass functions: + +```python +def _pass_symbol_table(ctx: AnalysisContext) -> None: + symbol_table = build_symbol_table( + ctx.project_dir, ctx.virtualenv, ctx.options, ctx.cached_symbol_table + ) + resolve_unresolved_constructors(symbol_table) + ctx.symbol_table = symbol_table + + +def _pass_call_graph(ctx: AnalysisContext) -> None: + assert ctx.symbol_table is not None, "call_graph pass requires the symbol-table pass" + st = ctx.symbol_table + + call_graph = list(jedi_call_graph_edges(st)) + if ctx.analysis_level >= 2: + pycg_edges = pycg_call_graph_edges( + ctx.project_dir, st, call_graph, ctx.options + ) + call_graph = merge_edges(call_graph, pycg_edges) + call_graph = filter_external_edges(call_graph, st) + call_graph.sort(key=lambda e: (e.src, e.dst)) + + app = PyApplication.builder().symbol_table(st).call_graph(call_graph).build() + resolve_imports(app, ctx.project_dir) + app.repository = repository_info(ctx.project_dir) + + sig_to_id = assign_ids(app, ctx.app_name) + app.external_symbols = home_external_symbols(app, app.id, sig_to_id) + populate_l1_body(app) + if ctx.analysis_level >= 2: + backfill_callees(app, sig_to_id) + reidentify_call_graph(app, sig_to_id) + + ctx.app = app + ctx.sig_to_id = sig_to_id + + +def _pass_intraproc_dataflow(ctx: AnalysisContext) -> None: + assert ctx.app is not None and ctx.sig_to_id is not None, \ + "intraproc dataflow pass requires the call-graph pass" + from codeanalyzer.dataflow.builder import build_function_pdgs, emit_l3_body + from codeanalyzer.dataflow.syntactic import SyntacticOracle + + infos, _func_asts = build_function_pdgs( + ctx.app, + k=ctx.options.graph_field_depth, + oracle_factory=lambda c, fast: SyntacticOracle(), + ) + emit_l3_body(ctx.app, infos, ctx.sig_to_id, set(ctx.options.graphs.split(","))) + ctx.infos = infos + + +def _pass_interproc_dataflow(ctx: AnalysisContext) -> None: + assert ctx.app is not None and ctx.sig_to_id is not None, \ + "interproc dataflow pass requires the call-graph pass" + assert ctx.infos is not None, \ + "interproc dataflow pass requires the intraproc pass (it reuses its PDGs)" + from codeanalyzer.dataflow.builder import ( + _base_types, build_program_graphs, emit_ddg_pointsto_delta, emit_l4, + ) + from codeanalyzer.dataflow.scalpel_oracle import make_alias_oracle + + ir = build_program_graphs( + ctx.app, + k=ctx.options.graph_field_depth, + oracle_factory=lambda c, fast: make_alias_oracle(c, fast, _base_types(c)), + ) + emit_l4(ctx.app, ir, ctx.sig_to_id) + emit_ddg_pointsto_delta(ctx.app, ctx.infos, ir, ctx.sig_to_id) + ctx.ir = ir +``` + +> Note: `jedi_call_graph_edges` returns the L1 edge list; today's `analyze()` passes those same Jedi edges into PyCG as the coupling graph (`jedi_edges=call_graph` here, before PyCG merges into `call_graph`). Passing the pre-merge `call_graph` list preserves that exactly. + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `python -m pytest test/test_pipeline.py -k "pass_" -v` +Expected: PASS + +- [ ] **Step 5: Commit** + +```bash +git add codeanalyzer/pipeline/passes.py test/test_pipeline.py +git commit -m "feat(pipeline): add the four analysis pass functions" +``` + +--- + +### Task 6: The `AnalysisPipeline` fluent class + +**Files:** +- Create: `codeanalyzer/pipeline/pipeline.py` +- Modify: `codeanalyzer/pipeline/__init__.py` (restore the full export) +- Test: `test/test_pipeline.py` + +**Interfaces:** +- Consumes: the four `_pass_*` functions (Task 5), `AnalysisContext` (Task 2), `analyzer_info` (`provenance`), `Analysis` (`schema`). +- Produces: `AnalysisPipeline(ctx)` with `.with_symbol_table()`, `.with_call_graph()`, `.with_intraproc_dataflow()`, `.with_interproc_dataflow()` (each returns `self`) and `.build() -> Analysis`. + +- [ ] **Step 1: Write the failing tests** + +Append to `test/test_pipeline.py`: + +```python +from codeanalyzer.pipeline import AnalysisPipeline +from codeanalyzer.schema import Analysis + + +def test_pipeline_gating_skips_dataflow_below_level(tmp_path): + ctx = _ctx(tmp_path, 2) + analysis = ( + AnalysisPipeline(ctx) + .with_symbol_table() + .with_call_graph() + .with_intraproc_dataflow() # min_level 3 -> skipped at level 2 + .with_interproc_dataflow() # min_level 4 -> skipped at level 2 + .build() + ) + assert isinstance(analysis, Analysis) + assert ctx.infos is None and ctx.ir is None # gates fired + assert analysis.max_level == 2 + assert analysis.k_limit is None # L3+ only + + +def test_pipeline_with_methods_return_self(tmp_path): + ctx = _ctx(tmp_path, 1) + pipe = AnalysisPipeline(ctx) + assert pipe.with_symbol_table() is pipe + + +def test_pipeline_level4_runs_intraproc_before_interproc(tmp_path): + ctx = _ctx(tmp_path, 4, src="def g(x):\n return x\ndef f(a):\n b = g(a)\n return b\n") + (AnalysisPipeline(ctx) + .with_symbol_table().with_call_graph() + .with_intraproc_dataflow().with_interproc_dataflow().build()) + assert ctx.infos is not None # L3 ran before L4 (reuse precondition held) + assert ctx.ir is not None +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `python -m pytest test/test_pipeline.py -k pipeline -v` +Expected: FAIL with `ImportError: cannot import name 'AnalysisPipeline'`. + +- [ ] **Step 3: Write the pipeline and restore the package export** + +Create `codeanalyzer/pipeline/pipeline.py`: + +```python +import time + +from codeanalyzer.pipeline.context import AnalysisContext +from codeanalyzer.pipeline.passes import ( + _pass_call_graph, _pass_interproc_dataflow, + _pass_intraproc_dataflow, _pass_symbol_table, +) +from codeanalyzer.provenance import analyzer_info +from codeanalyzer.schema import Analysis +from codeanalyzer.utils import logger + + +class AnalysisPipeline: + """Fluent chain of analysis passes over a shared AnalysisContext. + + Each ``.with_*`` runs one pass through the shared ``_run`` gate: a pass + below its intrinsic ``min_level`` is a logged no-op. ``.build()`` assembles + the ``Analysis`` envelope from the produced context. + """ + + def __init__(self, ctx: AnalysisContext): + self.ctx = ctx + + def with_symbol_table(self): + return self._run("symbol_table", 1, _pass_symbol_table) + + def with_call_graph(self): + return self._run("call_graph", 1, _pass_call_graph) + + def with_intraproc_dataflow(self): + return self._run("intraproc_dataflow", 3, _pass_intraproc_dataflow) + + def with_interproc_dataflow(self): + return self._run("interproc_dataflow", 4, _pass_interproc_dataflow) + + def _run(self, name, min_level, fn): + if self.ctx.analysis_level < min_level: + logger.info("⏭️ %s: skipped (level %d < %d)", name, + self.ctx.analysis_level, min_level) + return self + t0 = time.perf_counter() + fn(self.ctx) + logger.info("✅ %s: %.1fs", name, time.perf_counter() - t0) + return self + + def build(self) -> Analysis: + return Analysis( + max_level=self.ctx.analysis_level, + k_limit=self.ctx.options.graph_field_depth + if self.ctx.analysis_level >= 3 else None, + analyzer=analyzer_info(self.ctx.analysis_level), + application=self.ctx.app, + ) +``` + +Restore `codeanalyzer/pipeline/__init__.py` to the full export: + +```python +from codeanalyzer.pipeline.context import AnalysisContext +from codeanalyzer.pipeline.pipeline import AnalysisPipeline + +__all__ = ["AnalysisContext", "AnalysisPipeline"] +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `python -m pytest test/test_pipeline.py -q` +Expected: PASS (all pipeline unit tests green). + +- [ ] **Step 5: Commit** + +```bash +git add codeanalyzer/pipeline/pipeline.py codeanalyzer/pipeline/__init__.py test/test_pipeline.py +git commit -m "feat(pipeline): add the fluent AnalysisPipeline with self-gating passes" +``` + +--- + +### Task 7: Flip `Codeanalyzer.analyze()` to the pipeline and remove dead code + +The behavior-preserving swap. `analyze()` becomes a thin wrapper; the three delegating methods are deleted; the one direct test consumer is updated. The equivalence goldens and the full suite are the gate. + +**Files:** +- Modify: `codeanalyzer/core.py` (rewrite `analyze()`; delete `_build_symbol_table`, `_get_pycg_call_graph`, `_home_external_symbols`; adjust imports) +- Modify: `test/test_env_interpreter.py:151` +- Modify: `codeanalyzer/dataflow/scalpel_oracle.py` and `codeanalyzer/semantic_analysis/pycg/pycg_analysis.py` (prose comment references only) + +**Interfaces:** +- Consumes: `AnalysisContext`, `AnalysisPipeline` from `codeanalyzer.pipeline`. +- Produces: unchanged `Codeanalyzer.analyze() -> Analysis`. + +- [ ] **Step 1: Rewrite `analyze()`** + +In `codeanalyzer/core.py`, add near the top-level imports: + +```python +from codeanalyzer.pipeline import AnalysisContext, AnalysisPipeline +``` + +Replace the entire `analyze()` method (core.py:560-691) with: + +```python +def analyze(self) -> Analysis: + """Analyze the project and return the v2 ``Analysis`` envelope. + + Loads any cache seed, runs the fluent AnalysisPipeline, and persists the + result. The per-level work lives in the pipeline passes. + """ + cache_file = self.cache_dir / "analysis_cache.json" + + cached = None + if not self.rebuild_analysis and cache_file.exists(): + try: + cached = self._load_pyapplication_from_cache(cache_file) + if cached is not None: + logger.info("Loaded cached analysis") + except Exception as e: + logger.warning(f"Failed to load cache: {e}. Rebuilding analysis.") + cached = None + + if not self._cache_analyzer_matches(cached, analyzer_info(self.analysis_level).version): + if cached is not None: + logger.info("Analysis cache written by a different analyzer version; rebuilding.") + cached = None + + ctx = AnalysisContext( + options=self.options, + project_dir=self.project_dir, + virtualenv=self.virtualenv, + analysis_level=self.analysis_level, + app_name=self.options.app_name or self.project_dir.name, + cached_symbol_table=cached.application.symbol_table if cached else {}, + ) + + analysis = ( + AnalysisPipeline(ctx) + .with_symbol_table() + .with_call_graph() + .with_intraproc_dataflow() + .with_interproc_dataflow() + .build() + ) + + self._save_analysis_cache(analysis, cache_file) + return analysis +``` + +- [ ] **Step 2: Delete the now-unused delegating methods and imports** + +In `codeanalyzer/core.py`: delete the `_build_symbol_table`, `_get_pycg_call_graph`, and `_home_external_symbols` methods (the delegators from Tasks 3–4). Remove any imports left unused by the rewrite — check with `python -c "import ast,sys; ..."` or simply run `ruff`/`pyflakes` if available. Candidates now unused in `core.py`: `PyExternalSymbol`, `PyCallEdge`, `filter_external_edges`, `jedi_call_graph_edges`, `merge_edges`, `resolve_unresolved_constructors`, `PyCG`, `PyCGExceptions`, `resolve_imports`, `SymbolTableBuilder`, `ProgressBar`, `assign_ids`, `populate_l1_body`, `backfill_callees`, `reidentify_call_graph`, `repository_info`. Keep `analyzer_info` (used in the cache-version check), `Analysis`, `PyApplication`, `PyModule`, `model_dump_json`, `model_validate_json` (still used by cache load/save). + +> Do NOT delete `_load_pyapplication_from_cache`, `_save_analysis_cache`, `_cache_analyzer_matches`, `_compute_checksum`, `__enter__`, `__exit__`, or the interpreter/venv helpers — they stay on `Codeanalyzer`. + +- [ ] **Step 3: Update the one direct test consumer** + +In `test/test_env_interpreter.py`, replace line 151: + +```python + table = analyzer._build_symbol_table(cached_symbol_table={}) +``` + +with: + +```python + from codeanalyzer.pipeline.symbol_table import build_symbol_table + table = build_symbol_table( + analyzer.project_dir, analyzer.virtualenv, analyzer.options, + cached_symbol_table={}, + ) +``` + +- [ ] **Step 4: Fix the two stale prose comment references** + +In `codeanalyzer/dataflow/scalpel_oracle.py:256`, change the reference `core._get_pycg_call_graph` to `pipeline.passes.pycg_call_graph_edges`. In `codeanalyzer/semantic_analysis/pycg/pycg_analysis.py:497`, change `core.py's _build_symbol_table` to `pipeline.symbol_table.build_symbol_table`. These are comments only — no behavior change. + +- [ ] **Step 5: Run the full suite + equivalence gate** + +Run: `python -m pytest test/ -q` +Expected: PASS. Specifically confirm green: `test_pipeline_equivalence.py` (byte-identical output), `test_v2_superset.py` (monotonicity), `test_v2_l1_body/l2/l3/l4`, `test_dataflow_*`, `test_v2_cache`, `test_v2_two_projection_agreement`, `test_neo4j_schema`, `test_cli`, `test_env_interpreter`. + +- [ ] **Step 6: Verify end-to-end by running the CLI at each level** + +Run: +```bash +for L in 1 2 3 4; do + python -m codeanalyzer -i test/fixtures/single_functionalities/class_hierarchy -a $L --no-venv -vv -o /tmp/canpy_verify_a$L 2>&1 | grep -E "symbol_table|call_graph|intraproc|interproc|skipped" +done +``` +Expected: level 1 logs `✅ symbol_table` and `✅ call_graph`, then `⏭️ intraproc_dataflow: skipped (level 1 < 3)` and `⏭️ interproc_dataflow: skipped (level 1 < 4)`; level 3 runs intraproc and skips interproc; level 4 runs all four. Each `/tmp/canpy_verify_a$L/analysis.json` is written. + +- [ ] **Step 7: Commit** + +```bash +git add codeanalyzer/core.py test/test_env_interpreter.py \ + codeanalyzer/dataflow/scalpel_oracle.py \ + codeanalyzer/semantic_analysis/pycg/pycg_analysis.py +git commit -m "refactor(core): drive analyze() through the fluent AnalysisPipeline" +``` + +--- + +## Self-Review + +**1. Spec coverage:** +- Fluent `.with_*()` chain over a shared context → Tasks 2, 5, 6, 7. ✓ +- Four subsystem-grouped passes → Task 5. ✓ +- Self-gating on `min_level`, no `skip` arg → Task 6 (`_run`). ✓ +- Internal L2 gates kept inside `_pass_call_graph` → Task 5 (`if ctx.analysis_level >= 2:`). ✓ +- Caching/venv stay at the edge in `Codeanalyzer` → Task 7 (`analyze()` keeps cache load/save; `__enter__`/`__exit__` untouched). ✓ +- Three helpers move off `Codeanalyzer` (callers updated, no shim) → Tasks 3, 4, 7. ✓ +- Byte-identical output / monotonicity gate → Task 1 (goldens) + Task 7 Step 5. ✓ +- New unit tests (`_run` gating, passes in isolation, preconditions, self-gating reproduces old gates, L3→L4 reuse ordering) → Tasks 2, 5, 6. ✓ +- Helper-move regression (`test_env_interpreter.py`, prose comments) → Task 7 Steps 3–4. ✓ + +**2. Placeholder scan:** The `... # moved verbatim` markers in Task 3 Step 3 are explicit relocation instructions with exact source line ranges and a substitution table, not unfinished work — the engineer copies named, line-referenced blocks. No `TBD`/`TODO`/"handle edge cases". + +**3. Type consistency:** `build_symbol_table(project_dir, virtualenv, options, cached_symbol_table)` — defined Task 3, called Task 5 (`_pass_symbol_table`) and Task 7 (`test_env_interpreter`), consistent. `pycg_call_graph_edges(project_dir, symbol_table, jedi_edges, options)` — defined Task 4, called Task 5, consistent. `home_external_symbols(app, app_id, sig_to_id)` — defined Task 4, called Task 5, consistent. `_pass_*` names match between Tasks 5 and 6. `AnalysisContext` field names (`symbol_table`, `app`, `sig_to_id`, `infos`, `ir`) consistent across Tasks 2, 5, 6, 7. `min_level` values (1, 1, 3, 4) match the spec table. From 94478ebb9c58e6d313a7efecdcbab770a4e83cd3 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 11:12:39 -0400 Subject: [PATCH 03/14] test(pipeline): pin current analysis output as characterization goldens --- .../async_patterns.a1.json | 2671 ++++++ .../async_patterns.a2.json | 2692 ++++++ .../async_patterns.a3.json | 4413 ++++++++++ .../class_hierarchy.a1.json | 4864 +++++++++++ .../class_hierarchy.a2.json | 4898 +++++++++++ .../class_hierarchy.a3.json | 7450 +++++++++++++++++ .../decorators_and_hof.a1.json | 3362 ++++++++ .../decorators_and_hof.a2.json | 3376 ++++++++ .../decorators_and_hof.a3.json | 5709 +++++++++++++ .../method_call_resolution.a1.json | 653 ++ .../method_call_resolution.a2.json | 658 ++ .../method_call_resolution.a3.json | 946 +++ test/test_pipeline_equivalence.py | 87 + 13 files changed, 41779 insertions(+) create mode 100644 test/golden/pipeline_equivalence/async_patterns.a1.json create mode 100644 test/golden/pipeline_equivalence/async_patterns.a2.json create mode 100644 test/golden/pipeline_equivalence/async_patterns.a3.json create mode 100644 test/golden/pipeline_equivalence/class_hierarchy.a1.json create mode 100644 test/golden/pipeline_equivalence/class_hierarchy.a2.json create mode 100644 test/golden/pipeline_equivalence/class_hierarchy.a3.json create mode 100644 test/golden/pipeline_equivalence/decorators_and_hof.a1.json create mode 100644 test/golden/pipeline_equivalence/decorators_and_hof.a2.json create mode 100644 test/golden/pipeline_equivalence/decorators_and_hof.a3.json create mode 100644 test/golden/pipeline_equivalence/method_call_resolution.a1.json create mode 100644 test/golden/pipeline_equivalence/method_call_resolution.a2.json create mode 100644 test/golden/pipeline_equivalence/method_call_resolution.a3.json create mode 100644 test/test_pipeline_equivalence.py diff --git a/test/golden/pipeline_equivalence/async_patterns.a1.json b/test/golden/pipeline_equivalence/async_patterns.a1.json new file mode 100644 index 0000000..10bb804 --- /dev/null +++ b/test/golden/pipeline_equivalence/async_patterns.a1.json @@ -0,0 +1,2671 @@ +{ + "analyzer": { + "config": { + "analysis_level": 1 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/AsyncResource/__aenter__(self)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/AsyncResource/__aexit__(self,exc_type,exc_val,exc_tb)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.RuntimeError/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/async_main()", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.list/append", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/gather", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/fetch_all(urls)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/process_url(url)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/fetch_all(urls)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/fetch_data(url)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins/len", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.range/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.runners/run", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/async_main()", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/fetch_all(urls)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/read_resource(name)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.str/upper", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/process_url(url)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/fetch_data(url)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/process_url(url)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/AsyncResource/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/read_resource(name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/read_resource(name)", + "weight": 1 + } + ], + "external_symbols": { + "can://python/async_patterns/@external/asyncio.runners/run": { + "id": "can://python/async_patterns/@external/asyncio.runners/run", + "kind": "external", + "module": "asyncio.runners", + "name": "run" + }, + "can://python/async_patterns/@external/asyncio.tasks/gather": { + "id": "can://python/async_patterns/@external/asyncio.tasks/gather", + "kind": "external", + "module": "asyncio.tasks", + "name": "gather" + }, + "can://python/async_patterns/@external/asyncio.tasks/sleep": { + "id": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "external", + "module": "asyncio.tasks", + "name": "sleep" + }, + "can://python/async_patterns/@external/builtins.RuntimeError/__init__": { + "id": "can://python/async_patterns/@external/builtins.RuntimeError/__init__", + "kind": "external", + "module": "builtins.RuntimeError", + "name": "__init__" + }, + "can://python/async_patterns/@external/builtins.list/append": { + "id": "can://python/async_patterns/@external/builtins.list/append", + "kind": "external", + "module": "builtins.list", + "name": "append" + }, + "can://python/async_patterns/@external/builtins.range/__init__": { + "id": "can://python/async_patterns/@external/builtins.range/__init__", + "kind": "external", + "module": "builtins.range", + "name": "__init__" + }, + "can://python/async_patterns/@external/builtins.str/upper": { + "id": "can://python/async_patterns/@external/builtins.str/upper", + "kind": "external", + "module": "builtins.str", + "name": "upper" + }, + "can://python/async_patterns/@external/builtins/len": { + "id": "can://python/async_patterns/@external/builtins/len", + "kind": "external", + "module": "builtins", + "name": "len" + } + }, + "id": "can://python/async_patterns", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [ + { + "content": "Async/await patterns.\n\nExercises:\n- async def + await\n- asyncio.gather (concurrent tasks)\n- Async generator (async def + yield)\n- async for loop consuming an async generator\n- Async context manager (__aenter__ / __aexit__)\n- async with block\n- Nested awaits / task composition\n", + "end_column": 3, + "end_line": 11, + "is_docstring": true, + "start_column": 0, + "start_line": 1 + } + ], + "content_hash": "3b6eb8773ed5b7309052bb555ad62c5505210c2bbcae17902924b136bfa7d6e0", + "file_size": 3784, + "functions": { + "async_main": { + "accessed_symbols": [ + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 113, + "name": "dict", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "function", + "lineno": 119, + "name": "pipeline", + "qualified_name": "main.pipeline", + "scope": "local", + "type": "pipeline" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 119, + "name": "urls", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + } + ], + "body": { + "119:17": { + "kind": "call", + "span": { + "bytes": [ + 3649, + 3692 + ], + "end": [ + 119, + 60 + ], + "start": [ + 119, + 17 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "list" + } + ], + "callee_signature": "main.pipeline", + "end_column": 60, + "end_line": 119, + "is_constructor_call": false, + "method_name": "pipeline", + "return_type": "Coroutine", + "start_column": 17, + "start_line": 119 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 114, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 119, + "id": "can://python/async_patterns/main.py/async_main()", + "kind": "function", + "local_variables": [ + { + "end_column": 8, + "end_line": 118, + "initializer": "['http://example.com/a', 'http://example.com/b', 'http://example.com/c']", + "name": "urls", + "scope": "function", + "start_column": 4, + "start_line": 114, + "type": "list" + } + ], + "name": "async_main", + "parameters": [], + "return_type": "dict", + "signature": "main.async_main", + "span": { + "bytes": [ + 3485, + 3692 + ], + "end": [ + 119, + 60 + ], + "start": [ + 113, + 0 + ] + }, + "start_line": 113, + "summary": [], + "types": {} + }, + "collect_chunks": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 57, + "name": "chunks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 50, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 55, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 42, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "class", + "lineno": 54, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "class", + "lineno": 54, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "function", + "lineno": 55, + "name": "generate_chunks", + "qualified_name": "main.generate_chunks", + "scope": "local", + "type": "generate_chunks" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 55, + "name": "data", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 45, + "is_builtin": false, + "kind": "variable", + "lineno": 55, + "name": "size", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 22, + "is_builtin": false, + "kind": "variable", + "lineno": 56, + "name": "chunk", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 56, + "name": "chunks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + } + ], + "body": { + "55:23": { + "kind": "call", + "span": { + "bytes": [ + 1727, + 1754 + ], + "end": [ + 55, + 50 + ], + "start": [ + 55, + 23 + ] + } + }, + "56:8": { + "kind": "call", + "span": { + "bytes": [ + 1764, + 1784 + ], + "end": [ + 56, + 28 + ], + "start": [ + 56, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + }, + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "main.generate_chunks", + "end_column": 50, + "end_line": 55, + "is_constructor_call": false, + "method_name": "generate_chunks", + "return_type": "AsyncGenerator", + "start_column": 23, + "start_line": 55 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.list.append", + "end_column": 28, + "end_line": 56, + "is_constructor_call": false, + "method_name": "append", + "receiver_expr": "chunks", + "receiver_type": "list", + "start_column": 8, + "start_line": 56 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 54, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 57, + "id": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "kind": "function", + "local_variables": [ + { + "end_column": 10, + "end_line": 54, + "initializer": "[]", + "name": "chunks", + "scope": "function", + "start_column": 4, + "start_line": 54, + "type": "List[str]" + } + ], + "name": "collect_chunks", + "parameters": [ + { + "end_column": 34, + "end_line": 53, + "name": "data", + "start_column": 25, + "start_line": 53, + "type": "str" + }, + { + "end_column": 45, + "end_line": 53, + "name": "size", + "start_column": 36, + "start_line": 53, + "type": "int" + } + ], + "return_type": "List[str]", + "signature": "main.collect_chunks", + "span": { + "bytes": [ + 1616, + 1802 + ], + "end": [ + 57, + 17 + ], + "start": [ + 53, + 0 + ] + }, + "start_line": 53, + "summary": [], + "types": {} + }, + "fetch_all": { + "accessed_symbols": [ + { + "col_offset": 40, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 45, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 13, + "is_builtin": false, + "kind": "function", + "lineno": 35, + "name": "process_url", + "qualified_name": "main.process_url", + "scope": "local", + "type": "process_url" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "url", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "urls", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "module", + "lineno": 36, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "tasks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + } + ], + "body": { + "35:13": { + "kind": "call", + "span": { + "bytes": [ + 977, + 993 + ], + "end": [ + 35, + 29 + ], + "start": [ + 35, + 13 + ] + } + }, + "36:17": { + "kind": "call", + "span": { + "bytes": [ + 1028, + 1050 + ], + "end": [ + 36, + 39 + ], + "start": [ + 36, + 17 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.process_url", + "end_column": 29, + "end_line": 35, + "is_constructor_call": false, + "method_name": "process_url", + "return_type": "Coroutine", + "start_column": 13, + "start_line": 35 + }, + { + "argument_types": [ + "Future" + ], + "arguments": [ + { + "ast_kind": "Starred", + "inferred_type": "Future" + } + ], + "callee_signature": "asyncio.tasks.gather", + "end_column": 39, + "end_line": 36, + "is_constructor_call": false, + "method_name": "gather", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Future", + "start_column": 17, + "start_line": 36 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 35, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 36, + "id": "can://python/async_patterns/main.py/fetch_all(urls)", + "kind": "function", + "local_variables": [ + { + "end_column": 9, + "end_line": 35, + "initializer": "[process_url(url) for url in urls]", + "name": "tasks", + "scope": "function", + "start_column": 4, + "start_line": 35, + "type": "list" + } + ], + "name": "fetch_all", + "parameters": [ + { + "end_column": 35, + "end_line": 34, + "name": "urls", + "start_column": 20, + "start_line": 34, + "type": "List[str]" + } + ], + "return_type": "List[str]", + "signature": "main.fetch_all", + "span": { + "bytes": [ + 913, + 1050 + ], + "end": [ + 36, + 39 + ], + "start": [ + 34, + 0 + ] + }, + "start_line": 34, + "summary": [], + "types": {} + }, + "fetch_data": { + "accessed_symbols": [ + { + "col_offset": 34, + "is_builtin": false, + "kind": "class", + "lineno": 20, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 20, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "url", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 10, + "is_builtin": false, + "kind": "module", + "lineno": 21, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + } + ], + "body": { + "21:10": { + "kind": "call", + "span": { + "bytes": [ + 573, + 589 + ], + "end": [ + 21, + 26 + ], + "start": [ + 21, + 10 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 26, + "end_line": 21, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 10, + "start_line": 21 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 21, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 22, + "id": "can://python/async_patterns/main.py/fetch_data(url)", + "kind": "function", + "local_variables": [], + "name": "fetch_data", + "parameters": [ + { + "end_column": 29, + "end_line": 20, + "name": "url", + "start_column": 21, + "start_line": 20, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.fetch_data", + "span": { + "bytes": [ + 524, + 614 + ], + "end": [ + 22, + 24 + ], + "start": [ + 20, + 0 + ] + }, + "start_line": 20, + "summary": [], + "types": {} + }, + "generate_chunks": { + "accessed_symbols": [ + { + "col_offset": 51, + "is_builtin": false, + "kind": "variable", + "lineno": 43, + "name": "AsyncGenerator", + "qualified_name": "typing._SpecialGenericAlias", + "scope": "local", + "type": "_SpecialGenericAlias" + }, + { + "col_offset": 32, + "is_builtin": false, + "kind": "class", + "lineno": 43, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 43, + "is_builtin": false, + "kind": "class", + "lineno": 43, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 13, + "is_builtin": false, + "kind": "class", + "lineno": 44, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 44, + "name": "size", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 66, + "is_builtin": false, + "kind": "class", + "lineno": 43, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 22, + "is_builtin": false, + "kind": "function", + "lineno": 44, + "name": "len", + "qualified_name": "builtins.len", + "scope": "local", + "type": "len" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 44, + "name": "data", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "data", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "module", + "lineno": 45, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "i", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "i", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "size", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "44:13": { + "kind": "call", + "span": { + "bytes": [ + 1322, + 1347 + ], + "end": [ + 44, + 38 + ], + "start": [ + 44, + 13 + ] + } + }, + "44:22": { + "kind": "call", + "span": { + "bytes": [ + 1331, + 1340 + ], + "end": [ + 44, + 31 + ], + "start": [ + 44, + 22 + ] + } + }, + "45:14": { + "kind": "call", + "span": { + "bytes": [ + 1363, + 1379 + ], + "end": [ + 45, + 30 + ], + "start": [ + 45, + 14 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int", + "len", + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + }, + { + "ast_kind": "Call", + "inferred_type": "len" + }, + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "builtins.range.__init__", + "end_column": 38, + "end_line": 44, + "is_constructor_call": true, + "method_name": "range", + "return_type": "range", + "start_column": 13, + "start_line": 44 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.len", + "end_column": 31, + "end_line": 44, + "is_constructor_call": false, + "method_name": "len", + "return_type": "int", + "start_column": 22, + "start_line": 44 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 30, + "end_line": 45, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 14, + "start_line": 45 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 44, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 46, + "id": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "kind": "function", + "local_variables": [], + "name": "generate_chunks", + "parameters": [ + { + "end_column": 35, + "end_line": 43, + "name": "data", + "start_column": 26, + "start_line": 43, + "type": "str" + }, + { + "end_column": 46, + "end_line": 43, + "name": "size", + "start_column": 37, + "start_line": 43, + "type": "int" + } + ], + "return_type": "AsyncGenerator[str, None]", + "signature": "main.generate_chunks", + "span": { + "bytes": [ + 1231, + 1412 + ], + "end": [ + 46, + 32 + ], + "start": [ + 43, + 0 + ] + }, + "start_line": 43, + "summary": [], + "types": {} + }, + "main": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "module", + "lineno": 123, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "function", + "lineno": 123, + "name": "async_main", + "qualified_name": "main.async_main", + "scope": "local", + "type": "async_main" + } + ], + "body": { + "123:11": { + "kind": "call", + "span": { + "bytes": [ + 3718, + 3743 + ], + "end": [ + 123, + 36 + ], + "start": [ + 123, + 11 + ] + } + }, + "123:23": { + "kind": "call", + "span": { + "bytes": [ + 3730, + 3742 + ], + "end": [ + 123, + 35 + ], + "start": [ + 123, + 23 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "async_main" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "async_main" + } + ], + "callee_signature": "asyncio.runners.run", + "end_column": 36, + "end_line": 123, + "is_constructor_call": false, + "method_name": "run", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "start_column": 11, + "start_line": 123 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.async_main", + "end_column": 35, + "end_line": 123, + "is_constructor_call": false, + "method_name": "async_main", + "return_type": "Coroutine", + "start_column": 23, + "start_line": 123 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 123, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 123, + "id": "can://python/async_patterns/main.py/main()", + "kind": "function", + "local_variables": [], + "name": "main", + "parameters": [], + "signature": "main.main", + "span": { + "bytes": [ + 3695, + 3743 + ], + "end": [ + 123, + 36 + ], + "start": [ + 122, + 0 + ] + }, + "start_line": 122, + "summary": [], + "types": {} + }, + "pipeline": { + "accessed_symbols": [ + { + "col_offset": 59, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "dict", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 51, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 103, + "name": "fetched", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 104, + "name": "chunks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 105, + "name": "content", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 30, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "function", + "lineno": 99, + "name": "fetch_all", + "qualified_name": "main.fetch_all", + "scope": "local", + "type": "fetch_all" + }, + { + "col_offset": 30, + "is_builtin": false, + "kind": "variable", + "lineno": 99, + "name": "urls", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "function", + "lineno": 100, + "name": "collect_chunks", + "qualified_name": "main.collect_chunks", + "scope": "local", + "type": "collect_chunks" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "function", + "lineno": 101, + "name": "read_resource", + "qualified_name": "main.read_resource", + "scope": "local", + "type": "read_resource" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "variable", + "lineno": 101, + "name": "resource_name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "100:19": { + "kind": "call", + "span": { + "bytes": [ + 3120, + 3163 + ], + "end": [ + 100, + 62 + ], + "start": [ + 100, + 19 + ] + } + }, + "101:20": { + "kind": "call", + "span": { + "bytes": [ + 3184, + 3212 + ], + "end": [ + 101, + 48 + ], + "start": [ + 101, + 20 + ] + } + }, + "99:20": { + "kind": "call", + "span": { + "bytes": [ + 3085, + 3100 + ], + "end": [ + 99, + 35 + ], + "start": [ + 99, + 20 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "list" + } + ], + "callee_signature": "main.fetch_all", + "end_column": 35, + "end_line": 99, + "is_constructor_call": false, + "method_name": "fetch_all", + "return_type": "Coroutine", + "start_column": 20, + "start_line": 99 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "main.collect_chunks", + "end_column": 62, + "end_line": 100, + "is_constructor_call": false, + "method_name": "collect_chunks", + "return_type": "Coroutine", + "start_column": 19, + "start_line": 100 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.read_resource", + "end_column": 48, + "end_line": 101, + "is_constructor_call": false, + "method_name": "read_resource", + "return_type": "Coroutine", + "start_column": 20, + "start_line": 101 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 99, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 106, + "id": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "kind": "function", + "local_variables": [ + { + "end_column": 11, + "end_line": 99, + "initializer": "await fetch_all(urls)", + "name": "fetched", + "scope": "function", + "start_column": 4, + "start_line": 99, + "type": "list" + }, + { + "end_column": 10, + "end_line": 100, + "initializer": "await collect_chunks('hello world async', size=5)", + "name": "chunks", + "scope": "function", + "start_column": 4, + "start_line": 100, + "type": "list" + }, + { + "end_column": 11, + "end_line": 101, + "initializer": "await read_resource(resource_name)", + "name": "content", + "scope": "function", + "start_column": 4, + "start_line": 101, + "type": "str" + } + ], + "name": "pipeline", + "parameters": [ + { + "end_column": 34, + "end_line": 98, + "name": "urls", + "start_column": 19, + "start_line": 98, + "type": "List[str]" + }, + { + "end_column": 54, + "end_line": 98, + "name": "resource_name", + "start_column": 36, + "start_line": 98, + "type": "str" + } + ], + "return_type": "dict", + "signature": "main.pipeline", + "span": { + "bytes": [ + 3000, + 3313 + ], + "end": [ + 106, + 5 + ], + "start": [ + 98, + 0 + ] + }, + "start_line": 98, + "summary": [], + "types": {} + }, + "process_url": { + "accessed_symbols": [ + { + "col_offset": 35, + "is_builtin": false, + "kind": "class", + "lineno": 25, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "class", + "lineno": 25, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 16, + "is_builtin": false, + "kind": "function", + "lineno": 26, + "name": "fetch_data", + "qualified_name": "main.fetch_data", + "scope": "local", + "type": "fetch_data" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 26, + "name": "url", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 27, + "name": "raw", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "26:16": { + "kind": "call", + "span": { + "bytes": [ + 673, + 688 + ], + "end": [ + 26, + 31 + ], + "start": [ + 26, + 16 + ] + } + }, + "27:11": { + "kind": "call", + "span": { + "bytes": [ + 700, + 711 + ], + "end": [ + 27, + 22 + ], + "start": [ + 27, + 11 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.fetch_data", + "end_column": 31, + "end_line": 26, + "is_constructor_call": false, + "method_name": "fetch_data", + "return_type": "Coroutine", + "start_column": 16, + "start_line": 26 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.str.upper", + "end_column": 22, + "end_line": 27, + "is_constructor_call": false, + "method_name": "upper", + "receiver_expr": "raw", + "receiver_type": "str", + "return_type": "str", + "start_column": 11, + "start_line": 27 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 26, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 27, + "id": "can://python/async_patterns/main.py/process_url(url)", + "kind": "function", + "local_variables": [ + { + "end_column": 7, + "end_line": 26, + "initializer": "await fetch_data(url)", + "name": "raw", + "scope": "function", + "start_column": 4, + "start_line": 26, + "type": "str" + } + ], + "name": "process_url", + "parameters": [ + { + "end_column": 30, + "end_line": 25, + "name": "url", + "start_column": 22, + "start_line": 25, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.process_url", + "span": { + "bytes": [ + 617, + 711 + ], + "end": [ + 27, + 22 + ], + "start": [ + 25, + 0 + ] + }, + "start_line": 25, + "summary": [], + "types": {} + }, + "read_resource": { + "accessed_symbols": [ + { + "col_offset": 38, + "is_builtin": false, + "kind": "class", + "lineno": 89, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 30, + "is_builtin": false, + "kind": "class", + "lineno": 89, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 90, + "name": "AsyncResource", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "variable", + "lineno": 90, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 91, + "name": "res", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + } + ], + "body": { + "90:15": { + "kind": "call", + "span": { + "bytes": [ + 2759, + 2778 + ], + "end": [ + 90, + 34 + ], + "start": [ + 90, + 15 + ] + } + }, + "91:21": { + "kind": "call", + "span": { + "bytes": [ + 2808, + 2818 + ], + "end": [ + 91, + 31 + ], + "start": [ + 91, + 21 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.AsyncResource.__init__", + "end_column": 34, + "end_line": 90, + "is_constructor_call": true, + "method_name": "AsyncResource", + "return_type": "AsyncResource", + "start_column": 15, + "start_line": 90 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.AsyncResource.read", + "end_column": 31, + "end_line": 91, + "is_constructor_call": false, + "method_name": "read", + "receiver_expr": "res", + "receiver_type": "AsyncResource", + "return_type": "Coroutine", + "start_column": 21, + "start_line": 91 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 90, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 91, + "id": "can://python/async_patterns/main.py/read_resource(name)", + "kind": "function", + "local_variables": [], + "name": "read_resource", + "parameters": [ + { + "end_column": 33, + "end_line": 89, + "name": "name", + "start_column": 24, + "start_line": 89, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.read_resource", + "span": { + "bytes": [ + 2701, + 2818 + ], + "end": [ + 91, + 31 + ], + "start": [ + 89, + 0 + ] + }, + "start_line": 89, + "summary": [], + "types": {} + } + }, + "id": "can://python/async_patterns/main.py", + "imports": [ + { + "end_column": 14, + "end_line": 12, + "module": "asyncio", + "name": "asyncio", + "start_column": 0, + "start_line": 12 + }, + { + "end_column": 39, + "end_line": 13, + "module": "typing", + "name": "AsyncGenerator", + "start_column": 0, + "start_line": 13 + }, + { + "end_column": 39, + "end_line": 13, + "module": "typing", + "name": "List", + "start_column": 0, + "start_line": 13 + } + ], + "kind": "module", + "module_name": "main", + "source": "\"\"\"Async/await patterns.\n\nExercises:\n- async def + await\n- asyncio.gather (concurrent tasks)\n- Async generator (async def + yield)\n- async for loop consuming an async generator\n- Async context manager (__aenter__ / __aexit__)\n- async with block\n- Nested awaits / task composition\n\"\"\"\nimport asyncio\nfrom typing import AsyncGenerator, List\n\n\n# ---------------------------------------------------------------------------\n# 1. Basic async function\n# ---------------------------------------------------------------------------\n\nasync def fetch_data(url: str) -> str:\n await asyncio.sleep(0)\n return f\"data:{url}\"\n\n\nasync def process_url(url: str) -> str:\n raw = await fetch_data(url)\n return raw.upper()\n\n\n# ---------------------------------------------------------------------------\n# 2. Concurrent tasks with asyncio.gather\n# ---------------------------------------------------------------------------\n\nasync def fetch_all(urls: List[str]) -> List[str]:\n tasks = [process_url(url) for url in urls]\n return await asyncio.gather(*tasks)\n\n\n# ---------------------------------------------------------------------------\n# 3. Async generator\n# ---------------------------------------------------------------------------\n\nasync def generate_chunks(data: str, size: int) -> AsyncGenerator[str, None]:\n for i in range(0, len(data), size):\n await asyncio.sleep(0)\n yield data[i : i + size]\n\n\n# ---------------------------------------------------------------------------\n# 4. async for consuming an async generator\n# ---------------------------------------------------------------------------\n\nasync def collect_chunks(data: str, size: int) -> List[str]:\n chunks: List[str] = []\n async for chunk in generate_chunks(data, size):\n chunks.append(chunk)\n return chunks\n\n\n# ---------------------------------------------------------------------------\n# 5. Async context manager\n# ---------------------------------------------------------------------------\n\nclass AsyncResource:\n def __init__(self, name: str):\n self.name = name\n self._open = False\n\n async def __aenter__(self) -> \"AsyncResource\":\n await asyncio.sleep(0)\n self._open = True\n return self\n\n async def __aexit__(self, exc_type, exc_val, exc_tb) -> bool:\n await asyncio.sleep(0)\n self._open = False\n return False\n\n async def read(self) -> str:\n if not self._open:\n raise RuntimeError(\"Resource not open\")\n return f\"content of {self.name}\"\n\n\n# ---------------------------------------------------------------------------\n# 6. async with\n# ---------------------------------------------------------------------------\n\nasync def read_resource(name: str) -> str:\n async with AsyncResource(name) as res:\n return await res.read()\n\n\n# ---------------------------------------------------------------------------\n# 7. Task composition\n# ---------------------------------------------------------------------------\n\nasync def pipeline(urls: List[str], resource_name: str) -> dict:\n fetched = await fetch_all(urls)\n chunks = await collect_chunks(\"hello world async\", size=5)\n content = await read_resource(resource_name)\n return {\n \"fetched\": fetched,\n \"chunks\": chunks,\n \"content\": content,\n }\n\n\n# ---------------------------------------------------------------------------\n# 8. Driver\n# ---------------------------------------------------------------------------\n\nasync def async_main() -> dict:\n urls = [\n \"http://example.com/a\",\n \"http://example.com/b\",\n \"http://example.com/c\",\n ]\n return await pipeline(urls, resource_name=\"config.json\")\n\n\ndef main():\n return asyncio.run(async_main())\n\n\nif __name__ == \"__main__\":\n main()\n", + "types": { + "main.AsyncResource": { + "attributes": {}, + "base_classes": [], + "callables": { + "__aenter__": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 72, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 71, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "module", + "lineno": 70, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + } + ], + "body": { + "70:14": { + "kind": "call", + "span": { + "bytes": [ + 2163, + 2179 + ], + "end": [ + 70, + 30 + ], + "start": [ + 70, + 14 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 30, + "end_line": 70, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 14, + "start_line": 70 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 70, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 72, + "id": "can://python/async_patterns/main.py/AsyncResource/__aenter__(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 71, + "initializer": "True", + "name": "_open", + "scope": "class", + "start_column": 8, + "start_line": 71, + "type": "AsyncResource" + } + ], + "name": "__aenter__", + "parameters": [ + { + "end_column": 29, + "end_line": 69, + "name": "self", + "start_column": 25, + "start_line": 69, + "type": "AsyncResource" + } + ], + "return_type": "'AsyncResource'", + "signature": "main.AsyncResource.__aenter__", + "span": { + "bytes": [ + 2102, + 2225 + ], + "end": [ + 72, + 19 + ], + "start": [ + 69, + 4 + ] + }, + "start_line": 69, + "summary": [], + "types": {} + }, + "__aexit__": { + "accessed_symbols": [ + { + "col_offset": 60, + "is_builtin": false, + "kind": "class", + "lineno": 74, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 76, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "module", + "lineno": 75, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + } + ], + "body": { + "75:14": { + "kind": "call", + "span": { + "bytes": [ + 2307, + 2323 + ], + "end": [ + 75, + 30 + ], + "start": [ + 75, + 14 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 30, + "end_line": 75, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 14, + "start_line": 75 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 75, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 77, + "id": "can://python/async_patterns/main.py/AsyncResource/__aexit__(self,exc_type,exc_val,exc_tb)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 76, + "initializer": "False", + "name": "_open", + "scope": "class", + "start_column": 8, + "start_line": 76, + "type": "AsyncResource" + } + ], + "name": "__aexit__", + "parameters": [ + { + "end_column": 28, + "end_line": 74, + "name": "self", + "start_column": 24, + "start_line": 74, + "type": "AsyncResource" + }, + { + "end_column": 38, + "end_line": 74, + "name": "exc_type", + "start_column": 30, + "start_line": 74 + }, + { + "end_column": 47, + "end_line": 74, + "name": "exc_val", + "start_column": 40, + "start_line": 74 + }, + { + "end_column": 55, + "end_line": 74, + "name": "exc_tb", + "start_column": 49, + "start_line": 74 + } + ], + "return_type": "bool", + "signature": "main.AsyncResource.__aexit__", + "span": { + "bytes": [ + 2231, + 2371 + ], + "end": [ + 77, + 20 + ], + "start": [ + 74, + 4 + ] + }, + "start_line": 74, + "summary": [], + "types": {} + }, + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 66, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 65, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 66, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 67, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 66, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 67, + "id": "can://python/async_patterns/main.py/AsyncResource/__init__(self,name)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 66, + "initializer": "name", + "name": "name", + "scope": "class", + "start_column": 8, + "start_line": 66, + "type": "AsyncResource" + }, + { + "end_column": 18, + "end_line": 67, + "initializer": "False", + "name": "_open", + "scope": "class", + "start_column": 8, + "start_line": 67, + "type": "AsyncResource" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 65, + "name": "self", + "start_column": 17, + "start_line": 65, + "type": "AsyncResource" + }, + { + "end_column": 32, + "end_line": 65, + "name": "name", + "start_column": 23, + "start_line": 65, + "type": "str" + } + ], + "signature": "main.AsyncResource.__init__", + "span": { + "bytes": [ + 2014, + 2096 + ], + "end": [ + 67, + 26 + ], + "start": [ + 65, + 4 + ] + }, + "start_line": 65, + "summary": [], + "types": {} + }, + "read": { + "accessed_symbols": [ + { + "col_offset": 28, + "is_builtin": false, + "kind": "class", + "lineno": 79, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 80, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "class", + "lineno": 81, + "name": "RuntimeError", + "qualified_name": "builtins.RuntimeError", + "scope": "local", + "type": "RuntimeError" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "variable", + "lineno": 82, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + } + ], + "body": { + "81:18": { + "kind": "call", + "span": { + "bytes": [ + 2451, + 2484 + ], + "end": [ + 81, + 51 + ], + "start": [ + 81, + 18 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.RuntimeError.__init__", + "end_column": 51, + "end_line": 81, + "is_constructor_call": true, + "method_name": "RuntimeError", + "return_type": "RuntimeError", + "start_column": 18, + "start_line": 81 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 80, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 82, + "id": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "kind": "function", + "local_variables": [], + "name": "read", + "parameters": [ + { + "end_column": 23, + "end_line": 79, + "name": "self", + "start_column": 19, + "start_line": 79, + "type": "AsyncResource" + } + ], + "return_type": "str", + "signature": "main.AsyncResource.read", + "span": { + "bytes": [ + 2377, + 2525 + ], + "end": [ + 82, + 40 + ], + "start": [ + 79, + 4 + ] + }, + "start_line": 79, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 82, + "id": "can://python/async_patterns/main.py/AsyncResource", + "kind": "class", + "name": "AsyncResource", + "signature": "main.AsyncResource", + "span": { + "bytes": [ + 1989, + 2525 + ], + "end": [ + 82, + 40 + ], + "start": [ + 64, + 0 + ] + }, + "start_line": 64, + "types": {} + } + }, + "variables": [] + } + } + }, + "language": "python", + "max_level": 1, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/async_patterns.a2.json b/test/golden/pipeline_equivalence/async_patterns.a2.json new file mode 100644 index 0000000..3873caf --- /dev/null +++ b/test/golden/pipeline_equivalence/async_patterns.a2.json @@ -0,0 +1,2692 @@ +{ + "analyzer": { + "config": { + "analysis_level": 2 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/AsyncResource/__aenter__(self)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/AsyncResource/__aexit__(self,exc_type,exc_val,exc_tb)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.RuntimeError/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/async_main()", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.list/append", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/gather", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/fetch_all(urls)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/process_url(url)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/fetch_all(urls)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/fetch_data(url)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins/len", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.range/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.runners/run", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/async_main()", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/fetch_all(urls)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/read_resource(name)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.str/upper", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/process_url(url)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/fetch_data(url)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/process_url(url)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/AsyncResource/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/read_resource(name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/read_resource(name)", + "weight": 1 + } + ], + "external_symbols": { + "can://python/async_patterns/@external/asyncio.runners/run": { + "id": "can://python/async_patterns/@external/asyncio.runners/run", + "kind": "external", + "module": "asyncio.runners", + "name": "run" + }, + "can://python/async_patterns/@external/asyncio.tasks/gather": { + "id": "can://python/async_patterns/@external/asyncio.tasks/gather", + "kind": "external", + "module": "asyncio.tasks", + "name": "gather" + }, + "can://python/async_patterns/@external/asyncio.tasks/sleep": { + "id": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "external", + "module": "asyncio.tasks", + "name": "sleep" + }, + "can://python/async_patterns/@external/builtins.RuntimeError/__init__": { + "id": "can://python/async_patterns/@external/builtins.RuntimeError/__init__", + "kind": "external", + "module": "builtins.RuntimeError", + "name": "__init__" + }, + "can://python/async_patterns/@external/builtins.list/append": { + "id": "can://python/async_patterns/@external/builtins.list/append", + "kind": "external", + "module": "builtins.list", + "name": "append" + }, + "can://python/async_patterns/@external/builtins.range/__init__": { + "id": "can://python/async_patterns/@external/builtins.range/__init__", + "kind": "external", + "module": "builtins.range", + "name": "__init__" + }, + "can://python/async_patterns/@external/builtins.str/upper": { + "id": "can://python/async_patterns/@external/builtins.str/upper", + "kind": "external", + "module": "builtins.str", + "name": "upper" + }, + "can://python/async_patterns/@external/builtins/len": { + "id": "can://python/async_patterns/@external/builtins/len", + "kind": "external", + "module": "builtins", + "name": "len" + } + }, + "id": "can://python/async_patterns", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [ + { + "content": "Async/await patterns.\n\nExercises:\n- async def + await\n- asyncio.gather (concurrent tasks)\n- Async generator (async def + yield)\n- async for loop consuming an async generator\n- Async context manager (__aenter__ / __aexit__)\n- async with block\n- Nested awaits / task composition\n", + "end_column": 3, + "end_line": 11, + "is_docstring": true, + "start_column": 0, + "start_line": 1 + } + ], + "content_hash": "3b6eb8773ed5b7309052bb555ad62c5505210c2bbcae17902924b136bfa7d6e0", + "file_size": 3784, + "functions": { + "async_main": { + "accessed_symbols": [ + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 113, + "name": "dict", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "function", + "lineno": 119, + "name": "pipeline", + "qualified_name": "main.pipeline", + "scope": "local", + "type": "pipeline" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 119, + "name": "urls", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + } + ], + "body": { + "119:17": { + "callee": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "kind": "call", + "span": { + "bytes": [ + 3649, + 3692 + ], + "end": [ + 119, + 60 + ], + "start": [ + 119, + 17 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "list" + } + ], + "callee_signature": "main.pipeline", + "end_column": 60, + "end_line": 119, + "is_constructor_call": false, + "method_name": "pipeline", + "return_type": "Coroutine", + "start_column": 17, + "start_line": 119 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 114, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 119, + "id": "can://python/async_patterns/main.py/async_main()", + "kind": "function", + "local_variables": [ + { + "end_column": 8, + "end_line": 118, + "initializer": "['http://example.com/a', 'http://example.com/b', 'http://example.com/c']", + "name": "urls", + "scope": "function", + "start_column": 4, + "start_line": 114, + "type": "list" + } + ], + "name": "async_main", + "parameters": [], + "return_type": "dict", + "signature": "main.async_main", + "span": { + "bytes": [ + 3485, + 3692 + ], + "end": [ + 119, + 60 + ], + "start": [ + 113, + 0 + ] + }, + "start_line": 113, + "summary": [], + "types": {} + }, + "collect_chunks": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 57, + "name": "chunks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 50, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 55, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 42, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "class", + "lineno": 54, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "class", + "lineno": 54, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "function", + "lineno": 55, + "name": "generate_chunks", + "qualified_name": "main.generate_chunks", + "scope": "local", + "type": "generate_chunks" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 55, + "name": "data", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 45, + "is_builtin": false, + "kind": "variable", + "lineno": 55, + "name": "size", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 22, + "is_builtin": false, + "kind": "variable", + "lineno": 56, + "name": "chunk", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 56, + "name": "chunks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + } + ], + "body": { + "55:23": { + "callee": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "kind": "call", + "span": { + "bytes": [ + 1727, + 1754 + ], + "end": [ + 55, + 50 + ], + "start": [ + 55, + 23 + ] + } + }, + "56:8": { + "callee": "can://python/async_patterns/@external/builtins.list/append", + "kind": "call", + "span": { + "bytes": [ + 1764, + 1784 + ], + "end": [ + 56, + 28 + ], + "start": [ + 56, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + }, + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "main.generate_chunks", + "end_column": 50, + "end_line": 55, + "is_constructor_call": false, + "method_name": "generate_chunks", + "return_type": "AsyncGenerator", + "start_column": 23, + "start_line": 55 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.list.append", + "end_column": 28, + "end_line": 56, + "is_constructor_call": false, + "method_name": "append", + "receiver_expr": "chunks", + "receiver_type": "list", + "start_column": 8, + "start_line": 56 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 54, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 57, + "id": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "kind": "function", + "local_variables": [ + { + "end_column": 10, + "end_line": 54, + "initializer": "[]", + "name": "chunks", + "scope": "function", + "start_column": 4, + "start_line": 54, + "type": "List[str]" + } + ], + "name": "collect_chunks", + "parameters": [ + { + "end_column": 34, + "end_line": 53, + "name": "data", + "start_column": 25, + "start_line": 53, + "type": "str" + }, + { + "end_column": 45, + "end_line": 53, + "name": "size", + "start_column": 36, + "start_line": 53, + "type": "int" + } + ], + "return_type": "List[str]", + "signature": "main.collect_chunks", + "span": { + "bytes": [ + 1616, + 1802 + ], + "end": [ + 57, + 17 + ], + "start": [ + 53, + 0 + ] + }, + "start_line": 53, + "summary": [], + "types": {} + }, + "fetch_all": { + "accessed_symbols": [ + { + "col_offset": 40, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 45, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 13, + "is_builtin": false, + "kind": "function", + "lineno": 35, + "name": "process_url", + "qualified_name": "main.process_url", + "scope": "local", + "type": "process_url" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "url", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "urls", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "module", + "lineno": 36, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "tasks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + } + ], + "body": { + "35:13": { + "callee": "can://python/async_patterns/main.py/process_url(url)", + "kind": "call", + "span": { + "bytes": [ + 977, + 993 + ], + "end": [ + 35, + 29 + ], + "start": [ + 35, + 13 + ] + } + }, + "36:17": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/gather", + "kind": "call", + "span": { + "bytes": [ + 1028, + 1050 + ], + "end": [ + 36, + 39 + ], + "start": [ + 36, + 17 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.process_url", + "end_column": 29, + "end_line": 35, + "is_constructor_call": false, + "method_name": "process_url", + "return_type": "Coroutine", + "start_column": 13, + "start_line": 35 + }, + { + "argument_types": [ + "Future" + ], + "arguments": [ + { + "ast_kind": "Starred", + "inferred_type": "Future" + } + ], + "callee_signature": "asyncio.tasks.gather", + "end_column": 39, + "end_line": 36, + "is_constructor_call": false, + "method_name": "gather", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Future", + "start_column": 17, + "start_line": 36 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 35, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 36, + "id": "can://python/async_patterns/main.py/fetch_all(urls)", + "kind": "function", + "local_variables": [ + { + "end_column": 9, + "end_line": 35, + "initializer": "[process_url(url) for url in urls]", + "name": "tasks", + "scope": "function", + "start_column": 4, + "start_line": 35, + "type": "list" + } + ], + "name": "fetch_all", + "parameters": [ + { + "end_column": 35, + "end_line": 34, + "name": "urls", + "start_column": 20, + "start_line": 34, + "type": "List[str]" + } + ], + "return_type": "List[str]", + "signature": "main.fetch_all", + "span": { + "bytes": [ + 913, + 1050 + ], + "end": [ + 36, + 39 + ], + "start": [ + 34, + 0 + ] + }, + "start_line": 34, + "summary": [], + "types": {} + }, + "fetch_data": { + "accessed_symbols": [ + { + "col_offset": 34, + "is_builtin": false, + "kind": "class", + "lineno": 20, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 20, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "url", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 10, + "is_builtin": false, + "kind": "module", + "lineno": 21, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + } + ], + "body": { + "21:10": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "call", + "span": { + "bytes": [ + 573, + 589 + ], + "end": [ + 21, + 26 + ], + "start": [ + 21, + 10 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 26, + "end_line": 21, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 10, + "start_line": 21 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 21, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 22, + "id": "can://python/async_patterns/main.py/fetch_data(url)", + "kind": "function", + "local_variables": [], + "name": "fetch_data", + "parameters": [ + { + "end_column": 29, + "end_line": 20, + "name": "url", + "start_column": 21, + "start_line": 20, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.fetch_data", + "span": { + "bytes": [ + 524, + 614 + ], + "end": [ + 22, + 24 + ], + "start": [ + 20, + 0 + ] + }, + "start_line": 20, + "summary": [], + "types": {} + }, + "generate_chunks": { + "accessed_symbols": [ + { + "col_offset": 51, + "is_builtin": false, + "kind": "variable", + "lineno": 43, + "name": "AsyncGenerator", + "qualified_name": "typing._SpecialGenericAlias", + "scope": "local", + "type": "_SpecialGenericAlias" + }, + { + "col_offset": 32, + "is_builtin": false, + "kind": "class", + "lineno": 43, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 43, + "is_builtin": false, + "kind": "class", + "lineno": 43, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 13, + "is_builtin": false, + "kind": "class", + "lineno": 44, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 44, + "name": "size", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 66, + "is_builtin": false, + "kind": "class", + "lineno": 43, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 22, + "is_builtin": false, + "kind": "function", + "lineno": 44, + "name": "len", + "qualified_name": "builtins.len", + "scope": "local", + "type": "len" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 44, + "name": "data", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "data", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "module", + "lineno": 45, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "i", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "i", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "size", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "44:13": { + "callee": "can://python/async_patterns/@external/builtins.range/__init__", + "kind": "call", + "span": { + "bytes": [ + 1322, + 1347 + ], + "end": [ + 44, + 38 + ], + "start": [ + 44, + 13 + ] + } + }, + "44:22": { + "callee": "can://python/async_patterns/@external/builtins/len", + "kind": "call", + "span": { + "bytes": [ + 1331, + 1340 + ], + "end": [ + 44, + 31 + ], + "start": [ + 44, + 22 + ] + } + }, + "45:14": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "call", + "span": { + "bytes": [ + 1363, + 1379 + ], + "end": [ + 45, + 30 + ], + "start": [ + 45, + 14 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int", + "len", + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + }, + { + "ast_kind": "Call", + "inferred_type": "len" + }, + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "builtins.range.__init__", + "end_column": 38, + "end_line": 44, + "is_constructor_call": true, + "method_name": "range", + "return_type": "range", + "start_column": 13, + "start_line": 44 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.len", + "end_column": 31, + "end_line": 44, + "is_constructor_call": false, + "method_name": "len", + "return_type": "int", + "start_column": 22, + "start_line": 44 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 30, + "end_line": 45, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 14, + "start_line": 45 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 44, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 46, + "id": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "kind": "function", + "local_variables": [], + "name": "generate_chunks", + "parameters": [ + { + "end_column": 35, + "end_line": 43, + "name": "data", + "start_column": 26, + "start_line": 43, + "type": "str" + }, + { + "end_column": 46, + "end_line": 43, + "name": "size", + "start_column": 37, + "start_line": 43, + "type": "int" + } + ], + "return_type": "AsyncGenerator[str, None]", + "signature": "main.generate_chunks", + "span": { + "bytes": [ + 1231, + 1412 + ], + "end": [ + 46, + 32 + ], + "start": [ + 43, + 0 + ] + }, + "start_line": 43, + "summary": [], + "types": {} + }, + "main": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "module", + "lineno": 123, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "function", + "lineno": 123, + "name": "async_main", + "qualified_name": "main.async_main", + "scope": "local", + "type": "async_main" + } + ], + "body": { + "123:11": { + "callee": "can://python/async_patterns/@external/asyncio.runners/run", + "kind": "call", + "span": { + "bytes": [ + 3718, + 3743 + ], + "end": [ + 123, + 36 + ], + "start": [ + 123, + 11 + ] + } + }, + "123:23": { + "callee": "can://python/async_patterns/main.py/async_main()", + "kind": "call", + "span": { + "bytes": [ + 3730, + 3742 + ], + "end": [ + 123, + 35 + ], + "start": [ + 123, + 23 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "async_main" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "async_main" + } + ], + "callee_signature": "asyncio.runners.run", + "end_column": 36, + "end_line": 123, + "is_constructor_call": false, + "method_name": "run", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "start_column": 11, + "start_line": 123 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.async_main", + "end_column": 35, + "end_line": 123, + "is_constructor_call": false, + "method_name": "async_main", + "return_type": "Coroutine", + "start_column": 23, + "start_line": 123 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 123, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 123, + "id": "can://python/async_patterns/main.py/main()", + "kind": "function", + "local_variables": [], + "name": "main", + "parameters": [], + "signature": "main.main", + "span": { + "bytes": [ + 3695, + 3743 + ], + "end": [ + 123, + 36 + ], + "start": [ + 122, + 0 + ] + }, + "start_line": 122, + "summary": [], + "types": {} + }, + "pipeline": { + "accessed_symbols": [ + { + "col_offset": 59, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "dict", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 51, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 103, + "name": "fetched", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 104, + "name": "chunks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 105, + "name": "content", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 30, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "function", + "lineno": 99, + "name": "fetch_all", + "qualified_name": "main.fetch_all", + "scope": "local", + "type": "fetch_all" + }, + { + "col_offset": 30, + "is_builtin": false, + "kind": "variable", + "lineno": 99, + "name": "urls", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "function", + "lineno": 100, + "name": "collect_chunks", + "qualified_name": "main.collect_chunks", + "scope": "local", + "type": "collect_chunks" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "function", + "lineno": 101, + "name": "read_resource", + "qualified_name": "main.read_resource", + "scope": "local", + "type": "read_resource" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "variable", + "lineno": 101, + "name": "resource_name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "100:19": { + "callee": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "kind": "call", + "span": { + "bytes": [ + 3120, + 3163 + ], + "end": [ + 100, + 62 + ], + "start": [ + 100, + 19 + ] + } + }, + "101:20": { + "callee": "can://python/async_patterns/main.py/read_resource(name)", + "kind": "call", + "span": { + "bytes": [ + 3184, + 3212 + ], + "end": [ + 101, + 48 + ], + "start": [ + 101, + 20 + ] + } + }, + "99:20": { + "callee": "can://python/async_patterns/main.py/fetch_all(urls)", + "kind": "call", + "span": { + "bytes": [ + 3085, + 3100 + ], + "end": [ + 99, + 35 + ], + "start": [ + 99, + 20 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "list" + } + ], + "callee_signature": "main.fetch_all", + "end_column": 35, + "end_line": 99, + "is_constructor_call": false, + "method_name": "fetch_all", + "return_type": "Coroutine", + "start_column": 20, + "start_line": 99 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "main.collect_chunks", + "end_column": 62, + "end_line": 100, + "is_constructor_call": false, + "method_name": "collect_chunks", + "return_type": "Coroutine", + "start_column": 19, + "start_line": 100 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.read_resource", + "end_column": 48, + "end_line": 101, + "is_constructor_call": false, + "method_name": "read_resource", + "return_type": "Coroutine", + "start_column": 20, + "start_line": 101 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 99, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 106, + "id": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "kind": "function", + "local_variables": [ + { + "end_column": 11, + "end_line": 99, + "initializer": "await fetch_all(urls)", + "name": "fetched", + "scope": "function", + "start_column": 4, + "start_line": 99, + "type": "list" + }, + { + "end_column": 10, + "end_line": 100, + "initializer": "await collect_chunks('hello world async', size=5)", + "name": "chunks", + "scope": "function", + "start_column": 4, + "start_line": 100, + "type": "list" + }, + { + "end_column": 11, + "end_line": 101, + "initializer": "await read_resource(resource_name)", + "name": "content", + "scope": "function", + "start_column": 4, + "start_line": 101, + "type": "str" + } + ], + "name": "pipeline", + "parameters": [ + { + "end_column": 34, + "end_line": 98, + "name": "urls", + "start_column": 19, + "start_line": 98, + "type": "List[str]" + }, + { + "end_column": 54, + "end_line": 98, + "name": "resource_name", + "start_column": 36, + "start_line": 98, + "type": "str" + } + ], + "return_type": "dict", + "signature": "main.pipeline", + "span": { + "bytes": [ + 3000, + 3313 + ], + "end": [ + 106, + 5 + ], + "start": [ + 98, + 0 + ] + }, + "start_line": 98, + "summary": [], + "types": {} + }, + "process_url": { + "accessed_symbols": [ + { + "col_offset": 35, + "is_builtin": false, + "kind": "class", + "lineno": 25, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "class", + "lineno": 25, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 16, + "is_builtin": false, + "kind": "function", + "lineno": 26, + "name": "fetch_data", + "qualified_name": "main.fetch_data", + "scope": "local", + "type": "fetch_data" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 26, + "name": "url", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 27, + "name": "raw", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "26:16": { + "callee": "can://python/async_patterns/main.py/fetch_data(url)", + "kind": "call", + "span": { + "bytes": [ + 673, + 688 + ], + "end": [ + 26, + 31 + ], + "start": [ + 26, + 16 + ] + } + }, + "27:11": { + "callee": "can://python/async_patterns/@external/builtins.str/upper", + "kind": "call", + "span": { + "bytes": [ + 700, + 711 + ], + "end": [ + 27, + 22 + ], + "start": [ + 27, + 11 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.fetch_data", + "end_column": 31, + "end_line": 26, + "is_constructor_call": false, + "method_name": "fetch_data", + "return_type": "Coroutine", + "start_column": 16, + "start_line": 26 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.str.upper", + "end_column": 22, + "end_line": 27, + "is_constructor_call": false, + "method_name": "upper", + "receiver_expr": "raw", + "receiver_type": "str", + "return_type": "str", + "start_column": 11, + "start_line": 27 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 26, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 27, + "id": "can://python/async_patterns/main.py/process_url(url)", + "kind": "function", + "local_variables": [ + { + "end_column": 7, + "end_line": 26, + "initializer": "await fetch_data(url)", + "name": "raw", + "scope": "function", + "start_column": 4, + "start_line": 26, + "type": "str" + } + ], + "name": "process_url", + "parameters": [ + { + "end_column": 30, + "end_line": 25, + "name": "url", + "start_column": 22, + "start_line": 25, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.process_url", + "span": { + "bytes": [ + 617, + 711 + ], + "end": [ + 27, + 22 + ], + "start": [ + 25, + 0 + ] + }, + "start_line": 25, + "summary": [], + "types": {} + }, + "read_resource": { + "accessed_symbols": [ + { + "col_offset": 38, + "is_builtin": false, + "kind": "class", + "lineno": 89, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 30, + "is_builtin": false, + "kind": "class", + "lineno": 89, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 90, + "name": "AsyncResource", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "variable", + "lineno": 90, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 91, + "name": "res", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + } + ], + "body": { + "90:15": { + "callee": "can://python/async_patterns/main.py/AsyncResource/__init__(self,name)", + "kind": "call", + "span": { + "bytes": [ + 2759, + 2778 + ], + "end": [ + 90, + 34 + ], + "start": [ + 90, + 15 + ] + } + }, + "91:21": { + "callee": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "kind": "call", + "span": { + "bytes": [ + 2808, + 2818 + ], + "end": [ + 91, + 31 + ], + "start": [ + 91, + 21 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.AsyncResource.__init__", + "end_column": 34, + "end_line": 90, + "is_constructor_call": true, + "method_name": "AsyncResource", + "return_type": "AsyncResource", + "start_column": 15, + "start_line": 90 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.AsyncResource.read", + "end_column": 31, + "end_line": 91, + "is_constructor_call": false, + "method_name": "read", + "receiver_expr": "res", + "receiver_type": "AsyncResource", + "return_type": "Coroutine", + "start_column": 21, + "start_line": 91 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 90, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 91, + "id": "can://python/async_patterns/main.py/read_resource(name)", + "kind": "function", + "local_variables": [], + "name": "read_resource", + "parameters": [ + { + "end_column": 33, + "end_line": 89, + "name": "name", + "start_column": 24, + "start_line": 89, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.read_resource", + "span": { + "bytes": [ + 2701, + 2818 + ], + "end": [ + 91, + 31 + ], + "start": [ + 89, + 0 + ] + }, + "start_line": 89, + "summary": [], + "types": {} + } + }, + "id": "can://python/async_patterns/main.py", + "imports": [ + { + "end_column": 14, + "end_line": 12, + "module": "asyncio", + "name": "asyncio", + "start_column": 0, + "start_line": 12 + }, + { + "end_column": 39, + "end_line": 13, + "module": "typing", + "name": "AsyncGenerator", + "start_column": 0, + "start_line": 13 + }, + { + "end_column": 39, + "end_line": 13, + "module": "typing", + "name": "List", + "start_column": 0, + "start_line": 13 + } + ], + "kind": "module", + "module_name": "main", + "source": "\"\"\"Async/await patterns.\n\nExercises:\n- async def + await\n- asyncio.gather (concurrent tasks)\n- Async generator (async def + yield)\n- async for loop consuming an async generator\n- Async context manager (__aenter__ / __aexit__)\n- async with block\n- Nested awaits / task composition\n\"\"\"\nimport asyncio\nfrom typing import AsyncGenerator, List\n\n\n# ---------------------------------------------------------------------------\n# 1. Basic async function\n# ---------------------------------------------------------------------------\n\nasync def fetch_data(url: str) -> str:\n await asyncio.sleep(0)\n return f\"data:{url}\"\n\n\nasync def process_url(url: str) -> str:\n raw = await fetch_data(url)\n return raw.upper()\n\n\n# ---------------------------------------------------------------------------\n# 2. Concurrent tasks with asyncio.gather\n# ---------------------------------------------------------------------------\n\nasync def fetch_all(urls: List[str]) -> List[str]:\n tasks = [process_url(url) for url in urls]\n return await asyncio.gather(*tasks)\n\n\n# ---------------------------------------------------------------------------\n# 3. Async generator\n# ---------------------------------------------------------------------------\n\nasync def generate_chunks(data: str, size: int) -> AsyncGenerator[str, None]:\n for i in range(0, len(data), size):\n await asyncio.sleep(0)\n yield data[i : i + size]\n\n\n# ---------------------------------------------------------------------------\n# 4. async for consuming an async generator\n# ---------------------------------------------------------------------------\n\nasync def collect_chunks(data: str, size: int) -> List[str]:\n chunks: List[str] = []\n async for chunk in generate_chunks(data, size):\n chunks.append(chunk)\n return chunks\n\n\n# ---------------------------------------------------------------------------\n# 5. Async context manager\n# ---------------------------------------------------------------------------\n\nclass AsyncResource:\n def __init__(self, name: str):\n self.name = name\n self._open = False\n\n async def __aenter__(self) -> \"AsyncResource\":\n await asyncio.sleep(0)\n self._open = True\n return self\n\n async def __aexit__(self, exc_type, exc_val, exc_tb) -> bool:\n await asyncio.sleep(0)\n self._open = False\n return False\n\n async def read(self) -> str:\n if not self._open:\n raise RuntimeError(\"Resource not open\")\n return f\"content of {self.name}\"\n\n\n# ---------------------------------------------------------------------------\n# 6. async with\n# ---------------------------------------------------------------------------\n\nasync def read_resource(name: str) -> str:\n async with AsyncResource(name) as res:\n return await res.read()\n\n\n# ---------------------------------------------------------------------------\n# 7. Task composition\n# ---------------------------------------------------------------------------\n\nasync def pipeline(urls: List[str], resource_name: str) -> dict:\n fetched = await fetch_all(urls)\n chunks = await collect_chunks(\"hello world async\", size=5)\n content = await read_resource(resource_name)\n return {\n \"fetched\": fetched,\n \"chunks\": chunks,\n \"content\": content,\n }\n\n\n# ---------------------------------------------------------------------------\n# 8. Driver\n# ---------------------------------------------------------------------------\n\nasync def async_main() -> dict:\n urls = [\n \"http://example.com/a\",\n \"http://example.com/b\",\n \"http://example.com/c\",\n ]\n return await pipeline(urls, resource_name=\"config.json\")\n\n\ndef main():\n return asyncio.run(async_main())\n\n\nif __name__ == \"__main__\":\n main()\n", + "types": { + "main.AsyncResource": { + "attributes": {}, + "base_classes": [], + "callables": { + "__aenter__": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 72, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 71, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "module", + "lineno": 70, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + } + ], + "body": { + "70:14": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "call", + "span": { + "bytes": [ + 2163, + 2179 + ], + "end": [ + 70, + 30 + ], + "start": [ + 70, + 14 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 30, + "end_line": 70, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 14, + "start_line": 70 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 70, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 72, + "id": "can://python/async_patterns/main.py/AsyncResource/__aenter__(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 71, + "initializer": "True", + "name": "_open", + "scope": "class", + "start_column": 8, + "start_line": 71, + "type": "AsyncResource" + } + ], + "name": "__aenter__", + "parameters": [ + { + "end_column": 29, + "end_line": 69, + "name": "self", + "start_column": 25, + "start_line": 69, + "type": "AsyncResource" + } + ], + "return_type": "'AsyncResource'", + "signature": "main.AsyncResource.__aenter__", + "span": { + "bytes": [ + 2102, + 2225 + ], + "end": [ + 72, + 19 + ], + "start": [ + 69, + 4 + ] + }, + "start_line": 69, + "summary": [], + "types": {} + }, + "__aexit__": { + "accessed_symbols": [ + { + "col_offset": 60, + "is_builtin": false, + "kind": "class", + "lineno": 74, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 76, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "module", + "lineno": 75, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + } + ], + "body": { + "75:14": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "call", + "span": { + "bytes": [ + 2307, + 2323 + ], + "end": [ + 75, + 30 + ], + "start": [ + 75, + 14 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 30, + "end_line": 75, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 14, + "start_line": 75 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 75, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 77, + "id": "can://python/async_patterns/main.py/AsyncResource/__aexit__(self,exc_type,exc_val,exc_tb)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 76, + "initializer": "False", + "name": "_open", + "scope": "class", + "start_column": 8, + "start_line": 76, + "type": "AsyncResource" + } + ], + "name": "__aexit__", + "parameters": [ + { + "end_column": 28, + "end_line": 74, + "name": "self", + "start_column": 24, + "start_line": 74, + "type": "AsyncResource" + }, + { + "end_column": 38, + "end_line": 74, + "name": "exc_type", + "start_column": 30, + "start_line": 74 + }, + { + "end_column": 47, + "end_line": 74, + "name": "exc_val", + "start_column": 40, + "start_line": 74 + }, + { + "end_column": 55, + "end_line": 74, + "name": "exc_tb", + "start_column": 49, + "start_line": 74 + } + ], + "return_type": "bool", + "signature": "main.AsyncResource.__aexit__", + "span": { + "bytes": [ + 2231, + 2371 + ], + "end": [ + 77, + 20 + ], + "start": [ + 74, + 4 + ] + }, + "start_line": 74, + "summary": [], + "types": {} + }, + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 66, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 65, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 66, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 67, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 66, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 67, + "id": "can://python/async_patterns/main.py/AsyncResource/__init__(self,name)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 66, + "initializer": "name", + "name": "name", + "scope": "class", + "start_column": 8, + "start_line": 66, + "type": "AsyncResource" + }, + { + "end_column": 18, + "end_line": 67, + "initializer": "False", + "name": "_open", + "scope": "class", + "start_column": 8, + "start_line": 67, + "type": "AsyncResource" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 65, + "name": "self", + "start_column": 17, + "start_line": 65, + "type": "AsyncResource" + }, + { + "end_column": 32, + "end_line": 65, + "name": "name", + "start_column": 23, + "start_line": 65, + "type": "str" + } + ], + "signature": "main.AsyncResource.__init__", + "span": { + "bytes": [ + 2014, + 2096 + ], + "end": [ + 67, + 26 + ], + "start": [ + 65, + 4 + ] + }, + "start_line": 65, + "summary": [], + "types": {} + }, + "read": { + "accessed_symbols": [ + { + "col_offset": 28, + "is_builtin": false, + "kind": "class", + "lineno": 79, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 80, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "class", + "lineno": 81, + "name": "RuntimeError", + "qualified_name": "builtins.RuntimeError", + "scope": "local", + "type": "RuntimeError" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "variable", + "lineno": 82, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + } + ], + "body": { + "81:18": { + "callee": "can://python/async_patterns/@external/builtins.RuntimeError/__init__", + "kind": "call", + "span": { + "bytes": [ + 2451, + 2484 + ], + "end": [ + 81, + 51 + ], + "start": [ + 81, + 18 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.RuntimeError.__init__", + "end_column": 51, + "end_line": 81, + "is_constructor_call": true, + "method_name": "RuntimeError", + "return_type": "RuntimeError", + "start_column": 18, + "start_line": 81 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 80, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 82, + "id": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "kind": "function", + "local_variables": [], + "name": "read", + "parameters": [ + { + "end_column": 23, + "end_line": 79, + "name": "self", + "start_column": 19, + "start_line": 79, + "type": "AsyncResource" + } + ], + "return_type": "str", + "signature": "main.AsyncResource.read", + "span": { + "bytes": [ + 2377, + 2525 + ], + "end": [ + 82, + 40 + ], + "start": [ + 79, + 4 + ] + }, + "start_line": 79, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 82, + "id": "can://python/async_patterns/main.py/AsyncResource", + "kind": "class", + "name": "AsyncResource", + "signature": "main.AsyncResource", + "span": { + "bytes": [ + 1989, + 2525 + ], + "end": [ + 82, + 40 + ], + "start": [ + 64, + 0 + ] + }, + "start_line": 64, + "types": {} + } + }, + "variables": [] + } + } + }, + "language": "python", + "max_level": 2, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/async_patterns.a3.json b/test/golden/pipeline_equivalence/async_patterns.a3.json new file mode 100644 index 0000000..75f22dc --- /dev/null +++ b/test/golden/pipeline_equivalence/async_patterns.a3.json @@ -0,0 +1,4413 @@ +{ + "analyzer": { + "config": { + "analysis_level": 3 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/AsyncResource/__aenter__(self)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/AsyncResource/__aexit__(self,exc_type,exc_val,exc_tb)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.RuntimeError/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/async_main()", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.list/append", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/gather", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/fetch_all(urls)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/process_url(url)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/fetch_all(urls)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/fetch_data(url)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins/len", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.range/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/asyncio.runners/run", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/async_main()", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/fetch_all(urls)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/read_resource(name)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/@external/builtins.str/upper", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/process_url(url)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/fetch_data(url)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/process_url(url)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/AsyncResource/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/read_resource(name)", + "weight": 1 + }, + { + "dst": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "prov": [ + "jedi" + ], + "src": "can://python/async_patterns/main.py/read_resource(name)", + "weight": 1 + } + ], + "external_symbols": { + "can://python/async_patterns/@external/asyncio.runners/run": { + "id": "can://python/async_patterns/@external/asyncio.runners/run", + "kind": "external", + "module": "asyncio.runners", + "name": "run" + }, + "can://python/async_patterns/@external/asyncio.tasks/gather": { + "id": "can://python/async_patterns/@external/asyncio.tasks/gather", + "kind": "external", + "module": "asyncio.tasks", + "name": "gather" + }, + "can://python/async_patterns/@external/asyncio.tasks/sleep": { + "id": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "external", + "module": "asyncio.tasks", + "name": "sleep" + }, + "can://python/async_patterns/@external/builtins.RuntimeError/__init__": { + "id": "can://python/async_patterns/@external/builtins.RuntimeError/__init__", + "kind": "external", + "module": "builtins.RuntimeError", + "name": "__init__" + }, + "can://python/async_patterns/@external/builtins.list/append": { + "id": "can://python/async_patterns/@external/builtins.list/append", + "kind": "external", + "module": "builtins.list", + "name": "append" + }, + "can://python/async_patterns/@external/builtins.range/__init__": { + "id": "can://python/async_patterns/@external/builtins.range/__init__", + "kind": "external", + "module": "builtins.range", + "name": "__init__" + }, + "can://python/async_patterns/@external/builtins.str/upper": { + "id": "can://python/async_patterns/@external/builtins.str/upper", + "kind": "external", + "module": "builtins.str", + "name": "upper" + }, + "can://python/async_patterns/@external/builtins/len": { + "id": "can://python/async_patterns/@external/builtins/len", + "kind": "external", + "module": "builtins", + "name": "len" + } + }, + "id": "can://python/async_patterns", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [ + { + "content": "Async/await patterns.\n\nExercises:\n- async def + await\n- asyncio.gather (concurrent tasks)\n- Async generator (async def + yield)\n- async for loop consuming an async generator\n- Async context manager (__aenter__ / __aexit__)\n- async with block\n- Nested awaits / task composition\n", + "end_column": 3, + "end_line": 11, + "is_docstring": true, + "start_column": 0, + "start_line": 1 + } + ], + "content_hash": "3b6eb8773ed5b7309052bb555ad62c5505210c2bbcae17902924b136bfa7d6e0", + "file_size": 3784, + "functions": { + "async_main": { + "accessed_symbols": [ + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 113, + "name": "dict", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "function", + "lineno": 119, + "name": "pipeline", + "qualified_name": "main.pipeline", + "scope": "local", + "type": "pipeline" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 119, + "name": "urls", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + } + ], + "body": { + "114:4": { + "kind": "statement", + "span": { + "bytes": [ + 3521, + 3631 + ], + "end": [ + 118, + 5 + ], + "start": [ + 114, + 4 + ] + } + }, + "119:17": { + "callee": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "kind": "call", + "span": { + "bytes": [ + 3649, + 3692 + ], + "end": [ + 119, + 60 + ], + "start": [ + 119, + 17 + ] + } + }, + "119:4": { + "kind": "return", + "span": { + "bytes": [ + 3636, + 3692 + ], + "end": [ + 119, + 60 + ], + "start": [ + 119, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "list" + } + ], + "callee_signature": "main.pipeline", + "end_column": 60, + "end_line": 119, + "is_constructor_call": false, + "method_name": "pipeline", + "return_type": "Coroutine", + "start_column": 17, + "start_line": 119 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "114:4", + "src": "@entry" + }, + { + "dst": "119:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "114:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "119:4", + "kind": "fallthrough", + "src": "114:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "119:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "119:4" + } + ], + "code_start_line": 114, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "119:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::pipeline" + }, + { + "dst": "119:4", + "prov": [ + "ssa" + ], + "src": "114:4", + "var": "urls" + } + ], + "decorators": [], + "end_line": 119, + "id": "can://python/async_patterns/main.py/async_main()", + "kind": "function", + "local_variables": [ + { + "end_column": 8, + "end_line": 118, + "initializer": "['http://example.com/a', 'http://example.com/b', 'http://example.com/c']", + "name": "urls", + "scope": "function", + "start_column": 4, + "start_line": 114, + "type": "list" + } + ], + "name": "async_main", + "parameters": [], + "return_type": "dict", + "signature": "main.async_main", + "span": { + "bytes": [ + 3485, + 3692 + ], + "end": [ + 119, + 60 + ], + "start": [ + 113, + 0 + ] + }, + "start_line": 113, + "summary": [], + "types": {} + }, + "collect_chunks": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 57, + "name": "chunks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 50, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 55, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 42, + "is_builtin": false, + "kind": "class", + "lineno": 53, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "class", + "lineno": 54, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "class", + "lineno": 54, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "function", + "lineno": 55, + "name": "generate_chunks", + "qualified_name": "main.generate_chunks", + "scope": "local", + "type": "generate_chunks" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 55, + "name": "data", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 45, + "is_builtin": false, + "kind": "variable", + "lineno": 55, + "name": "size", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 22, + "is_builtin": false, + "kind": "variable", + "lineno": 56, + "name": "chunk", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 56, + "name": "chunks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + } + ], + "body": { + "54:4": { + "kind": "statement", + "span": { + "bytes": [ + 1681, + 1703 + ], + "end": [ + 54, + 26 + ], + "start": [ + 54, + 4 + ] + } + }, + "55:23": { + "callee": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "kind": "call", + "span": { + "bytes": [ + 1727, + 1754 + ], + "end": [ + 55, + 50 + ], + "start": [ + 55, + 23 + ] + } + }, + "56:8": { + "callee": "can://python/async_patterns/@external/builtins.list/append", + "kind": "call", + "span": { + "bytes": [ + 1764, + 1784 + ], + "end": [ + 56, + 28 + ], + "start": [ + 56, + 8 + ] + } + }, + "57:4": { + "kind": "return", + "span": { + "bytes": [ + 1789, + 1802 + ], + "end": [ + 57, + 17 + ], + "start": [ + 57, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + }, + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "main.generate_chunks", + "end_column": 50, + "end_line": 55, + "is_constructor_call": false, + "method_name": "generate_chunks", + "return_type": "AsyncGenerator", + "start_column": 23, + "start_line": 55 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.list.append", + "end_column": 28, + "end_line": 56, + "is_constructor_call": false, + "method_name": "append", + "receiver_expr": "chunks", + "receiver_type": "list", + "start_column": 8, + "start_line": 56 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "54:4", + "src": "@entry" + }, + { + "dst": "55:23", + "src": "54:4" + }, + { + "dst": "56:8", + "src": "55:23" + }, + { + "dst": "57:4", + "src": "55:23" + }, + { + "dst": "55:23", + "src": "56:8" + } + ], + "cfg": [ + { + "dst": "54:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "55:23", + "kind": "fallthrough", + "src": "54:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "54:4" + }, + { + "dst": "56:8", + "kind": "true", + "src": "55:23" + }, + { + "dst": "57:4", + "kind": "false", + "src": "55:23" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "55:23" + }, + { + "dst": "55:23", + "kind": "loop_back", + "src": "56:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "56:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "57:4" + } + ], + "code_start_line": 54, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "55:23", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "data" + }, + { + "dst": "55:23", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::generate_chunks" + }, + { + "dst": "55:23", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "size" + }, + { + "dst": "56:8", + "prov": [ + "ssa" + ], + "src": "54:4", + "var": "chunks" + }, + { + "dst": "56:8", + "prov": [ + "ssa" + ], + "src": "54:4", + "var": "chunks.append" + }, + { + "dst": "57:4", + "prov": [ + "ssa" + ], + "src": "54:4", + "var": "chunks" + }, + { + "dst": "55:23", + "prov": [ + "ssa" + ], + "src": "55:23", + "var": "data" + }, + { + "dst": "55:23", + "prov": [ + "ssa" + ], + "src": "55:23", + "var": "size" + }, + { + "dst": "56:8", + "prov": [ + "ssa" + ], + "src": "55:23", + "var": "chunk" + }, + { + "dst": "56:8", + "prov": [ + "ssa" + ], + "src": "56:8", + "var": "chunk" + }, + { + "dst": "56:8", + "prov": [ + "ssa" + ], + "src": "56:8", + "var": "chunks" + }, + { + "dst": "56:8", + "prov": [ + "ssa" + ], + "src": "56:8", + "var": "chunks.append" + }, + { + "dst": "57:4", + "prov": [ + "ssa" + ], + "src": "56:8", + "var": "chunks" + } + ], + "decorators": [], + "end_line": 57, + "id": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "kind": "function", + "local_variables": [ + { + "end_column": 10, + "end_line": 54, + "initializer": "[]", + "name": "chunks", + "scope": "function", + "start_column": 4, + "start_line": 54, + "type": "List[str]" + } + ], + "name": "collect_chunks", + "parameters": [ + { + "end_column": 34, + "end_line": 53, + "name": "data", + "start_column": 25, + "start_line": 53, + "type": "str" + }, + { + "end_column": 45, + "end_line": 53, + "name": "size", + "start_column": 36, + "start_line": 53, + "type": "int" + } + ], + "return_type": "List[str]", + "signature": "main.collect_chunks", + "span": { + "bytes": [ + 1616, + 1802 + ], + "end": [ + 57, + 17 + ], + "start": [ + 53, + 0 + ] + }, + "start_line": 53, + "summary": [], + "types": {} + }, + "fetch_all": { + "accessed_symbols": [ + { + "col_offset": 40, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 45, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 13, + "is_builtin": false, + "kind": "function", + "lineno": 35, + "name": "process_url", + "qualified_name": "main.process_url", + "scope": "local", + "type": "process_url" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "url", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "urls", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "module", + "lineno": 36, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "tasks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + } + ], + "body": { + "35:13": { + "callee": "can://python/async_patterns/main.py/process_url(url)", + "kind": "call", + "span": { + "bytes": [ + 977, + 993 + ], + "end": [ + 35, + 29 + ], + "start": [ + 35, + 13 + ] + } + }, + "35:4": { + "kind": "statement", + "span": { + "bytes": [ + 968, + 1010 + ], + "end": [ + 35, + 46 + ], + "start": [ + 35, + 4 + ] + } + }, + "36:17": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/gather", + "kind": "call", + "span": { + "bytes": [ + 1028, + 1050 + ], + "end": [ + 36, + 39 + ], + "start": [ + 36, + 17 + ] + } + }, + "36:4": { + "kind": "return", + "span": { + "bytes": [ + 1015, + 1050 + ], + "end": [ + 36, + 39 + ], + "start": [ + 36, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.process_url", + "end_column": 29, + "end_line": 35, + "is_constructor_call": false, + "method_name": "process_url", + "return_type": "Coroutine", + "start_column": 13, + "start_line": 35 + }, + { + "argument_types": [ + "Future" + ], + "arguments": [ + { + "ast_kind": "Starred", + "inferred_type": "Future" + } + ], + "callee_signature": "asyncio.tasks.gather", + "end_column": 39, + "end_line": 36, + "is_constructor_call": false, + "method_name": "gather", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Future", + "start_column": 17, + "start_line": 36 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "35:4", + "src": "@entry" + }, + { + "dst": "36:4", + "src": "35:4" + } + ], + "cfg": [ + { + "dst": "35:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "36:4", + "kind": "fallthrough", + "src": "35:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "35:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "36:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "36:4" + } + ], + "code_start_line": 35, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "35:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::process_url" + }, + { + "dst": "35:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "urls" + }, + { + "dst": "36:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio" + }, + { + "dst": "36:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio.gather" + }, + { + "dst": "36:4", + "prov": [ + "ssa" + ], + "src": "35:4", + "var": "tasks" + } + ], + "decorators": [], + "end_line": 36, + "id": "can://python/async_patterns/main.py/fetch_all(urls)", + "kind": "function", + "local_variables": [ + { + "end_column": 9, + "end_line": 35, + "initializer": "[process_url(url) for url in urls]", + "name": "tasks", + "scope": "function", + "start_column": 4, + "start_line": 35, + "type": "list" + } + ], + "name": "fetch_all", + "parameters": [ + { + "end_column": 35, + "end_line": 34, + "name": "urls", + "start_column": 20, + "start_line": 34, + "type": "List[str]" + } + ], + "return_type": "List[str]", + "signature": "main.fetch_all", + "span": { + "bytes": [ + 913, + 1050 + ], + "end": [ + 36, + 39 + ], + "start": [ + 34, + 0 + ] + }, + "start_line": 34, + "summary": [], + "types": {} + }, + "fetch_data": { + "accessed_symbols": [ + { + "col_offset": 34, + "is_builtin": false, + "kind": "class", + "lineno": 20, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 20, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "url", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 10, + "is_builtin": false, + "kind": "module", + "lineno": 21, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + } + ], + "body": { + "21:10": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "call", + "span": { + "bytes": [ + 573, + 589 + ], + "end": [ + 21, + 26 + ], + "start": [ + 21, + 10 + ] + } + }, + "21:4": { + "kind": "statement", + "span": { + "bytes": [ + 567, + 589 + ], + "end": [ + 21, + 26 + ], + "start": [ + 21, + 4 + ] + } + }, + "22:4": { + "kind": "return", + "span": { + "bytes": [ + 594, + 614 + ], + "end": [ + 22, + 24 + ], + "start": [ + 22, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 26, + "end_line": 21, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 10, + "start_line": 21 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "21:4", + "src": "@entry" + }, + { + "dst": "22:4", + "src": "21:4" + } + ], + "cfg": [ + { + "dst": "21:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "22:4", + "kind": "await_resume", + "src": "21:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "21:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "22:4" + } + ], + "code_start_line": 21, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "21:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio" + }, + { + "dst": "21:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio.sleep" + }, + { + "dst": "22:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "url" + } + ], + "decorators": [], + "end_line": 22, + "id": "can://python/async_patterns/main.py/fetch_data(url)", + "kind": "function", + "local_variables": [], + "name": "fetch_data", + "parameters": [ + { + "end_column": 29, + "end_line": 20, + "name": "url", + "start_column": 21, + "start_line": 20, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.fetch_data", + "span": { + "bytes": [ + 524, + 614 + ], + "end": [ + 22, + 24 + ], + "start": [ + 20, + 0 + ] + }, + "start_line": 20, + "summary": [], + "types": {} + }, + "generate_chunks": { + "accessed_symbols": [ + { + "col_offset": 51, + "is_builtin": false, + "kind": "variable", + "lineno": 43, + "name": "AsyncGenerator", + "qualified_name": "typing._SpecialGenericAlias", + "scope": "local", + "type": "_SpecialGenericAlias" + }, + { + "col_offset": 32, + "is_builtin": false, + "kind": "class", + "lineno": 43, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 43, + "is_builtin": false, + "kind": "class", + "lineno": 43, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 13, + "is_builtin": false, + "kind": "class", + "lineno": 44, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 44, + "name": "size", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 66, + "is_builtin": false, + "kind": "class", + "lineno": 43, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 22, + "is_builtin": false, + "kind": "function", + "lineno": 44, + "name": "len", + "qualified_name": "builtins.len", + "scope": "local", + "type": "len" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 44, + "name": "data", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "data", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "module", + "lineno": 45, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "i", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "i", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 46, + "name": "size", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "44:13": { + "callee": "can://python/async_patterns/@external/builtins.range/__init__", + "kind": "call", + "span": { + "bytes": [ + 1322, + 1347 + ], + "end": [ + 44, + 38 + ], + "start": [ + 44, + 13 + ] + } + }, + "44:22": { + "callee": "can://python/async_patterns/@external/builtins/len", + "kind": "call", + "span": { + "bytes": [ + 1331, + 1340 + ], + "end": [ + 44, + 31 + ], + "start": [ + 44, + 22 + ] + } + }, + "45:14": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "call", + "span": { + "bytes": [ + 1363, + 1379 + ], + "end": [ + 45, + 30 + ], + "start": [ + 45, + 14 + ] + } + }, + "45:8": { + "kind": "statement", + "span": { + "bytes": [ + 1357, + 1379 + ], + "end": [ + 45, + 30 + ], + "start": [ + 45, + 8 + ] + } + }, + "46:8": { + "kind": "statement", + "span": { + "bytes": [ + 1388, + 1412 + ], + "end": [ + 46, + 32 + ], + "start": [ + 46, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "int", + "len", + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + }, + { + "ast_kind": "Call", + "inferred_type": "len" + }, + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "builtins.range.__init__", + "end_column": 38, + "end_line": 44, + "is_constructor_call": true, + "method_name": "range", + "return_type": "range", + "start_column": 13, + "start_line": 44 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.len", + "end_column": 31, + "end_line": 44, + "is_constructor_call": false, + "method_name": "len", + "return_type": "int", + "start_column": 22, + "start_line": 44 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 30, + "end_line": 45, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 14, + "start_line": 45 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "45:8", + "src": "44:13" + }, + { + "dst": "46:8", + "src": "45:8" + }, + { + "dst": "44:13", + "src": "46:8" + } + ], + "cfg": [ + { + "dst": "44:13", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "45:8", + "kind": "true", + "src": "44:13" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "44:13" + }, + { + "dst": "@exit", + "kind": "return", + "src": "44:13" + }, + { + "dst": "46:8", + "kind": "await_resume", + "src": "45:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "45:8" + }, + { + "dst": "44:13", + "kind": "loop_back", + "src": "46:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "46:8" + }, + { + "dst": "@exit", + "kind": "yield", + "src": "46:8" + } + ], + "code_start_line": 44, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [ + { + "dst": "44:13", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "data" + }, + { + "dst": "44:13", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "len" + }, + { + "dst": "44:13", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "range" + }, + { + "dst": "44:13", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "size" + }, + { + "dst": "45:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio" + }, + { + "dst": "45:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio.sleep" + }, + { + "dst": "46:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "data[*]" + }, + { + "dst": "46:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "size" + }, + { + "dst": "44:13", + "prov": [ + "ssa" + ], + "src": "44:13", + "var": "data" + }, + { + "dst": "44:13", + "prov": [ + "ssa" + ], + "src": "44:13", + "var": "size" + }, + { + "dst": "46:8", + "prov": [ + "ssa" + ], + "src": "44:13", + "var": "data[*]" + }, + { + "dst": "46:8", + "prov": [ + "ssa" + ], + "src": "44:13", + "var": "i" + }, + { + "dst": "46:8", + "prov": [ + "ssa" + ], + "src": "44:13", + "var": "size" + }, + { + "dst": "45:8", + "prov": [ + "ssa" + ], + "src": "45:8", + "var": "main::asyncio" + }, + { + "dst": "45:8", + "prov": [ + "ssa" + ], + "src": "45:8", + "var": "main::asyncio.sleep" + } + ], + "decorators": [], + "end_line": 46, + "id": "can://python/async_patterns/main.py/generate_chunks(data,size)", + "kind": "function", + "local_variables": [], + "name": "generate_chunks", + "parameters": [ + { + "end_column": 35, + "end_line": 43, + "name": "data", + "start_column": 26, + "start_line": 43, + "type": "str" + }, + { + "end_column": 46, + "end_line": 43, + "name": "size", + "start_column": 37, + "start_line": 43, + "type": "int" + } + ], + "return_type": "AsyncGenerator[str, None]", + "signature": "main.generate_chunks", + "span": { + "bytes": [ + 1231, + 1412 + ], + "end": [ + 46, + 32 + ], + "start": [ + 43, + 0 + ] + }, + "start_line": 43, + "summary": [], + "types": {} + }, + "main": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "module", + "lineno": 123, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "function", + "lineno": 123, + "name": "async_main", + "qualified_name": "main.async_main", + "scope": "local", + "type": "async_main" + } + ], + "body": { + "123:11": { + "callee": "can://python/async_patterns/@external/asyncio.runners/run", + "kind": "call", + "span": { + "bytes": [ + 3718, + 3743 + ], + "end": [ + 123, + 36 + ], + "start": [ + 123, + 11 + ] + } + }, + "123:23": { + "callee": "can://python/async_patterns/main.py/async_main()", + "kind": "call", + "span": { + "bytes": [ + 3730, + 3742 + ], + "end": [ + 123, + 35 + ], + "start": [ + 123, + 23 + ] + } + }, + "123:4": { + "kind": "return", + "span": { + "bytes": [ + 3711, + 3743 + ], + "end": [ + 123, + 36 + ], + "start": [ + 123, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "async_main" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "async_main" + } + ], + "callee_signature": "asyncio.runners.run", + "end_column": 36, + "end_line": 123, + "is_constructor_call": false, + "method_name": "run", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "start_column": 11, + "start_line": 123 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.async_main", + "end_column": 35, + "end_line": 123, + "is_constructor_call": false, + "method_name": "async_main", + "return_type": "Coroutine", + "start_column": 23, + "start_line": 123 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "123:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "123:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "123:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "123:4" + } + ], + "code_start_line": 123, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "123:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::async_main" + }, + { + "dst": "123:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio" + }, + { + "dst": "123:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio.run" + } + ], + "decorators": [], + "end_line": 123, + "id": "can://python/async_patterns/main.py/main()", + "kind": "function", + "local_variables": [], + "name": "main", + "parameters": [], + "signature": "main.main", + "span": { + "bytes": [ + 3695, + 3743 + ], + "end": [ + 123, + 36 + ], + "start": [ + 122, + 0 + ] + }, + "start_line": 122, + "summary": [], + "types": {} + }, + "pipeline": { + "accessed_symbols": [ + { + "col_offset": 59, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "dict", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 51, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 103, + "name": "fetched", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 104, + "name": "chunks", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 105, + "name": "content", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 30, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "function", + "lineno": 99, + "name": "fetch_all", + "qualified_name": "main.fetch_all", + "scope": "local", + "type": "fetch_all" + }, + { + "col_offset": 30, + "is_builtin": false, + "kind": "variable", + "lineno": 99, + "name": "urls", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "function", + "lineno": 100, + "name": "collect_chunks", + "qualified_name": "main.collect_chunks", + "scope": "local", + "type": "collect_chunks" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "function", + "lineno": 101, + "name": "read_resource", + "qualified_name": "main.read_resource", + "scope": "local", + "type": "read_resource" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "variable", + "lineno": 101, + "name": "resource_name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "100:19": { + "callee": "can://python/async_patterns/main.py/collect_chunks(data,size)", + "kind": "call", + "span": { + "bytes": [ + 3120, + 3163 + ], + "end": [ + 100, + 62 + ], + "start": [ + 100, + 19 + ] + } + }, + "100:4": { + "kind": "statement", + "span": { + "bytes": [ + 3105, + 3163 + ], + "end": [ + 100, + 62 + ], + "start": [ + 100, + 4 + ] + } + }, + "101:20": { + "callee": "can://python/async_patterns/main.py/read_resource(name)", + "kind": "call", + "span": { + "bytes": [ + 3184, + 3212 + ], + "end": [ + 101, + 48 + ], + "start": [ + 101, + 20 + ] + } + }, + "101:4": { + "kind": "statement", + "span": { + "bytes": [ + 3168, + 3212 + ], + "end": [ + 101, + 48 + ], + "start": [ + 101, + 4 + ] + } + }, + "102:4": { + "kind": "return", + "span": { + "bytes": [ + 3217, + 3313 + ], + "end": [ + 106, + 5 + ], + "start": [ + 102, + 4 + ] + } + }, + "99:20": { + "callee": "can://python/async_patterns/main.py/fetch_all(urls)", + "kind": "call", + "span": { + "bytes": [ + 3085, + 3100 + ], + "end": [ + 99, + 35 + ], + "start": [ + 99, + 20 + ] + } + }, + "99:4": { + "kind": "statement", + "span": { + "bytes": [ + 3069, + 3100 + ], + "end": [ + 99, + 35 + ], + "start": [ + 99, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "list" + } + ], + "callee_signature": "main.fetch_all", + "end_column": 35, + "end_line": 99, + "is_constructor_call": false, + "method_name": "fetch_all", + "return_type": "Coroutine", + "start_column": 20, + "start_line": 99 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "main.collect_chunks", + "end_column": 62, + "end_line": 100, + "is_constructor_call": false, + "method_name": "collect_chunks", + "return_type": "Coroutine", + "start_column": 19, + "start_line": 100 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.read_resource", + "end_column": 48, + "end_line": 101, + "is_constructor_call": false, + "method_name": "read_resource", + "return_type": "Coroutine", + "start_column": 20, + "start_line": 101 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "99:4", + "src": "@entry" + }, + { + "dst": "100:4", + "src": "99:4" + }, + { + "dst": "101:4", + "src": "100:4" + }, + { + "dst": "102:4", + "src": "101:4" + } + ], + "cfg": [ + { + "dst": "99:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "100:4", + "kind": "await_resume", + "src": "99:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "99:4" + }, + { + "dst": "101:4", + "kind": "await_resume", + "src": "100:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "100:4" + }, + { + "dst": "102:4", + "kind": "await_resume", + "src": "101:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "101:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "102:4" + } + ], + "code_start_line": 99, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "99:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::fetch_all" + }, + { + "dst": "99:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "urls" + }, + { + "dst": "100:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::collect_chunks" + }, + { + "dst": "101:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::read_resource" + }, + { + "dst": "101:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "resource_name" + }, + { + "dst": "102:4", + "prov": [ + "ssa" + ], + "src": "99:4", + "var": "fetched" + }, + { + "dst": "102:4", + "prov": [ + "ssa" + ], + "src": "100:4", + "var": "chunks" + }, + { + "dst": "102:4", + "prov": [ + "ssa" + ], + "src": "101:4", + "var": "content" + } + ], + "decorators": [], + "end_line": 106, + "id": "can://python/async_patterns/main.py/pipeline(urls,resource_name)", + "kind": "function", + "local_variables": [ + { + "end_column": 11, + "end_line": 99, + "initializer": "await fetch_all(urls)", + "name": "fetched", + "scope": "function", + "start_column": 4, + "start_line": 99, + "type": "list" + }, + { + "end_column": 10, + "end_line": 100, + "initializer": "await collect_chunks('hello world async', size=5)", + "name": "chunks", + "scope": "function", + "start_column": 4, + "start_line": 100, + "type": "list" + }, + { + "end_column": 11, + "end_line": 101, + "initializer": "await read_resource(resource_name)", + "name": "content", + "scope": "function", + "start_column": 4, + "start_line": 101, + "type": "str" + } + ], + "name": "pipeline", + "parameters": [ + { + "end_column": 34, + "end_line": 98, + "name": "urls", + "start_column": 19, + "start_line": 98, + "type": "List[str]" + }, + { + "end_column": 54, + "end_line": 98, + "name": "resource_name", + "start_column": 36, + "start_line": 98, + "type": "str" + } + ], + "return_type": "dict", + "signature": "main.pipeline", + "span": { + "bytes": [ + 3000, + 3313 + ], + "end": [ + 106, + 5 + ], + "start": [ + 98, + 0 + ] + }, + "start_line": 98, + "summary": [], + "types": {} + }, + "process_url": { + "accessed_symbols": [ + { + "col_offset": 35, + "is_builtin": false, + "kind": "class", + "lineno": 25, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "class", + "lineno": 25, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 16, + "is_builtin": false, + "kind": "function", + "lineno": 26, + "name": "fetch_data", + "qualified_name": "main.fetch_data", + "scope": "local", + "type": "fetch_data" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 26, + "name": "url", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 27, + "name": "raw", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "26:16": { + "callee": "can://python/async_patterns/main.py/fetch_data(url)", + "kind": "call", + "span": { + "bytes": [ + 673, + 688 + ], + "end": [ + 26, + 31 + ], + "start": [ + 26, + 16 + ] + } + }, + "26:4": { + "kind": "statement", + "span": { + "bytes": [ + 661, + 688 + ], + "end": [ + 26, + 31 + ], + "start": [ + 26, + 4 + ] + } + }, + "27:11": { + "callee": "can://python/async_patterns/@external/builtins.str/upper", + "kind": "call", + "span": { + "bytes": [ + 700, + 711 + ], + "end": [ + 27, + 22 + ], + "start": [ + 27, + 11 + ] + } + }, + "27:4": { + "kind": "return", + "span": { + "bytes": [ + 693, + 711 + ], + "end": [ + 27, + 22 + ], + "start": [ + 27, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.fetch_data", + "end_column": 31, + "end_line": 26, + "is_constructor_call": false, + "method_name": "fetch_data", + "return_type": "Coroutine", + "start_column": 16, + "start_line": 26 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.str.upper", + "end_column": 22, + "end_line": 27, + "is_constructor_call": false, + "method_name": "upper", + "receiver_expr": "raw", + "receiver_type": "str", + "return_type": "str", + "start_column": 11, + "start_line": 27 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "26:4", + "src": "@entry" + }, + { + "dst": "27:4", + "src": "26:4" + } + ], + "cfg": [ + { + "dst": "26:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "27:4", + "kind": "await_resume", + "src": "26:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "26:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "27:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "27:4" + } + ], + "code_start_line": 26, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "26:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::fetch_data" + }, + { + "dst": "26:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "url" + }, + { + "dst": "27:4", + "prov": [ + "ssa" + ], + "src": "26:4", + "var": "raw" + }, + { + "dst": "27:4", + "prov": [ + "ssa" + ], + "src": "26:4", + "var": "raw.upper" + } + ], + "decorators": [], + "end_line": 27, + "id": "can://python/async_patterns/main.py/process_url(url)", + "kind": "function", + "local_variables": [ + { + "end_column": 7, + "end_line": 26, + "initializer": "await fetch_data(url)", + "name": "raw", + "scope": "function", + "start_column": 4, + "start_line": 26, + "type": "str" + } + ], + "name": "process_url", + "parameters": [ + { + "end_column": 30, + "end_line": 25, + "name": "url", + "start_column": 22, + "start_line": 25, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.process_url", + "span": { + "bytes": [ + 617, + 711 + ], + "end": [ + 27, + 22 + ], + "start": [ + 25, + 0 + ] + }, + "start_line": 25, + "summary": [], + "types": {} + }, + "read_resource": { + "accessed_symbols": [ + { + "col_offset": 38, + "is_builtin": false, + "kind": "class", + "lineno": 89, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 30, + "is_builtin": false, + "kind": "class", + "lineno": 89, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 90, + "name": "AsyncResource", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "variable", + "lineno": 90, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 91, + "name": "res", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + } + ], + "body": { + "90:15": { + "callee": "can://python/async_patterns/main.py/AsyncResource/__init__(self,name)", + "kind": "call", + "span": { + "bytes": [ + 2759, + 2778 + ], + "end": [ + 90, + 34 + ], + "start": [ + 90, + 15 + ] + } + }, + "90:4": { + "kind": "statement", + "span": { + "bytes": [ + 2748, + 2778 + ], + "end": [ + 90, + 34 + ], + "start": [ + 90, + 4 + ] + } + }, + "91:21": { + "callee": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "kind": "call", + "span": { + "bytes": [ + 2808, + 2818 + ], + "end": [ + 91, + 31 + ], + "start": [ + 91, + 21 + ] + } + }, + "91:8": { + "kind": "return", + "span": { + "bytes": [ + 2795, + 2818 + ], + "end": [ + 91, + 31 + ], + "start": [ + 91, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.AsyncResource.__init__", + "end_column": 34, + "end_line": 90, + "is_constructor_call": true, + "method_name": "AsyncResource", + "return_type": "AsyncResource", + "start_column": 15, + "start_line": 90 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.AsyncResource.read", + "end_column": 31, + "end_line": 91, + "is_constructor_call": false, + "method_name": "read", + "receiver_expr": "res", + "receiver_type": "AsyncResource", + "return_type": "Coroutine", + "start_column": 21, + "start_line": 91 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "90:4", + "src": "@entry" + }, + { + "dst": "91:8", + "src": "90:4" + } + ], + "cfg": [ + { + "dst": "90:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "91:8", + "kind": "fallthrough", + "src": "90:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "90:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "91:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "91:8" + } + ], + "code_start_line": 90, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "90:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::AsyncResource" + }, + { + "dst": "90:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + }, + { + "dst": "91:8", + "prov": [ + "ssa" + ], + "src": "90:4", + "var": "res" + }, + { + "dst": "91:8", + "prov": [ + "ssa" + ], + "src": "90:4", + "var": "res.read" + } + ], + "decorators": [], + "end_line": 91, + "id": "can://python/async_patterns/main.py/read_resource(name)", + "kind": "function", + "local_variables": [], + "name": "read_resource", + "parameters": [ + { + "end_column": 33, + "end_line": 89, + "name": "name", + "start_column": 24, + "start_line": 89, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.read_resource", + "span": { + "bytes": [ + 2701, + 2818 + ], + "end": [ + 91, + 31 + ], + "start": [ + 89, + 0 + ] + }, + "start_line": 89, + "summary": [], + "types": {} + } + }, + "id": "can://python/async_patterns/main.py", + "imports": [ + { + "end_column": 14, + "end_line": 12, + "module": "asyncio", + "name": "asyncio", + "start_column": 0, + "start_line": 12 + }, + { + "end_column": 39, + "end_line": 13, + "module": "typing", + "name": "AsyncGenerator", + "start_column": 0, + "start_line": 13 + }, + { + "end_column": 39, + "end_line": 13, + "module": "typing", + "name": "List", + "start_column": 0, + "start_line": 13 + } + ], + "kind": "module", + "module_name": "main", + "source": "\"\"\"Async/await patterns.\n\nExercises:\n- async def + await\n- asyncio.gather (concurrent tasks)\n- Async generator (async def + yield)\n- async for loop consuming an async generator\n- Async context manager (__aenter__ / __aexit__)\n- async with block\n- Nested awaits / task composition\n\"\"\"\nimport asyncio\nfrom typing import AsyncGenerator, List\n\n\n# ---------------------------------------------------------------------------\n# 1. Basic async function\n# ---------------------------------------------------------------------------\n\nasync def fetch_data(url: str) -> str:\n await asyncio.sleep(0)\n return f\"data:{url}\"\n\n\nasync def process_url(url: str) -> str:\n raw = await fetch_data(url)\n return raw.upper()\n\n\n# ---------------------------------------------------------------------------\n# 2. Concurrent tasks with asyncio.gather\n# ---------------------------------------------------------------------------\n\nasync def fetch_all(urls: List[str]) -> List[str]:\n tasks = [process_url(url) for url in urls]\n return await asyncio.gather(*tasks)\n\n\n# ---------------------------------------------------------------------------\n# 3. Async generator\n# ---------------------------------------------------------------------------\n\nasync def generate_chunks(data: str, size: int) -> AsyncGenerator[str, None]:\n for i in range(0, len(data), size):\n await asyncio.sleep(0)\n yield data[i : i + size]\n\n\n# ---------------------------------------------------------------------------\n# 4. async for consuming an async generator\n# ---------------------------------------------------------------------------\n\nasync def collect_chunks(data: str, size: int) -> List[str]:\n chunks: List[str] = []\n async for chunk in generate_chunks(data, size):\n chunks.append(chunk)\n return chunks\n\n\n# ---------------------------------------------------------------------------\n# 5. Async context manager\n# ---------------------------------------------------------------------------\n\nclass AsyncResource:\n def __init__(self, name: str):\n self.name = name\n self._open = False\n\n async def __aenter__(self) -> \"AsyncResource\":\n await asyncio.sleep(0)\n self._open = True\n return self\n\n async def __aexit__(self, exc_type, exc_val, exc_tb) -> bool:\n await asyncio.sleep(0)\n self._open = False\n return False\n\n async def read(self) -> str:\n if not self._open:\n raise RuntimeError(\"Resource not open\")\n return f\"content of {self.name}\"\n\n\n# ---------------------------------------------------------------------------\n# 6. async with\n# ---------------------------------------------------------------------------\n\nasync def read_resource(name: str) -> str:\n async with AsyncResource(name) as res:\n return await res.read()\n\n\n# ---------------------------------------------------------------------------\n# 7. Task composition\n# ---------------------------------------------------------------------------\n\nasync def pipeline(urls: List[str], resource_name: str) -> dict:\n fetched = await fetch_all(urls)\n chunks = await collect_chunks(\"hello world async\", size=5)\n content = await read_resource(resource_name)\n return {\n \"fetched\": fetched,\n \"chunks\": chunks,\n \"content\": content,\n }\n\n\n# ---------------------------------------------------------------------------\n# 8. Driver\n# ---------------------------------------------------------------------------\n\nasync def async_main() -> dict:\n urls = [\n \"http://example.com/a\",\n \"http://example.com/b\",\n \"http://example.com/c\",\n ]\n return await pipeline(urls, resource_name=\"config.json\")\n\n\ndef main():\n return asyncio.run(async_main())\n\n\nif __name__ == \"__main__\":\n main()\n", + "types": { + "main.AsyncResource": { + "attributes": {}, + "base_classes": [], + "callables": { + "__aenter__": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 72, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 71, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "module", + "lineno": 70, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + } + ], + "body": { + "70:14": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "call", + "span": { + "bytes": [ + 2163, + 2179 + ], + "end": [ + 70, + 30 + ], + "start": [ + 70, + 14 + ] + } + }, + "70:8": { + "kind": "statement", + "span": { + "bytes": [ + 2157, + 2179 + ], + "end": [ + 70, + 30 + ], + "start": [ + 70, + 8 + ] + } + }, + "71:8": { + "kind": "statement", + "span": { + "bytes": [ + 2188, + 2205 + ], + "end": [ + 71, + 25 + ], + "start": [ + 71, + 8 + ] + } + }, + "72:8": { + "kind": "return", + "span": { + "bytes": [ + 2214, + 2225 + ], + "end": [ + 72, + 19 + ], + "start": [ + 72, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 30, + "end_line": 70, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 14, + "start_line": 70 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "70:8", + "src": "@entry" + }, + { + "dst": "71:8", + "src": "70:8" + }, + { + "dst": "72:8", + "src": "71:8" + } + ], + "cfg": [ + { + "dst": "70:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "71:8", + "kind": "await_resume", + "src": "70:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "70:8" + }, + { + "dst": "72:8", + "kind": "fallthrough", + "src": "71:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "71:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "72:8" + } + ], + "code_start_line": 70, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "70:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio" + }, + { + "dst": "70:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio.sleep" + }, + { + "dst": "71:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "72:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "72:8", + "prov": [ + "ssa" + ], + "src": "71:8", + "var": "self" + } + ], + "decorators": [], + "end_line": 72, + "id": "can://python/async_patterns/main.py/AsyncResource/__aenter__(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 71, + "initializer": "True", + "name": "_open", + "scope": "class", + "start_column": 8, + "start_line": 71, + "type": "AsyncResource" + } + ], + "name": "__aenter__", + "parameters": [ + { + "end_column": 29, + "end_line": 69, + "name": "self", + "start_column": 25, + "start_line": 69, + "type": "AsyncResource" + } + ], + "return_type": "'AsyncResource'", + "signature": "main.AsyncResource.__aenter__", + "span": { + "bytes": [ + 2102, + 2225 + ], + "end": [ + 72, + 19 + ], + "start": [ + 69, + 4 + ] + }, + "start_line": 69, + "summary": [], + "types": {} + }, + "__aexit__": { + "accessed_symbols": [ + { + "col_offset": 60, + "is_builtin": false, + "kind": "class", + "lineno": 74, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 76, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "module", + "lineno": 75, + "name": "asyncio", + "qualified_name": "asyncio", + "scope": "global", + "type": "asyncio" + } + ], + "body": { + "75:14": { + "callee": "can://python/async_patterns/@external/asyncio.tasks/sleep", + "kind": "call", + "span": { + "bytes": [ + 2307, + 2323 + ], + "end": [ + 75, + 30 + ], + "start": [ + 75, + 14 + ] + } + }, + "75:8": { + "kind": "statement", + "span": { + "bytes": [ + 2301, + 2323 + ], + "end": [ + 75, + 30 + ], + "start": [ + 75, + 8 + ] + } + }, + "76:8": { + "kind": "statement", + "span": { + "bytes": [ + 2332, + 2350 + ], + "end": [ + 76, + 26 + ], + "start": [ + 76, + 8 + ] + } + }, + "77:8": { + "kind": "return", + "span": { + "bytes": [ + 2359, + 2371 + ], + "end": [ + 77, + 20 + ], + "start": [ + 77, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "asyncio.tasks.sleep", + "end_column": 30, + "end_line": 75, + "is_constructor_call": false, + "method_name": "sleep", + "receiver_expr": "asyncio", + "receiver_type": "asyncio", + "return_type": "Coroutine", + "start_column": 14, + "start_line": 75 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "75:8", + "src": "@entry" + }, + { + "dst": "76:8", + "src": "75:8" + }, + { + "dst": "77:8", + "src": "76:8" + } + ], + "cfg": [ + { + "dst": "75:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "76:8", + "kind": "await_resume", + "src": "75:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "75:8" + }, + { + "dst": "77:8", + "kind": "fallthrough", + "src": "76:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "76:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "77:8" + } + ], + "code_start_line": 75, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "75:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio" + }, + { + "dst": "75:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::asyncio.sleep" + }, + { + "dst": "76:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + } + ], + "decorators": [], + "end_line": 77, + "id": "can://python/async_patterns/main.py/AsyncResource/__aexit__(self,exc_type,exc_val,exc_tb)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 76, + "initializer": "False", + "name": "_open", + "scope": "class", + "start_column": 8, + "start_line": 76, + "type": "AsyncResource" + } + ], + "name": "__aexit__", + "parameters": [ + { + "end_column": 28, + "end_line": 74, + "name": "self", + "start_column": 24, + "start_line": 74, + "type": "AsyncResource" + }, + { + "end_column": 38, + "end_line": 74, + "name": "exc_type", + "start_column": 30, + "start_line": 74 + }, + { + "end_column": 47, + "end_line": 74, + "name": "exc_val", + "start_column": 40, + "start_line": 74 + }, + { + "end_column": 55, + "end_line": 74, + "name": "exc_tb", + "start_column": 49, + "start_line": 74 + } + ], + "return_type": "bool", + "signature": "main.AsyncResource.__aexit__", + "span": { + "bytes": [ + 2231, + 2371 + ], + "end": [ + 77, + 20 + ], + "start": [ + 74, + 4 + ] + }, + "start_line": 74, + "summary": [], + "types": {} + }, + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 66, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 65, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 66, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 67, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + } + ], + "body": { + "66:8": { + "kind": "statement", + "span": { + "bytes": [ + 2053, + 2069 + ], + "end": [ + 66, + 24 + ], + "start": [ + 66, + 8 + ] + } + }, + "67:8": { + "kind": "statement", + "span": { + "bytes": [ + 2078, + 2096 + ], + "end": [ + 67, + 26 + ], + "start": [ + 67, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "66:8", + "src": "@entry" + }, + { + "dst": "67:8", + "src": "66:8" + } + ], + "cfg": [ + { + "dst": "66:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "67:8", + "kind": "fallthrough", + "src": "66:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "66:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "67:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "67:8" + } + ], + "code_start_line": 66, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "66:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + }, + { + "dst": "66:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "67:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "67:8", + "prov": [ + "ssa" + ], + "src": "66:8", + "var": "self" + } + ], + "decorators": [], + "end_line": 67, + "id": "can://python/async_patterns/main.py/AsyncResource/__init__(self,name)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 66, + "initializer": "name", + "name": "name", + "scope": "class", + "start_column": 8, + "start_line": 66, + "type": "AsyncResource" + }, + { + "end_column": 18, + "end_line": 67, + "initializer": "False", + "name": "_open", + "scope": "class", + "start_column": 8, + "start_line": 67, + "type": "AsyncResource" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 65, + "name": "self", + "start_column": 17, + "start_line": 65, + "type": "AsyncResource" + }, + { + "end_column": 32, + "end_line": 65, + "name": "name", + "start_column": 23, + "start_line": 65, + "type": "str" + } + ], + "signature": "main.AsyncResource.__init__", + "span": { + "bytes": [ + 2014, + 2096 + ], + "end": [ + 67, + 26 + ], + "start": [ + 65, + 4 + ] + }, + "start_line": 65, + "summary": [], + "types": {} + }, + "read": { + "accessed_symbols": [ + { + "col_offset": 28, + "is_builtin": false, + "kind": "class", + "lineno": 79, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 80, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "class", + "lineno": 81, + "name": "RuntimeError", + "qualified_name": "builtins.RuntimeError", + "scope": "local", + "type": "RuntimeError" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "variable", + "lineno": 82, + "name": "self", + "qualified_name": "main.AsyncResource", + "scope": "local", + "type": "AsyncResource" + } + ], + "body": { + "80:11": { + "kind": "branch", + "span": { + "bytes": [ + 2417, + 2431 + ], + "end": [ + 80, + 25 + ], + "start": [ + 80, + 11 + ] + } + }, + "81:12": { + "kind": "raise", + "span": { + "bytes": [ + 2445, + 2484 + ], + "end": [ + 81, + 51 + ], + "start": [ + 81, + 12 + ] + } + }, + "81:18": { + "callee": "can://python/async_patterns/@external/builtins.RuntimeError/__init__", + "kind": "call", + "span": { + "bytes": [ + 2451, + 2484 + ], + "end": [ + 81, + 51 + ], + "start": [ + 81, + 18 + ] + } + }, + "82:8": { + "kind": "return", + "span": { + "bytes": [ + 2493, + 2525 + ], + "end": [ + 82, + 40 + ], + "start": [ + 82, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.RuntimeError.__init__", + "end_column": 51, + "end_line": 81, + "is_constructor_call": true, + "method_name": "RuntimeError", + "return_type": "RuntimeError", + "start_column": 18, + "start_line": 81 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "80:11", + "src": "@entry" + }, + { + "dst": "81:12", + "src": "80:11" + }, + { + "dst": "82:8", + "src": "80:11" + } + ], + "cfg": [ + { + "dst": "80:11", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "81:12", + "kind": "true", + "src": "80:11" + }, + { + "dst": "82:8", + "kind": "false", + "src": "80:11" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "80:11" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "81:12" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "82:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "82:8" + } + ], + "code_start_line": 80, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [ + { + "dst": "80:11", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self._open" + }, + { + "dst": "81:12", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "RuntimeError" + }, + { + "dst": "82:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.name" + } + ], + "decorators": [], + "end_line": 82, + "id": "can://python/async_patterns/main.py/AsyncResource/read(self)", + "kind": "function", + "local_variables": [], + "name": "read", + "parameters": [ + { + "end_column": 23, + "end_line": 79, + "name": "self", + "start_column": 19, + "start_line": 79, + "type": "AsyncResource" + } + ], + "return_type": "str", + "signature": "main.AsyncResource.read", + "span": { + "bytes": [ + 2377, + 2525 + ], + "end": [ + 82, + 40 + ], + "start": [ + 79, + 4 + ] + }, + "start_line": 79, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 82, + "id": "can://python/async_patterns/main.py/AsyncResource", + "kind": "class", + "name": "AsyncResource", + "signature": "main.AsyncResource", + "span": { + "bytes": [ + 1989, + 2525 + ], + "end": [ + 82, + 40 + ], + "start": [ + 64, + 0 + ] + }, + "start_line": 64, + "types": {} + } + }, + "variables": [] + } + } + }, + "k_limit": 3, + "language": "python", + "max_level": 3, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/class_hierarchy.a1.json b/test/golden/pipeline_equivalence/class_hierarchy.a1.json new file mode 100644 index 0000000..5f29103 --- /dev/null +++ b/test/golden/pipeline_equivalence/class_hierarchy.a1.json @@ -0,0 +1,4864 @@ +{ + "analyzer": { + "config": { + "analysis_level": 1 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/class_hierarchy/@external/builtins.list/append", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.object/__init_subclass__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/__init_subclass__(cls,kwargs)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/__init_subclass__(cls,kwargs)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/create(cls,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Cat/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Cat/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/development(cls)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.bool/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.int/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/typing.Mapping/get", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 3 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/production(cls)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Duck/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Duck/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Dog/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 6 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/kingdom()", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/validate_port(port)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/process_animals(animals)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "weight": 2 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/process_animals(animals)", + "weight": 1 + } + ], + "external_symbols": { + "can://python/class_hierarchy/@external/builtins.bool/__init__": { + "id": "can://python/class_hierarchy/@external/builtins.bool/__init__", + "kind": "external", + "module": "builtins.bool", + "name": "__init__" + }, + "can://python/class_hierarchy/@external/builtins.classmethod/__get__": { + "id": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "external", + "module": "builtins.classmethod", + "name": "__get__" + }, + "can://python/class_hierarchy/@external/builtins.int/__init__": { + "id": "can://python/class_hierarchy/@external/builtins.int/__init__", + "kind": "external", + "module": "builtins.int", + "name": "__init__" + }, + "can://python/class_hierarchy/@external/builtins.list/append": { + "id": "can://python/class_hierarchy/@external/builtins.list/append", + "kind": "external", + "module": "builtins.list", + "name": "append" + }, + "can://python/class_hierarchy/@external/builtins.object/__init_subclass__": { + "id": "can://python/class_hierarchy/@external/builtins.object/__init_subclass__", + "kind": "external", + "module": "builtins.object", + "name": "__init_subclass__" + }, + "can://python/class_hierarchy/@external/builtins.super/__init__": { + "id": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "external", + "module": "builtins.super", + "name": "__init__" + }, + "can://python/class_hierarchy/@external/typing.Mapping/get": { + "id": "can://python/class_hierarchy/@external/typing.Mapping/get", + "kind": "external", + "module": "typing.Mapping", + "name": "get" + } + }, + "id": "can://python/class_hierarchy", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [ + { + "content": "Class hierarchy patterns.\n\nExercises:\n- Abstract base class (abc.ABC + @abstractmethod)\n- Multiple inheritance and MRO\n- super() in __init__ and regular methods\n- @classmethod as factory\n- @staticmethod utility\n- __init_subclass__ hook\n- Dynamic dispatch / polymorphism\n", + "end_column": 3, + "end_line": 11, + "is_docstring": true, + "start_column": 0, + "start_line": 1 + } + ], + "content_hash": "c09e8f66fc9464c0e77b889a31ca1bab0aeb2eea376d77016d132dc67e6bebd2", + "file_size": 5029, + "functions": { + "main": { + "accessed_symbols": [ + { + "col_offset": 9, + "is_builtin": false, + "kind": "class", + "lineno": 168, + "name": "PoliceDog", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "class", + "lineno": 169, + "name": "RescuePoliceDog", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "function", + "lineno": 171, + "name": "process_animals", + "qualified_name": "main.process_animals", + "scope": "local", + "type": "process_animals" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "descriptions", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "sounds", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "cfg_dev", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 42, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "cfg_prod", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 52, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "cfg_custom", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 64, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "valid", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 71, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "kingdom", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 10, + "is_builtin": false, + "kind": "class", + "lineno": 165, + "name": "Dog", + "qualified_name": "main.Dog", + "scope": "local", + "type": "Dog" + }, + { + "col_offset": 10, + "is_builtin": false, + "kind": "class", + "lineno": 166, + "name": "Cat", + "qualified_name": "main.Cat", + "scope": "local", + "type": "Cat" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "class", + "lineno": 167, + "name": "Duck", + "qualified_name": "main.Duck", + "scope": "local", + "type": "Duck" + }, + { + "col_offset": 36, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "dog", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "cat", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 46, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "duck", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 52, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "k9", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + }, + { + "col_offset": 56, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "elite", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "function", + "lineno": 172, + "name": "make_sound_twice", + "qualified_name": "main.make_sound_twice", + "scope": "local", + "type": "make_sound_twice" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 172, + "name": "a", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "class", + "lineno": 174, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 175, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "class", + "lineno": 176, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "class", + "lineno": 178, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 178, + "name": "cfg_dev", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "class", + "lineno": 179, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 44, + "is_builtin": false, + "kind": "variable", + "lineno": 172, + "name": "dog", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 49, + "is_builtin": false, + "kind": "variable", + "lineno": 172, + "name": "cat", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "165:10": { + "kind": "call", + "span": { + "bytes": [ + 4377, + 4394 + ], + "end": [ + 165, + 27 + ], + "start": [ + 165, + 10 + ] + } + }, + "166:10": { + "kind": "call", + "span": { + "bytes": [ + 4405, + 4427 + ], + "end": [ + 166, + 32 + ], + "start": [ + 166, + 10 + ] + } + }, + "167:11": { + "kind": "call", + "span": { + "bytes": [ + 4439, + 4460 + ], + "end": [ + 167, + 32 + ], + "start": [ + 167, + 11 + ] + } + }, + "168:9": { + "kind": "call", + "span": { + "bytes": [ + 4470, + 4498 + ], + "end": [ + 168, + 37 + ], + "start": [ + 168, + 9 + ] + } + }, + "169:12": { + "kind": "call", + "span": { + "bytes": [ + 4511, + 4566 + ], + "end": [ + 169, + 67 + ], + "start": [ + 169, + 12 + ] + } + }, + "171:19": { + "kind": "call", + "span": { + "bytes": [ + 4587, + 4631 + ], + "end": [ + 171, + 63 + ], + "start": [ + 171, + 19 + ] + } + }, + "172:14": { + "kind": "call", + "span": { + "bytes": [ + 4646, + 4665 + ], + "end": [ + 172, + 33 + ], + "start": [ + 172, + 14 + ] + } + }, + "174:14": { + "kind": "call", + "span": { + "bytes": [ + 4702, + 4722 + ], + "end": [ + 174, + 34 + ], + "start": [ + 174, + 14 + ] + } + }, + "175:15": { + "kind": "call", + "span": { + "bytes": [ + 4738, + 4757 + ], + "end": [ + 175, + 34 + ], + "start": [ + 175, + 15 + ] + } + }, + "176:17": { + "kind": "call", + "span": { + "bytes": [ + 4775, + 4829 + ], + "end": [ + 176, + 71 + ], + "start": [ + 176, + 17 + ] + } + }, + "178:12": { + "kind": "call", + "span": { + "bytes": [ + 4843, + 4877 + ], + "end": [ + 178, + 46 + ], + "start": [ + 178, + 12 + ] + } + }, + "179:14": { + "kind": "call", + "span": { + "bytes": [ + 4892, + 4908 + ], + "end": [ + 179, + 30 + ], + "start": [ + 179, + 14 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 27, + "end_line": 165, + "is_constructor_call": false, + "method_name": "create", + "receiver_expr": "Dog", + "receiver_type": "Dog", + "return_type": "Animal", + "start_column": 10, + "start_line": 165 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 32, + "end_line": 166, + "is_constructor_call": false, + "method_name": "create", + "receiver_expr": "Cat", + "receiver_type": "Cat", + "return_type": "Animal", + "start_column": 10, + "start_line": 166 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 32, + "end_line": 167, + "is_constructor_call": false, + "method_name": "create", + "receiver_expr": "Duck", + "receiver_type": "Duck", + "return_type": "Animal", + "start_column": 11, + "start_line": 167 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "main.PoliceDog.__init__", + "end_column": 37, + "end_line": 168, + "is_constructor_call": true, + "method_name": "PoliceDog", + "return_type": "PoliceDog", + "start_column": 9, + "start_line": 168 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "main.RescuePoliceDog.__init__", + "end_column": 67, + "end_line": 169, + "is_constructor_call": true, + "method_name": "RescuePoliceDog", + "return_type": "RescuePoliceDog", + "start_column": 12, + "start_line": 169 + }, + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "List", + "inferred_type": "list" + } + ], + "callee_signature": "main.process_animals", + "end_column": 63, + "end_line": 171, + "is_constructor_call": false, + "method_name": "process_animals", + "return_type": "list", + "start_column": 19, + "start_line": 171 + }, + { + "argument_types": [ + "Animal" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "Animal" + } + ], + "callee_signature": "main.make_sound_twice", + "end_column": 33, + "end_line": 172, + "is_constructor_call": false, + "method_name": "make_sound_twice", + "return_type": "str", + "start_column": 14, + "start_line": 172 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 34, + "end_line": 174, + "is_constructor_call": false, + "method_name": "development", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "Config", + "start_column": 14, + "start_line": 174 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 34, + "end_line": 175, + "is_constructor_call": false, + "method_name": "production", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "Config", + "start_column": 15, + "start_line": 175 + }, + { + "argument_types": [ + "Config" + ], + "arguments": [ + { + "ast_kind": "Dict", + "inferred_type": "Config" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 71, + "end_line": 176, + "is_constructor_call": false, + "method_name": "from_dict", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "Config", + "start_column": 17, + "start_line": 176 + }, + { + "argument_types": [ + "Config" + ], + "arguments": [ + { + "ast_kind": "Attribute", + "inferred_type": "Config" + } + ], + "callee_signature": "main.Config.validate_port", + "end_column": 46, + "end_line": 178, + "is_constructor_call": false, + "method_name": "validate_port", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "bool", + "start_column": 12, + "start_line": 178 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.kingdom", + "end_column": 30, + "end_line": 179, + "is_constructor_call": false, + "method_name": "kingdom", + "receiver_expr": "Animal", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 14, + "start_line": 179 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 165, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 181, + "id": "can://python/class_hierarchy/main.py/main()", + "kind": "function", + "local_variables": [ + { + "end_column": 7, + "end_line": 165, + "initializer": "Dog.create('Rex')", + "name": "dog", + "scope": "function", + "start_column": 4, + "start_line": 165, + "type": "Animal" + }, + { + "end_column": 7, + "end_line": 166, + "initializer": "Cat.create('Whiskers')", + "name": "cat", + "scope": "function", + "start_column": 4, + "start_line": 166, + "type": "Animal" + }, + { + "end_column": 8, + "end_line": 167, + "initializer": "Duck.create('Donald')", + "name": "duck", + "scope": "function", + "start_column": 4, + "start_line": 167, + "type": "Animal" + }, + { + "end_column": 6, + "end_line": 168, + "initializer": "PoliceDog('Buddy', badge=42)", + "name": "k9", + "scope": "function", + "start_column": 4, + "start_line": 168, + "type": "PoliceDog" + }, + { + "end_column": 9, + "end_line": 169, + "initializer": "RescuePoliceDog('Max', badge=99, specialty='avalanche')", + "name": "elite", + "scope": "function", + "start_column": 4, + "start_line": 169, + "type": "RescuePoliceDog" + }, + { + "end_column": 16, + "end_line": 171, + "initializer": "process_animals([dog, cat, duck, k9, elite])", + "name": "descriptions", + "scope": "function", + "start_column": 4, + "start_line": 171, + "type": "list" + }, + { + "end_column": 10, + "end_line": 172, + "initializer": "[make_sound_twice(a) for a in [dog, cat]]", + "name": "sounds", + "scope": "function", + "start_column": 4, + "start_line": 172, + "type": "list" + }, + { + "end_column": 11, + "end_line": 174, + "initializer": "Config.development()", + "name": "cfg_dev", + "scope": "function", + "start_column": 4, + "start_line": 174, + "type": "Config" + }, + { + "end_column": 12, + "end_line": 175, + "initializer": "Config.production()", + "name": "cfg_prod", + "scope": "function", + "start_column": 4, + "start_line": 175, + "type": "Config" + }, + { + "end_column": 14, + "end_line": 176, + "initializer": "Config.from_dict({'host': '10.0.0.1', 'port': '9090'})", + "name": "cfg_custom", + "scope": "function", + "start_column": 4, + "start_line": 176, + "type": "Config" + }, + { + "end_column": 9, + "end_line": 178, + "initializer": "Config.validate_port(cfg_dev.port)", + "name": "valid", + "scope": "function", + "start_column": 4, + "start_line": 178, + "type": "bool" + }, + { + "end_column": 11, + "end_line": 179, + "initializer": "Animal.kingdom()", + "name": "kingdom", + "scope": "function", + "start_column": 4, + "start_line": 179, + "type": "str" + } + ], + "name": "main", + "parameters": [], + "signature": "main.main", + "span": { + "bytes": [ + 4355, + 4988 + ], + "end": [ + 181, + 78 + ], + "start": [ + 164, + 0 + ] + }, + "start_line": 164, + "summary": [], + "types": {} + }, + "make_sound_twice": { + "accessed_symbols": [ + { + "col_offset": 40, + "is_builtin": false, + "kind": "class", + "lineno": 156, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 156, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "variable", + "lineno": 157, + "name": "animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 157, + "name": "animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "157:14": { + "kind": "call", + "span": { + "bytes": [ + 4150, + 4164 + ], + "end": [ + 157, + 28 + ], + "start": [ + 157, + 14 + ] + } + }, + "157:31": { + "kind": "call", + "span": { + "bytes": [ + 4167, + 4181 + ], + "end": [ + 157, + 45 + ], + "start": [ + 157, + 31 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.speak", + "end_column": 28, + "end_line": 157, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "animal", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 14, + "start_line": 157 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.speak", + "end_column": 45, + "end_line": 157, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "animal", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 31, + "start_line": 157 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 157, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 157, + "id": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "kind": "function", + "local_variables": [], + "name": "make_sound_twice", + "parameters": [ + { + "end_column": 35, + "end_line": 156, + "name": "animal", + "start_column": 21, + "start_line": 156, + "type": "Animal" + } + ], + "return_type": "str", + "signature": "main.make_sound_twice", + "span": { + "bytes": [ + 4091, + 4183 + ], + "end": [ + 157, + 47 + ], + "start": [ + 156, + 0 + ] + }, + "start_line": 156, + "summary": [], + "types": {} + }, + "process_animals": { + "accessed_symbols": [ + { + "col_offset": 46, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 51, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "variable", + "lineno": 153, + "name": "animals", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "variable", + "lineno": 153, + "name": "a", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "153:12": { + "kind": "call", + "span": { + "bytes": [ + 4058, + 4070 + ], + "end": [ + 153, + 24 + ], + "start": [ + 153, + 12 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.describe", + "end_column": 24, + "end_line": 153, + "is_constructor_call": false, + "method_name": "describe", + "receiver_expr": "a", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 12, + "start_line": 153 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 153, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 153, + "id": "can://python/class_hierarchy/main.py/process_animals(animals)", + "kind": "function", + "local_variables": [], + "name": "process_animals", + "parameters": [ + { + "end_column": 41, + "end_line": 152, + "name": "animals", + "start_column": 20, + "start_line": 152, + "type": "List[Animal]" + } + ], + "return_type": "List[str]", + "signature": "main.process_animals", + "span": { + "bytes": [ + 3989, + 4088 + ], + "end": [ + 153, + 42 + ], + "start": [ + 152, + 0 + ] + }, + "start_line": 152, + "summary": [], + "types": {} + } + }, + "id": "can://python/class_hierarchy/main.py", + "imports": [ + { + "end_column": 35, + "end_line": 12, + "module": "abc", + "name": "ABC", + "start_column": 0, + "start_line": 12 + }, + { + "end_column": 35, + "end_line": 12, + "module": "abc", + "name": "abstractmethod", + "start_column": 0, + "start_line": 12 + }, + { + "end_column": 23, + "end_line": 13, + "module": "typing", + "name": "List", + "start_column": 0, + "start_line": 13 + } + ], + "kind": "module", + "module_name": "main", + "source": "\"\"\"Class hierarchy patterns.\n\nExercises:\n- Abstract base class (abc.ABC + @abstractmethod)\n- Multiple inheritance and MRO\n- super() in __init__ and regular methods\n- @classmethod as factory\n- @staticmethod utility\n- __init_subclass__ hook\n- Dynamic dispatch / polymorphism\n\"\"\"\nfrom abc import ABC, abstractmethod\nfrom typing import List\n\n\n# ---------------------------------------------------------------------------\n# 1. Abstract base class\n# ---------------------------------------------------------------------------\n\nclass Animal(ABC):\n _registry: List[\"Animal\"] = []\n\n def __init_subclass__(cls, **kwargs):\n super().__init_subclass__(**kwargs)\n\n def __init__(self, name: str):\n self.name = name\n Animal._registry.append(self)\n\n @abstractmethod\n def speak(self) -> str:\n ...\n\n @classmethod\n def create(cls, name: str) -> \"Animal\":\n return cls(name)\n\n @staticmethod\n def kingdom() -> str:\n return \"Animalia\"\n\n def describe(self) -> str:\n return f\"{self.name} says: {self.speak()}\"\n\n\n# ---------------------------------------------------------------------------\n# 2. Concrete subclasses\n# ---------------------------------------------------------------------------\n\nclass Dog(Animal):\n def __init__(self, name: str):\n super().__init__(name)\n\n def speak(self) -> str:\n return \"Woof\"\n\n def fetch(self, item: str) -> str:\n return f\"{self.name} fetches {item}\"\n\n\nclass Cat(Animal):\n def __init__(self, name: str):\n super().__init__(name)\n\n def speak(self) -> str:\n return \"Meow\"\n\n def purr(self) -> str:\n return f\"{self.name} purrs\"\n\n\n# ---------------------------------------------------------------------------\n# 3. Multiple inheritance + MRO\n# ---------------------------------------------------------------------------\n\nclass Swimmer(ABC):\n @abstractmethod\n def swim(self) -> str:\n ...\n\n\nclass Duck(Animal, Swimmer):\n def __init__(self, name: str):\n super().__init__(name)\n\n def speak(self) -> str:\n return \"Quack\"\n\n def swim(self) -> str:\n return f\"{self.name} paddles\"\n\n\n# ---------------------------------------------------------------------------\n# 4. Deep inheritance chain with super() method call\n# ---------------------------------------------------------------------------\n\nclass PoliceDog(Dog):\n def __init__(self, name: str, badge: int):\n super().__init__(name)\n self.badge = badge\n\n def speak(self) -> str:\n base_bark = super().speak()\n return f\"{base_bark} (K9 unit #{self.badge})\"\n\n\nclass RescuePoliceDog(PoliceDog):\n def __init__(self, name: str, badge: int, specialty: str):\n super().__init__(name, badge)\n self.specialty = specialty\n\n def speak(self) -> str:\n base = super().speak()\n return f\"{base} [{self.specialty}]\"\n\n\n# ---------------------------------------------------------------------------\n# 5. @classmethod factory pattern\n# ---------------------------------------------------------------------------\n\nclass Config:\n def __init__(self, host: str, port: int, debug: bool = False):\n self.host = host\n self.port = port\n self.debug = debug\n\n @classmethod\n def from_dict(cls, d: dict) -> \"Config\":\n return cls(\n host=d.get(\"host\", \"localhost\"),\n port=int(d.get(\"port\", 8080)),\n debug=bool(d.get(\"debug\", False)),\n )\n\n @classmethod\n def development(cls) -> \"Config\":\n return cls(host=\"127.0.0.1\", port=5000, debug=True)\n\n @classmethod\n def production(cls) -> \"Config\":\n return cls(host=\"0.0.0.0\", port=80, debug=False)\n\n @staticmethod\n def validate_port(port: int) -> bool:\n return 1 <= port <= 65535\n\n\n# ---------------------------------------------------------------------------\n# 6. Dynamic dispatch via polymorphism\n# ---------------------------------------------------------------------------\n\ndef process_animals(animals: List[Animal]) -> List[str]:\n return [a.describe() for a in animals]\n\n\ndef make_sound_twice(animal: Animal) -> str:\n return f\"{animal.speak()} {animal.speak()}\"\n\n\n# ---------------------------------------------------------------------------\n# 7. Driver\n# ---------------------------------------------------------------------------\n\ndef main():\n dog = Dog.create(\"Rex\")\n cat = Cat.create(\"Whiskers\")\n duck = Duck.create(\"Donald\")\n k9 = PoliceDog(\"Buddy\", badge=42)\n elite = RescuePoliceDog(\"Max\", badge=99, specialty=\"avalanche\")\n\n descriptions = process_animals([dog, cat, duck, k9, elite])\n sounds = [make_sound_twice(a) for a in [dog, cat]]\n\n cfg_dev = Config.development()\n cfg_prod = Config.production()\n cfg_custom = Config.from_dict({\"host\": \"10.0.0.1\", \"port\": \"9090\"})\n\n valid = Config.validate_port(cfg_dev.port)\n kingdom = Animal.kingdom()\n\n return descriptions, sounds, cfg_dev, cfg_prod, cfg_custom, valid, kingdom\n\n\nif __name__ == \"__main__\":\n main()\n", + "types": { + "main.Animal": { + "attributes": { + "_registry": { + "comments": [], + "end_line": 21, + "initializer": "[]", + "name": "_registry", + "start_line": 21, + "type": "List['Animal']" + } + }, + "base_classes": [ + "ABC" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 27, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 26, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 27, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 32, + "is_builtin": false, + "kind": "variable", + "lineno": 28, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 28, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "28:8": { + "kind": "call", + "span": { + "bytes": [ + 731, + 760 + ], + "end": [ + 28, + 37 + ], + "start": [ + 28, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Animal" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "Animal" + } + ], + "callee_signature": "builtins.list.append", + "end_column": 37, + "end_line": 28, + "is_constructor_call": false, + "method_name": "append", + "receiver_expr": "Animal._registry", + "receiver_type": "Animal", + "start_column": 8, + "start_line": 28 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 27, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 28, + "id": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 27, + "initializer": "name", + "name": "name", + "scope": "class", + "start_column": 8, + "start_line": 27, + "type": "Animal" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 26, + "name": "self", + "start_column": 17, + "start_line": 26, + "type": "Animal" + }, + { + "end_column": 32, + "end_line": 26, + "name": "name", + "start_column": 23, + "start_line": 26, + "type": "str" + } + ], + "signature": "main.Animal.__init__", + "span": { + "bytes": [ + 667, + 760 + ], + "end": [ + 28, + 37 + ], + "start": [ + 26, + 4 + ] + }, + "start_line": 26, + "summary": [], + "types": {} + }, + "__init_subclass__": { + "accessed_symbols": [ + { + "col_offset": 36, + "is_builtin": false, + "kind": "variable", + "lineno": 24, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 24, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "24:8": { + "kind": "call", + "span": { + "bytes": [ + 626, + 633 + ], + "end": [ + 24, + 15 + ], + "start": [ + 24, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.object.__init_subclass__", + "end_column": 43, + "end_line": 24, + "is_constructor_call": false, + "method_name": "__init_subclass__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 24 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 24, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 24 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 24, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 24, + "id": "can://python/class_hierarchy/main.py/Animal/__init_subclass__(cls,kwargs)", + "kind": "function", + "local_variables": [], + "name": "__init_subclass__", + "parameters": [ + { + "end_column": 29, + "end_line": 23, + "name": "cls", + "start_column": 26, + "start_line": 23, + "type": "Animal" + }, + { + "end_column": 39, + "end_line": 23, + "name": "kwargs", + "start_column": 33, + "start_line": 23, + "type": "dict" + } + ], + "signature": "main.Animal.__init_subclass__", + "span": { + "bytes": [ + 580, + 661 + ], + "end": [ + 24, + 43 + ], + "start": [ + 23, + 4 + ] + }, + "start_line": 23, + "summary": [], + "types": {} + }, + "create": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 36, + "name": "cls", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "36:15": { + "kind": "call", + "span": { + "bytes": [ + 899, + 908 + ], + "end": [ + 36, + 24 + ], + "start": [ + 36, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 24, + "end_line": 36, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Animal", + "start_column": 15, + "start_line": 36 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 36, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "classmethod" + ], + "end_line": 36, + "id": "can://python/class_hierarchy/main.py/Animal/create(cls,name)", + "kind": "function", + "local_variables": [], + "name": "create", + "parameters": [ + { + "end_column": 18, + "end_line": 35, + "name": "cls", + "start_column": 15, + "start_line": 35, + "type": "Animal" + }, + { + "end_column": 29, + "end_line": 35, + "name": "name", + "start_column": 20, + "start_line": 35, + "type": "str" + } + ], + "return_type": "'Animal'", + "signature": "main.Animal.create", + "span": { + "bytes": [ + 844, + 908 + ], + "end": [ + 36, + 24 + ], + "start": [ + 35, + 4 + ] + }, + "start_line": 35, + "summary": [], + "types": {} + }, + "describe": { + "accessed_symbols": [ + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 42, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 43, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 36, + "is_builtin": false, + "kind": "variable", + "lineno": 43, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "43:36": { + "kind": "call", + "span": { + "bytes": [ + 1048, + 1060 + ], + "end": [ + 43, + 48 + ], + "start": [ + 43, + 36 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.speak", + "end_column": 48, + "end_line": 43, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "self", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 36, + "start_line": 43 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 43, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 43, + "id": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "kind": "function", + "local_variables": [], + "name": "describe", + "parameters": [ + { + "end_column": 21, + "end_line": 42, + "name": "self", + "start_column": 17, + "start_line": 42, + "type": "Animal" + } + ], + "return_type": "str", + "signature": "main.Animal.describe", + "span": { + "bytes": [ + 985, + 1062 + ], + "end": [ + 43, + 50 + ], + "start": [ + 42, + 4 + ] + }, + "start_line": 42, + "summary": [], + "types": {} + }, + "kingdom": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 38, + "name": "staticmethod", + "qualified_name": "builtins.staticmethod", + "scope": "local", + "type": "staticmethod" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 39, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 40, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "staticmethod" + ], + "end_line": 40, + "id": "can://python/class_hierarchy/main.py/Animal/kingdom()", + "kind": "function", + "local_variables": [], + "name": "kingdom", + "parameters": [], + "return_type": "str", + "signature": "main.Animal.kingdom", + "span": { + "bytes": [ + 932, + 979 + ], + "end": [ + 40, + 25 + ], + "start": [ + 39, + 4 + ] + }, + "start_line": 39, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "function", + "lineno": 30, + "name": "abstractmethod", + "qualified_name": "abc.abstractmethod", + "scope": "local", + "type": "abstractmethod" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 31, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 32, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [ + "abstractmethod" + ], + "end_line": 32, + "id": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 31, + "name": "self", + "start_column": 14, + "start_line": 31, + "type": "Animal" + } + ], + "return_type": "str", + "signature": "main.Animal.speak", + "span": { + "bytes": [ + 786, + 821 + ], + "end": [ + 32, + 11 + ], + "start": [ + 31, + 4 + ] + }, + "start_line": 31, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 43, + "id": "can://python/class_hierarchy/main.py/Animal", + "kind": "class", + "name": "Animal", + "signature": "main.Animal", + "span": { + "bytes": [ + 521, + 1062 + ], + "end": [ + 43, + 50 + ], + "start": [ + 20, + 0 + ] + }, + "start_line": 20, + "types": {} + }, + "main.Cat": { + "attributes": {}, + "base_classes": [ + "Animal" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 62, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 63, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 63, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "63:8": { + "kind": "call", + "span": { + "bytes": [ + 1532, + 1539 + ], + "end": [ + 63, + 15 + ], + "start": [ + 63, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 30, + "end_line": 63, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 63 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 63, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 63 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 63, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 63, + "id": "can://python/class_hierarchy/main.py/Cat/__init__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 62, + "name": "self", + "start_column": 17, + "start_line": 62, + "type": "Cat" + }, + { + "end_column": 32, + "end_line": 62, + "name": "name", + "start_column": 23, + "start_line": 62, + "type": "str" + } + ], + "signature": "main.Cat.__init__", + "span": { + "bytes": [ + 1493, + 1554 + ], + "end": [ + 63, + 30 + ], + "start": [ + 62, + 4 + ] + }, + "start_line": 62, + "summary": [], + "types": {} + }, + "purr": { + "accessed_symbols": [ + { + "col_offset": 22, + "is_builtin": false, + "kind": "class", + "lineno": 68, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 69, + "name": "self", + "qualified_name": "main.Cat", + "scope": "local", + "type": "Cat" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 69, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 69, + "id": "can://python/class_hierarchy/main.py/Cat/purr(self)", + "kind": "function", + "local_variables": [], + "name": "purr", + "parameters": [ + { + "end_column": 17, + "end_line": 68, + "name": "self", + "start_column": 13, + "start_line": 68, + "type": "Cat" + } + ], + "return_type": "str", + "signature": "main.Cat.purr", + "span": { + "bytes": [ + 1611, + 1669 + ], + "end": [ + 69, + 35 + ], + "start": [ + 68, + 4 + ] + }, + "start_line": 68, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 65, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 66, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 66, + "id": "can://python/class_hierarchy/main.py/Cat/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 65, + "name": "self", + "start_column": 14, + "start_line": 65, + "type": "Cat" + } + ], + "return_type": "str", + "signature": "main.Cat.speak", + "span": { + "bytes": [ + 1560, + 1605 + ], + "end": [ + 66, + 21 + ], + "start": [ + 65, + 4 + ] + }, + "start_line": 65, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 69, + "id": "can://python/class_hierarchy/main.py/Cat", + "kind": "class", + "name": "Cat", + "signature": "main.Cat", + "span": { + "bytes": [ + 1470, + 1669 + ], + "end": [ + 69, + 35 + ], + "start": [ + 61, + 0 + ] + }, + "start_line": 61, + "types": {} + }, + "main.Config": { + "attributes": {}, + "base_classes": [], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 123, + "name": "host", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 124, + "name": "port", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 125, + "name": "debug", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 122, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 40, + "is_builtin": false, + "kind": "class", + "lineno": 122, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 52, + "is_builtin": false, + "kind": "class", + "lineno": 122, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 123, + "name": "self", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 124, + "name": "self", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 125, + "name": "self", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 123, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 125, + "id": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 123, + "initializer": "host", + "name": "host", + "scope": "class", + "start_column": 8, + "start_line": 123, + "type": "Config" + }, + { + "end_column": 17, + "end_line": 124, + "initializer": "port", + "name": "port", + "scope": "class", + "start_column": 8, + "start_line": 124, + "type": "Config" + }, + { + "end_column": 18, + "end_line": 125, + "initializer": "debug", + "name": "debug", + "scope": "class", + "start_column": 8, + "start_line": 125, + "type": "Config" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 122, + "name": "self", + "start_column": 17, + "start_line": 122, + "type": "Config" + }, + { + "end_column": 32, + "end_line": 122, + "name": "host", + "start_column": 23, + "start_line": 122, + "type": "str" + }, + { + "end_column": 43, + "end_line": 122, + "name": "port", + "start_column": 34, + "start_line": 122, + "type": "int" + }, + { + "default_value": "False", + "end_column": 56, + "end_line": 122, + "name": "debug", + "start_column": 45, + "start_line": 122, + "type": "bool" + } + ], + "signature": "main.Config.__init__", + "span": { + "bytes": [ + 3100, + 3239 + ], + "end": [ + 125, + 26 + ], + "start": [ + 122, + 4 + ] + }, + "start_line": 122, + "summary": [], + "types": {} + }, + "development": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 135, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 137, + "name": "cls", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + } + ], + "body": { + "137:15": { + "kind": "call", + "span": { + "bytes": [ + 3539, + 3583 + ], + "end": [ + 137, + 59 + ], + "start": [ + 137, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Config.__init__", + "end_column": 59, + "end_line": 137, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Config", + "start_column": 15, + "start_line": 137 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 137, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "classmethod" + ], + "end_line": 137, + "id": "can://python/class_hierarchy/main.py/Config/development(cls)", + "kind": "function", + "local_variables": [], + "name": "development", + "parameters": [ + { + "end_column": 23, + "end_line": 136, + "name": "cls", + "start_column": 20, + "start_line": 136, + "type": "Config" + } + ], + "return_type": "'Config'", + "signature": "main.Config.development", + "span": { + "bytes": [ + 3490, + 3583 + ], + "end": [ + 137, + 59 + ], + "start": [ + 136, + 4 + ] + }, + "start_line": 136, + "summary": [], + "types": {} + }, + "from_dict": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 127, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 128, + "name": "dict", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 129, + "name": "cls", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "class", + "lineno": 131, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "class", + "lineno": 132, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "variable", + "lineno": 130, + "name": "d", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 131, + "name": "d", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 132, + "name": "d", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "129:15": { + "kind": "call", + "span": { + "bytes": [ + 3318, + 3467 + ], + "end": [ + 133, + 9 + ], + "start": [ + 129, + 15 + ] + } + }, + "130:17": { + "kind": "call", + "span": { + "bytes": [ + 3340, + 3366 + ], + "end": [ + 130, + 43 + ], + "start": [ + 130, + 17 + ] + } + }, + "131:17": { + "kind": "call", + "span": { + "bytes": [ + 3385, + 3409 + ], + "end": [ + 131, + 41 + ], + "start": [ + 131, + 17 + ] + } + }, + "131:21": { + "kind": "call", + "span": { + "bytes": [ + 3389, + 3408 + ], + "end": [ + 131, + 40 + ], + "start": [ + 131, + 21 + ] + } + }, + "132:18": { + "kind": "call", + "span": { + "bytes": [ + 3429, + 3456 + ], + "end": [ + 132, + 45 + ], + "start": [ + 132, + 18 + ] + } + }, + "132:23": { + "kind": "call", + "span": { + "bytes": [ + 3434, + 3455 + ], + "end": [ + 132, + 44 + ], + "start": [ + 132, + 23 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Config.__init__", + "end_column": 9, + "end_line": 133, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Config", + "start_column": 15, + "start_line": 129 + }, + { + "argument_types": [ + "str", + "Constant" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + }, + { + "ast_kind": "Constant" + } + ], + "callee_signature": "typing.Mapping.get", + "end_column": 43, + "end_line": 130, + "is_constructor_call": false, + "method_name": "get", + "receiver_expr": "d", + "receiver_type": "dict", + "start_column": 17, + "start_line": 130 + }, + { + "argument_types": [ + "dict" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "dict" + } + ], + "callee_signature": "builtins.int.__init__", + "end_column": 41, + "end_line": 131, + "is_constructor_call": true, + "method_name": "int", + "return_type": "int", + "start_column": 17, + "start_line": 131 + }, + { + "argument_types": [ + "str", + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "typing.Mapping.get", + "end_column": 40, + "end_line": 131, + "is_constructor_call": false, + "method_name": "get", + "receiver_expr": "d", + "receiver_type": "dict", + "start_column": 21, + "start_line": 131 + }, + { + "argument_types": [ + "dict" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "dict" + } + ], + "callee_signature": "builtins.bool.__init__", + "end_column": 45, + "end_line": 132, + "is_constructor_call": true, + "method_name": "bool", + "return_type": "bool", + "start_column": 18, + "start_line": 132 + }, + { + "argument_types": [ + "str", + "Constant" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + }, + { + "ast_kind": "Constant" + } + ], + "callee_signature": "typing.Mapping.get", + "end_column": 44, + "end_line": 132, + "is_constructor_call": false, + "method_name": "get", + "receiver_expr": "d", + "receiver_type": "dict", + "start_column": 23, + "start_line": 132 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 129, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "classmethod" + ], + "end_line": 133, + "id": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "kind": "function", + "local_variables": [], + "name": "from_dict", + "parameters": [ + { + "end_column": 21, + "end_line": 128, + "name": "cls", + "start_column": 18, + "start_line": 128, + "type": "Config" + }, + { + "end_column": 30, + "end_line": 128, + "name": "d", + "start_column": 23, + "start_line": 128, + "type": "dict" + } + ], + "return_type": "'Config'", + "signature": "main.Config.from_dict", + "span": { + "bytes": [ + 3262, + 3467 + ], + "end": [ + 133, + 9 + ], + "start": [ + 128, + 4 + ] + }, + "start_line": 128, + "summary": [], + "types": {} + }, + "production": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 139, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 141, + "name": "cls", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + } + ], + "body": { + "141:15": { + "kind": "call", + "span": { + "bytes": [ + 3654, + 3695 + ], + "end": [ + 141, + 56 + ], + "start": [ + 141, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Config.__init__", + "end_column": 56, + "end_line": 141, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Config", + "start_column": 15, + "start_line": 141 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 141, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "classmethod" + ], + "end_line": 141, + "id": "can://python/class_hierarchy/main.py/Config/production(cls)", + "kind": "function", + "local_variables": [], + "name": "production", + "parameters": [ + { + "end_column": 22, + "end_line": 140, + "name": "cls", + "start_column": 19, + "start_line": 140, + "type": "Config" + } + ], + "return_type": "'Config'", + "signature": "main.Config.production", + "span": { + "bytes": [ + 3606, + 3695 + ], + "end": [ + 141, + 56 + ], + "start": [ + 140, + 4 + ] + }, + "start_line": 140, + "summary": [], + "types": {} + }, + "validate_port": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 143, + "name": "staticmethod", + "qualified_name": "builtins.staticmethod", + "scope": "local", + "type": "staticmethod" + }, + { + "col_offset": 36, + "is_builtin": false, + "kind": "class", + "lineno": 144, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 28, + "is_builtin": false, + "kind": "class", + "lineno": 144, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 145, + "name": "port", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 145, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "staticmethod" + ], + "end_line": 145, + "id": "can://python/class_hierarchy/main.py/Config/validate_port(port)", + "kind": "function", + "local_variables": [], + "name": "validate_port", + "parameters": [ + { + "end_column": 31, + "end_line": 144, + "name": "port", + "start_column": 22, + "start_line": 144, + "type": "int" + } + ], + "return_type": "bool", + "signature": "main.Config.validate_port", + "span": { + "bytes": [ + 3719, + 3790 + ], + "end": [ + 145, + 33 + ], + "start": [ + 144, + 4 + ] + }, + "start_line": 144, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 145, + "id": "can://python/class_hierarchy/main.py/Config", + "kind": "class", + "name": "Config", + "signature": "main.Config", + "span": { + "bytes": [ + 3082, + 3790 + ], + "end": [ + 145, + 33 + ], + "start": [ + 121, + 0 + ] + }, + "start_line": 121, + "types": {} + }, + "main.Dog": { + "attributes": {}, + "base_classes": [ + "Animal" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 51, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 52, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 52, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "52:8": { + "kind": "call", + "span": { + "bytes": [ + 1309, + 1316 + ], + "end": [ + 52, + 15 + ], + "start": [ + 52, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 30, + "end_line": 52, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 52 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 52, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 52 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 52, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 52, + "id": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 51, + "name": "self", + "start_column": 17, + "start_line": 51, + "type": "Dog" + }, + { + "end_column": 32, + "end_line": 51, + "name": "name", + "start_column": 23, + "start_line": 51, + "type": "str" + } + ], + "signature": "main.Dog.__init__", + "span": { + "bytes": [ + 1270, + 1331 + ], + "end": [ + 52, + 30 + ], + "start": [ + 51, + 4 + ] + }, + "start_line": 51, + "summary": [], + "types": {} + }, + "fetch": { + "accessed_symbols": [ + { + "col_offset": 34, + "is_builtin": false, + "kind": "class", + "lineno": 57, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 57, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 38, + "is_builtin": false, + "kind": "variable", + "lineno": 58, + "name": "item", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 58, + "name": "self", + "qualified_name": "main.Dog", + "scope": "local", + "type": "Dog" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 58, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 58, + "id": "can://python/class_hierarchy/main.py/Dog/fetch(self,item)", + "kind": "function", + "local_variables": [], + "name": "fetch", + "parameters": [ + { + "end_column": 18, + "end_line": 57, + "name": "self", + "start_column": 14, + "start_line": 57, + "type": "Dog" + }, + { + "end_column": 29, + "end_line": 57, + "name": "item", + "start_column": 20, + "start_line": 57, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.Dog.fetch", + "span": { + "bytes": [ + 1388, + 1467 + ], + "end": [ + 58, + 44 + ], + "start": [ + 57, + 4 + ] + }, + "start_line": 57, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 54, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 55, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 55, + "id": "can://python/class_hierarchy/main.py/Dog/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 54, + "name": "self", + "start_column": 14, + "start_line": 54, + "type": "Dog" + } + ], + "return_type": "str", + "signature": "main.Dog.speak", + "span": { + "bytes": [ + 1337, + 1382 + ], + "end": [ + 55, + 21 + ], + "start": [ + 54, + 4 + ] + }, + "start_line": 54, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 58, + "id": "can://python/class_hierarchy/main.py/Dog", + "kind": "class", + "name": "Dog", + "signature": "main.Dog", + "span": { + "bytes": [ + 1247, + 1467 + ], + "end": [ + 58, + 44 + ], + "start": [ + 50, + 0 + ] + }, + "start_line": 50, + "types": {} + }, + "main.Duck": { + "attributes": {}, + "base_classes": [ + "Animal", + "Swimmer" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 83, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 84, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 84, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "84:8": { + "kind": "call", + "span": { + "bytes": [ + 2014, + 2021 + ], + "end": [ + 84, + 15 + ], + "start": [ + 84, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 30, + "end_line": 84, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 84 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 84, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 84 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 84, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 84, + "id": "can://python/class_hierarchy/main.py/Duck/__init__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 83, + "name": "self", + "start_column": 17, + "start_line": 83, + "type": "Duck" + }, + { + "end_column": 32, + "end_line": 83, + "name": "name", + "start_column": 23, + "start_line": 83, + "type": "str" + } + ], + "signature": "main.Duck.__init__", + "span": { + "bytes": [ + 1975, + 2036 + ], + "end": [ + 84, + 30 + ], + "start": [ + 83, + 4 + ] + }, + "start_line": 83, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 86, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 87, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 87, + "id": "can://python/class_hierarchy/main.py/Duck/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 86, + "name": "self", + "start_column": 14, + "start_line": 86, + "type": "Duck" + } + ], + "return_type": "str", + "signature": "main.Duck.speak", + "span": { + "bytes": [ + 2042, + 2088 + ], + "end": [ + 87, + 22 + ], + "start": [ + 86, + 4 + ] + }, + "start_line": 86, + "summary": [], + "types": {} + }, + "swim": { + "accessed_symbols": [ + { + "col_offset": 22, + "is_builtin": false, + "kind": "class", + "lineno": 89, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 90, + "name": "self", + "qualified_name": "main.Duck", + "scope": "local", + "type": "Duck" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 90, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 90, + "id": "can://python/class_hierarchy/main.py/Duck/swim(self)", + "kind": "function", + "local_variables": [], + "name": "swim", + "parameters": [ + { + "end_column": 17, + "end_line": 89, + "name": "self", + "start_column": 13, + "start_line": 89, + "type": "Duck" + } + ], + "return_type": "str", + "signature": "main.Duck.swim", + "span": { + "bytes": [ + 2094, + 2154 + ], + "end": [ + 90, + 37 + ], + "start": [ + 89, + 4 + ] + }, + "start_line": 89, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 90, + "id": "can://python/class_hierarchy/main.py/Duck", + "kind": "class", + "name": "Duck", + "signature": "main.Duck", + "span": { + "bytes": [ + 1942, + 2154 + ], + "end": [ + 90, + 37 + ], + "start": [ + 82, + 0 + ] + }, + "start_line": 82, + "types": {} + }, + "main.PoliceDog": { + "attributes": {}, + "base_classes": [ + "Dog" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 100, + "name": "badge", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 99, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 100, + "name": "self", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 99, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "99:8": { + "kind": "call", + "span": { + "bytes": [ + 2444, + 2451 + ], + "end": [ + 99, + 15 + ], + "start": [ + 99, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Dog.__init__", + "end_column": 30, + "end_line": 99, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 99 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 99, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 99 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 99, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 100, + "id": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 100, + "initializer": "badge", + "name": "badge", + "scope": "class", + "start_column": 8, + "start_line": 100, + "type": "PoliceDog" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 98, + "name": "self", + "start_column": 17, + "start_line": 98, + "type": "PoliceDog" + }, + { + "end_column": 32, + "end_line": 98, + "name": "name", + "start_column": 23, + "start_line": 98, + "type": "str" + }, + { + "end_column": 44, + "end_line": 98, + "name": "badge", + "start_column": 34, + "start_line": 98, + "type": "int" + } + ], + "signature": "main.PoliceDog.__init__", + "span": { + "bytes": [ + 2393, + 2493 + ], + "end": [ + 100, + 26 + ], + "start": [ + 98, + 4 + ] + }, + "start_line": 98, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 102, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 104, + "name": "base_bark", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "class", + "lineno": 103, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + }, + { + "col_offset": 40, + "is_builtin": false, + "kind": "variable", + "lineno": 104, + "name": "self", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + } + ], + "body": { + "103:20": { + "kind": "call", + "span": { + "bytes": [ + 2543, + 2550 + ], + "end": [ + 103, + 27 + ], + "start": [ + 103, + 20 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Dog.speak", + "end_column": 35, + "end_line": 103, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "super()", + "receiver_type": "super", + "return_type": "str", + "start_column": 20, + "start_line": 103 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 27, + "end_line": 103, + "is_constructor_call": true, + "method_name": "super", + "start_column": 20, + "start_line": 103 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 103, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 104, + "id": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 103, + "initializer": "super().speak()", + "name": "base_bark", + "scope": "function", + "start_column": 8, + "start_line": 103, + "type": "str" + } + ], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 102, + "name": "self", + "start_column": 14, + "start_line": 102, + "type": "PoliceDog" + } + ], + "return_type": "str", + "signature": "main.PoliceDog.speak", + "span": { + "bytes": [ + 2499, + 2612 + ], + "end": [ + 104, + 53 + ], + "start": [ + 102, + 4 + ] + }, + "start_line": 102, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 104, + "id": "can://python/class_hierarchy/main.py/PoliceDog", + "kind": "class", + "name": "PoliceDog", + "signature": "main.PoliceDog", + "span": { + "bytes": [ + 2367, + 2612 + ], + "end": [ + 104, + 53 + ], + "start": [ + 97, + 0 + ] + }, + "start_line": 97, + "types": {} + }, + "main.RescuePoliceDog": { + "attributes": {}, + "base_classes": [ + "PoliceDog" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 110, + "name": "specialty", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 108, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "class", + "lineno": 108, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 57, + "is_builtin": false, + "kind": "class", + "lineno": 108, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 109, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 109, + "name": "badge", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 110, + "name": "self", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 109, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "109:8": { + "kind": "call", + "span": { + "bytes": [ + 2720, + 2727 + ], + "end": [ + 109, + 15 + ], + "start": [ + 109, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + }, + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "main.PoliceDog.__init__", + "end_column": 37, + "end_line": 109, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 109 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 109, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 109 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 109, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 110, + "id": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "kind": "function", + "local_variables": [ + { + "end_column": 22, + "end_line": 110, + "initializer": "specialty", + "name": "specialty", + "scope": "class", + "start_column": 8, + "start_line": 110, + "type": "RescuePoliceDog" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 108, + "name": "self", + "start_column": 17, + "start_line": 108, + "type": "RescuePoliceDog" + }, + { + "end_column": 32, + "end_line": 108, + "name": "name", + "start_column": 23, + "start_line": 108, + "type": "str" + }, + { + "end_column": 44, + "end_line": 108, + "name": "badge", + "start_column": 34, + "start_line": 108, + "type": "int" + }, + { + "end_column": 60, + "end_line": 108, + "name": "specialty", + "start_column": 46, + "start_line": 108, + "type": "str" + } + ], + "signature": "main.RescuePoliceDog.__init__", + "span": { + "bytes": [ + 2653, + 2784 + ], + "end": [ + 110, + 34 + ], + "start": [ + 108, + 4 + ] + }, + "start_line": 108, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 112, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 114, + "name": "base", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 113, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 114, + "name": "self", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + } + ], + "body": { + "113:15": { + "kind": "call", + "span": { + "bytes": [ + 2829, + 2836 + ], + "end": [ + 113, + 22 + ], + "start": [ + 113, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.PoliceDog.speak", + "end_column": 30, + "end_line": 113, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "super()", + "receiver_type": "super", + "return_type": "str", + "start_column": 15, + "start_line": 113 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 22, + "end_line": 113, + "is_constructor_call": true, + "method_name": "super", + "start_column": 15, + "start_line": 113 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 113, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 114, + "id": "can://python/class_hierarchy/main.py/RescuePoliceDog/speak(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 12, + "end_line": 113, + "initializer": "super().speak()", + "name": "base", + "scope": "function", + "start_column": 8, + "start_line": 113, + "type": "str" + } + ], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 112, + "name": "self", + "start_column": 14, + "start_line": 112, + "type": "RescuePoliceDog" + } + ], + "return_type": "str", + "signature": "main.RescuePoliceDog.speak", + "span": { + "bytes": [ + 2790, + 2888 + ], + "end": [ + 114, + 43 + ], + "start": [ + 112, + 4 + ] + }, + "start_line": 112, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 114, + "id": "can://python/class_hierarchy/main.py/RescuePoliceDog", + "kind": "class", + "name": "RescuePoliceDog", + "signature": "main.RescuePoliceDog", + "span": { + "bytes": [ + 2615, + 2888 + ], + "end": [ + 114, + 43 + ], + "start": [ + 107, + 0 + ] + }, + "start_line": 107, + "types": {} + }, + "main.Swimmer": { + "attributes": {}, + "base_classes": [ + "ABC" + ], + "callables": { + "swim": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "abstractmethod", + "qualified_name": "abc.abstractmethod", + "scope": "local", + "type": "abstractmethod" + }, + { + "col_offset": 22, + "is_builtin": false, + "kind": "class", + "lineno": 78, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 79, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [ + "abstractmethod" + ], + "end_line": 79, + "id": "can://python/class_hierarchy/main.py/Swimmer/swim(self)", + "kind": "function", + "local_variables": [], + "name": "swim", + "parameters": [ + { + "end_column": 17, + "end_line": 78, + "name": "self", + "start_column": 13, + "start_line": 78, + "type": "Swimmer" + } + ], + "return_type": "str", + "signature": "main.Swimmer.swim", + "span": { + "bytes": [ + 1905, + 1939 + ], + "end": [ + 79, + 11 + ], + "start": [ + 78, + 4 + ] + }, + "start_line": 78, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 79, + "id": "can://python/class_hierarchy/main.py/Swimmer", + "kind": "class", + "name": "Swimmer", + "signature": "main.Swimmer", + "span": { + "bytes": [ + 1861, + 1939 + ], + "end": [ + 79, + 11 + ], + "start": [ + 76, + 0 + ] + }, + "start_line": 76, + "types": {} + } + }, + "variables": [] + } + } + }, + "language": "python", + "max_level": 1, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/class_hierarchy.a2.json b/test/golden/pipeline_equivalence/class_hierarchy.a2.json new file mode 100644 index 0000000..bd886ae --- /dev/null +++ b/test/golden/pipeline_equivalence/class_hierarchy.a2.json @@ -0,0 +1,4898 @@ +{ + "analyzer": { + "config": { + "analysis_level": 2 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/class_hierarchy/@external/builtins.list/append", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.object/__init_subclass__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/__init_subclass__(cls,kwargs)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/__init_subclass__(cls,kwargs)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/create(cls,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Cat/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Cat/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/development(cls)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.bool/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.int/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/typing.Mapping/get", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 3 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/production(cls)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Duck/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Duck/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Dog/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 6 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/kingdom()", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/validate_port(port)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/process_animals(animals)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "weight": 2 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/process_animals(animals)", + "weight": 1 + } + ], + "external_symbols": { + "can://python/class_hierarchy/@external/builtins.bool/__init__": { + "id": "can://python/class_hierarchy/@external/builtins.bool/__init__", + "kind": "external", + "module": "builtins.bool", + "name": "__init__" + }, + "can://python/class_hierarchy/@external/builtins.classmethod/__get__": { + "id": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "external", + "module": "builtins.classmethod", + "name": "__get__" + }, + "can://python/class_hierarchy/@external/builtins.int/__init__": { + "id": "can://python/class_hierarchy/@external/builtins.int/__init__", + "kind": "external", + "module": "builtins.int", + "name": "__init__" + }, + "can://python/class_hierarchy/@external/builtins.list/append": { + "id": "can://python/class_hierarchy/@external/builtins.list/append", + "kind": "external", + "module": "builtins.list", + "name": "append" + }, + "can://python/class_hierarchy/@external/builtins.object/__init_subclass__": { + "id": "can://python/class_hierarchy/@external/builtins.object/__init_subclass__", + "kind": "external", + "module": "builtins.object", + "name": "__init_subclass__" + }, + "can://python/class_hierarchy/@external/builtins.super/__init__": { + "id": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "external", + "module": "builtins.super", + "name": "__init__" + }, + "can://python/class_hierarchy/@external/typing.Mapping/get": { + "id": "can://python/class_hierarchy/@external/typing.Mapping/get", + "kind": "external", + "module": "typing.Mapping", + "name": "get" + } + }, + "id": "can://python/class_hierarchy", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [ + { + "content": "Class hierarchy patterns.\n\nExercises:\n- Abstract base class (abc.ABC + @abstractmethod)\n- Multiple inheritance and MRO\n- super() in __init__ and regular methods\n- @classmethod as factory\n- @staticmethod utility\n- __init_subclass__ hook\n- Dynamic dispatch / polymorphism\n", + "end_column": 3, + "end_line": 11, + "is_docstring": true, + "start_column": 0, + "start_line": 1 + } + ], + "content_hash": "c09e8f66fc9464c0e77b889a31ca1bab0aeb2eea376d77016d132dc67e6bebd2", + "file_size": 5029, + "functions": { + "main": { + "accessed_symbols": [ + { + "col_offset": 9, + "is_builtin": false, + "kind": "class", + "lineno": 168, + "name": "PoliceDog", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "class", + "lineno": 169, + "name": "RescuePoliceDog", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "function", + "lineno": 171, + "name": "process_animals", + "qualified_name": "main.process_animals", + "scope": "local", + "type": "process_animals" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "descriptions", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "sounds", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "cfg_dev", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 42, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "cfg_prod", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 52, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "cfg_custom", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 64, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "valid", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 71, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "kingdom", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 10, + "is_builtin": false, + "kind": "class", + "lineno": 165, + "name": "Dog", + "qualified_name": "main.Dog", + "scope": "local", + "type": "Dog" + }, + { + "col_offset": 10, + "is_builtin": false, + "kind": "class", + "lineno": 166, + "name": "Cat", + "qualified_name": "main.Cat", + "scope": "local", + "type": "Cat" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "class", + "lineno": 167, + "name": "Duck", + "qualified_name": "main.Duck", + "scope": "local", + "type": "Duck" + }, + { + "col_offset": 36, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "dog", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "cat", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 46, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "duck", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 52, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "k9", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + }, + { + "col_offset": 56, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "elite", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "function", + "lineno": 172, + "name": "make_sound_twice", + "qualified_name": "main.make_sound_twice", + "scope": "local", + "type": "make_sound_twice" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 172, + "name": "a", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "class", + "lineno": 174, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 175, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "class", + "lineno": 176, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "class", + "lineno": 178, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 178, + "name": "cfg_dev", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "class", + "lineno": 179, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 44, + "is_builtin": false, + "kind": "variable", + "lineno": 172, + "name": "dog", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 49, + "is_builtin": false, + "kind": "variable", + "lineno": 172, + "name": "cat", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "165:10": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4377, + 4394 + ], + "end": [ + 165, + 27 + ], + "start": [ + 165, + 10 + ] + } + }, + "166:10": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4405, + 4427 + ], + "end": [ + 166, + 32 + ], + "start": [ + 166, + 10 + ] + } + }, + "167:11": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4439, + 4460 + ], + "end": [ + 167, + 32 + ], + "start": [ + 167, + 11 + ] + } + }, + "168:9": { + "callee": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "kind": "call", + "span": { + "bytes": [ + 4470, + 4498 + ], + "end": [ + 168, + 37 + ], + "start": [ + 168, + 9 + ] + } + }, + "169:12": { + "callee": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "kind": "call", + "span": { + "bytes": [ + 4511, + 4566 + ], + "end": [ + 169, + 67 + ], + "start": [ + 169, + 12 + ] + } + }, + "171:19": { + "callee": "can://python/class_hierarchy/main.py/process_animals(animals)", + "kind": "call", + "span": { + "bytes": [ + 4587, + 4631 + ], + "end": [ + 171, + 63 + ], + "start": [ + 171, + 19 + ] + } + }, + "172:14": { + "callee": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "kind": "call", + "span": { + "bytes": [ + 4646, + 4665 + ], + "end": [ + 172, + 33 + ], + "start": [ + 172, + 14 + ] + } + }, + "174:14": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4702, + 4722 + ], + "end": [ + 174, + 34 + ], + "start": [ + 174, + 14 + ] + } + }, + "175:15": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4738, + 4757 + ], + "end": [ + 175, + 34 + ], + "start": [ + 175, + 15 + ] + } + }, + "176:17": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4775, + 4829 + ], + "end": [ + 176, + 71 + ], + "start": [ + 176, + 17 + ] + } + }, + "178:12": { + "callee": "can://python/class_hierarchy/main.py/Config/validate_port(port)", + "kind": "call", + "span": { + "bytes": [ + 4843, + 4877 + ], + "end": [ + 178, + 46 + ], + "start": [ + 178, + 12 + ] + } + }, + "179:14": { + "callee": "can://python/class_hierarchy/main.py/Animal/kingdom()", + "kind": "call", + "span": { + "bytes": [ + 4892, + 4908 + ], + "end": [ + 179, + 30 + ], + "start": [ + 179, + 14 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 27, + "end_line": 165, + "is_constructor_call": false, + "method_name": "create", + "receiver_expr": "Dog", + "receiver_type": "Dog", + "return_type": "Animal", + "start_column": 10, + "start_line": 165 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 32, + "end_line": 166, + "is_constructor_call": false, + "method_name": "create", + "receiver_expr": "Cat", + "receiver_type": "Cat", + "return_type": "Animal", + "start_column": 10, + "start_line": 166 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 32, + "end_line": 167, + "is_constructor_call": false, + "method_name": "create", + "receiver_expr": "Duck", + "receiver_type": "Duck", + "return_type": "Animal", + "start_column": 11, + "start_line": 167 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "main.PoliceDog.__init__", + "end_column": 37, + "end_line": 168, + "is_constructor_call": true, + "method_name": "PoliceDog", + "return_type": "PoliceDog", + "start_column": 9, + "start_line": 168 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "main.RescuePoliceDog.__init__", + "end_column": 67, + "end_line": 169, + "is_constructor_call": true, + "method_name": "RescuePoliceDog", + "return_type": "RescuePoliceDog", + "start_column": 12, + "start_line": 169 + }, + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "List", + "inferred_type": "list" + } + ], + "callee_signature": "main.process_animals", + "end_column": 63, + "end_line": 171, + "is_constructor_call": false, + "method_name": "process_animals", + "return_type": "list", + "start_column": 19, + "start_line": 171 + }, + { + "argument_types": [ + "Animal" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "Animal" + } + ], + "callee_signature": "main.make_sound_twice", + "end_column": 33, + "end_line": 172, + "is_constructor_call": false, + "method_name": "make_sound_twice", + "return_type": "str", + "start_column": 14, + "start_line": 172 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 34, + "end_line": 174, + "is_constructor_call": false, + "method_name": "development", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "Config", + "start_column": 14, + "start_line": 174 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 34, + "end_line": 175, + "is_constructor_call": false, + "method_name": "production", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "Config", + "start_column": 15, + "start_line": 175 + }, + { + "argument_types": [ + "Config" + ], + "arguments": [ + { + "ast_kind": "Dict", + "inferred_type": "Config" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 71, + "end_line": 176, + "is_constructor_call": false, + "method_name": "from_dict", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "Config", + "start_column": 17, + "start_line": 176 + }, + { + "argument_types": [ + "Config" + ], + "arguments": [ + { + "ast_kind": "Attribute", + "inferred_type": "Config" + } + ], + "callee_signature": "main.Config.validate_port", + "end_column": 46, + "end_line": 178, + "is_constructor_call": false, + "method_name": "validate_port", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "bool", + "start_column": 12, + "start_line": 178 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.kingdom", + "end_column": 30, + "end_line": 179, + "is_constructor_call": false, + "method_name": "kingdom", + "receiver_expr": "Animal", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 14, + "start_line": 179 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 165, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 181, + "id": "can://python/class_hierarchy/main.py/main()", + "kind": "function", + "local_variables": [ + { + "end_column": 7, + "end_line": 165, + "initializer": "Dog.create('Rex')", + "name": "dog", + "scope": "function", + "start_column": 4, + "start_line": 165, + "type": "Animal" + }, + { + "end_column": 7, + "end_line": 166, + "initializer": "Cat.create('Whiskers')", + "name": "cat", + "scope": "function", + "start_column": 4, + "start_line": 166, + "type": "Animal" + }, + { + "end_column": 8, + "end_line": 167, + "initializer": "Duck.create('Donald')", + "name": "duck", + "scope": "function", + "start_column": 4, + "start_line": 167, + "type": "Animal" + }, + { + "end_column": 6, + "end_line": 168, + "initializer": "PoliceDog('Buddy', badge=42)", + "name": "k9", + "scope": "function", + "start_column": 4, + "start_line": 168, + "type": "PoliceDog" + }, + { + "end_column": 9, + "end_line": 169, + "initializer": "RescuePoliceDog('Max', badge=99, specialty='avalanche')", + "name": "elite", + "scope": "function", + "start_column": 4, + "start_line": 169, + "type": "RescuePoliceDog" + }, + { + "end_column": 16, + "end_line": 171, + "initializer": "process_animals([dog, cat, duck, k9, elite])", + "name": "descriptions", + "scope": "function", + "start_column": 4, + "start_line": 171, + "type": "list" + }, + { + "end_column": 10, + "end_line": 172, + "initializer": "[make_sound_twice(a) for a in [dog, cat]]", + "name": "sounds", + "scope": "function", + "start_column": 4, + "start_line": 172, + "type": "list" + }, + { + "end_column": 11, + "end_line": 174, + "initializer": "Config.development()", + "name": "cfg_dev", + "scope": "function", + "start_column": 4, + "start_line": 174, + "type": "Config" + }, + { + "end_column": 12, + "end_line": 175, + "initializer": "Config.production()", + "name": "cfg_prod", + "scope": "function", + "start_column": 4, + "start_line": 175, + "type": "Config" + }, + { + "end_column": 14, + "end_line": 176, + "initializer": "Config.from_dict({'host': '10.0.0.1', 'port': '9090'})", + "name": "cfg_custom", + "scope": "function", + "start_column": 4, + "start_line": 176, + "type": "Config" + }, + { + "end_column": 9, + "end_line": 178, + "initializer": "Config.validate_port(cfg_dev.port)", + "name": "valid", + "scope": "function", + "start_column": 4, + "start_line": 178, + "type": "bool" + }, + { + "end_column": 11, + "end_line": 179, + "initializer": "Animal.kingdom()", + "name": "kingdom", + "scope": "function", + "start_column": 4, + "start_line": 179, + "type": "str" + } + ], + "name": "main", + "parameters": [], + "signature": "main.main", + "span": { + "bytes": [ + 4355, + 4988 + ], + "end": [ + 181, + 78 + ], + "start": [ + 164, + 0 + ] + }, + "start_line": 164, + "summary": [], + "types": {} + }, + "make_sound_twice": { + "accessed_symbols": [ + { + "col_offset": 40, + "is_builtin": false, + "kind": "class", + "lineno": 156, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 156, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "variable", + "lineno": 157, + "name": "animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 157, + "name": "animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "157:14": { + "callee": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "kind": "call", + "span": { + "bytes": [ + 4150, + 4164 + ], + "end": [ + 157, + 28 + ], + "start": [ + 157, + 14 + ] + } + }, + "157:31": { + "callee": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "kind": "call", + "span": { + "bytes": [ + 4167, + 4181 + ], + "end": [ + 157, + 45 + ], + "start": [ + 157, + 31 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.speak", + "end_column": 28, + "end_line": 157, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "animal", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 14, + "start_line": 157 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.speak", + "end_column": 45, + "end_line": 157, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "animal", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 31, + "start_line": 157 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 157, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 157, + "id": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "kind": "function", + "local_variables": [], + "name": "make_sound_twice", + "parameters": [ + { + "end_column": 35, + "end_line": 156, + "name": "animal", + "start_column": 21, + "start_line": 156, + "type": "Animal" + } + ], + "return_type": "str", + "signature": "main.make_sound_twice", + "span": { + "bytes": [ + 4091, + 4183 + ], + "end": [ + 157, + 47 + ], + "start": [ + 156, + 0 + ] + }, + "start_line": 156, + "summary": [], + "types": {} + }, + "process_animals": { + "accessed_symbols": [ + { + "col_offset": 46, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 51, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "variable", + "lineno": 153, + "name": "animals", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "variable", + "lineno": 153, + "name": "a", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "153:12": { + "callee": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "kind": "call", + "span": { + "bytes": [ + 4058, + 4070 + ], + "end": [ + 153, + 24 + ], + "start": [ + 153, + 12 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.describe", + "end_column": 24, + "end_line": 153, + "is_constructor_call": false, + "method_name": "describe", + "receiver_expr": "a", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 12, + "start_line": 153 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 153, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 153, + "id": "can://python/class_hierarchy/main.py/process_animals(animals)", + "kind": "function", + "local_variables": [], + "name": "process_animals", + "parameters": [ + { + "end_column": 41, + "end_line": 152, + "name": "animals", + "start_column": 20, + "start_line": 152, + "type": "List[Animal]" + } + ], + "return_type": "List[str]", + "signature": "main.process_animals", + "span": { + "bytes": [ + 3989, + 4088 + ], + "end": [ + 153, + 42 + ], + "start": [ + 152, + 0 + ] + }, + "start_line": 152, + "summary": [], + "types": {} + } + }, + "id": "can://python/class_hierarchy/main.py", + "imports": [ + { + "end_column": 35, + "end_line": 12, + "module": "abc", + "name": "ABC", + "start_column": 0, + "start_line": 12 + }, + { + "end_column": 35, + "end_line": 12, + "module": "abc", + "name": "abstractmethod", + "start_column": 0, + "start_line": 12 + }, + { + "end_column": 23, + "end_line": 13, + "module": "typing", + "name": "List", + "start_column": 0, + "start_line": 13 + } + ], + "kind": "module", + "module_name": "main", + "source": "\"\"\"Class hierarchy patterns.\n\nExercises:\n- Abstract base class (abc.ABC + @abstractmethod)\n- Multiple inheritance and MRO\n- super() in __init__ and regular methods\n- @classmethod as factory\n- @staticmethod utility\n- __init_subclass__ hook\n- Dynamic dispatch / polymorphism\n\"\"\"\nfrom abc import ABC, abstractmethod\nfrom typing import List\n\n\n# ---------------------------------------------------------------------------\n# 1. Abstract base class\n# ---------------------------------------------------------------------------\n\nclass Animal(ABC):\n _registry: List[\"Animal\"] = []\n\n def __init_subclass__(cls, **kwargs):\n super().__init_subclass__(**kwargs)\n\n def __init__(self, name: str):\n self.name = name\n Animal._registry.append(self)\n\n @abstractmethod\n def speak(self) -> str:\n ...\n\n @classmethod\n def create(cls, name: str) -> \"Animal\":\n return cls(name)\n\n @staticmethod\n def kingdom() -> str:\n return \"Animalia\"\n\n def describe(self) -> str:\n return f\"{self.name} says: {self.speak()}\"\n\n\n# ---------------------------------------------------------------------------\n# 2. Concrete subclasses\n# ---------------------------------------------------------------------------\n\nclass Dog(Animal):\n def __init__(self, name: str):\n super().__init__(name)\n\n def speak(self) -> str:\n return \"Woof\"\n\n def fetch(self, item: str) -> str:\n return f\"{self.name} fetches {item}\"\n\n\nclass Cat(Animal):\n def __init__(self, name: str):\n super().__init__(name)\n\n def speak(self) -> str:\n return \"Meow\"\n\n def purr(self) -> str:\n return f\"{self.name} purrs\"\n\n\n# ---------------------------------------------------------------------------\n# 3. Multiple inheritance + MRO\n# ---------------------------------------------------------------------------\n\nclass Swimmer(ABC):\n @abstractmethod\n def swim(self) -> str:\n ...\n\n\nclass Duck(Animal, Swimmer):\n def __init__(self, name: str):\n super().__init__(name)\n\n def speak(self) -> str:\n return \"Quack\"\n\n def swim(self) -> str:\n return f\"{self.name} paddles\"\n\n\n# ---------------------------------------------------------------------------\n# 4. Deep inheritance chain with super() method call\n# ---------------------------------------------------------------------------\n\nclass PoliceDog(Dog):\n def __init__(self, name: str, badge: int):\n super().__init__(name)\n self.badge = badge\n\n def speak(self) -> str:\n base_bark = super().speak()\n return f\"{base_bark} (K9 unit #{self.badge})\"\n\n\nclass RescuePoliceDog(PoliceDog):\n def __init__(self, name: str, badge: int, specialty: str):\n super().__init__(name, badge)\n self.specialty = specialty\n\n def speak(self) -> str:\n base = super().speak()\n return f\"{base} [{self.specialty}]\"\n\n\n# ---------------------------------------------------------------------------\n# 5. @classmethod factory pattern\n# ---------------------------------------------------------------------------\n\nclass Config:\n def __init__(self, host: str, port: int, debug: bool = False):\n self.host = host\n self.port = port\n self.debug = debug\n\n @classmethod\n def from_dict(cls, d: dict) -> \"Config\":\n return cls(\n host=d.get(\"host\", \"localhost\"),\n port=int(d.get(\"port\", 8080)),\n debug=bool(d.get(\"debug\", False)),\n )\n\n @classmethod\n def development(cls) -> \"Config\":\n return cls(host=\"127.0.0.1\", port=5000, debug=True)\n\n @classmethod\n def production(cls) -> \"Config\":\n return cls(host=\"0.0.0.0\", port=80, debug=False)\n\n @staticmethod\n def validate_port(port: int) -> bool:\n return 1 <= port <= 65535\n\n\n# ---------------------------------------------------------------------------\n# 6. Dynamic dispatch via polymorphism\n# ---------------------------------------------------------------------------\n\ndef process_animals(animals: List[Animal]) -> List[str]:\n return [a.describe() for a in animals]\n\n\ndef make_sound_twice(animal: Animal) -> str:\n return f\"{animal.speak()} {animal.speak()}\"\n\n\n# ---------------------------------------------------------------------------\n# 7. Driver\n# ---------------------------------------------------------------------------\n\ndef main():\n dog = Dog.create(\"Rex\")\n cat = Cat.create(\"Whiskers\")\n duck = Duck.create(\"Donald\")\n k9 = PoliceDog(\"Buddy\", badge=42)\n elite = RescuePoliceDog(\"Max\", badge=99, specialty=\"avalanche\")\n\n descriptions = process_animals([dog, cat, duck, k9, elite])\n sounds = [make_sound_twice(a) for a in [dog, cat]]\n\n cfg_dev = Config.development()\n cfg_prod = Config.production()\n cfg_custom = Config.from_dict({\"host\": \"10.0.0.1\", \"port\": \"9090\"})\n\n valid = Config.validate_port(cfg_dev.port)\n kingdom = Animal.kingdom()\n\n return descriptions, sounds, cfg_dev, cfg_prod, cfg_custom, valid, kingdom\n\n\nif __name__ == \"__main__\":\n main()\n", + "types": { + "main.Animal": { + "attributes": { + "_registry": { + "comments": [], + "end_line": 21, + "initializer": "[]", + "name": "_registry", + "start_line": 21, + "type": "List['Animal']" + } + }, + "base_classes": [ + "ABC" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 27, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 26, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 27, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 32, + "is_builtin": false, + "kind": "variable", + "lineno": 28, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 28, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "28:8": { + "callee": "can://python/class_hierarchy/@external/builtins.list/append", + "kind": "call", + "span": { + "bytes": [ + 731, + 760 + ], + "end": [ + 28, + 37 + ], + "start": [ + 28, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Animal" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "Animal" + } + ], + "callee_signature": "builtins.list.append", + "end_column": 37, + "end_line": 28, + "is_constructor_call": false, + "method_name": "append", + "receiver_expr": "Animal._registry", + "receiver_type": "Animal", + "start_column": 8, + "start_line": 28 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 27, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 28, + "id": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 27, + "initializer": "name", + "name": "name", + "scope": "class", + "start_column": 8, + "start_line": 27, + "type": "Animal" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 26, + "name": "self", + "start_column": 17, + "start_line": 26, + "type": "Animal" + }, + { + "end_column": 32, + "end_line": 26, + "name": "name", + "start_column": 23, + "start_line": 26, + "type": "str" + } + ], + "signature": "main.Animal.__init__", + "span": { + "bytes": [ + 667, + 760 + ], + "end": [ + 28, + 37 + ], + "start": [ + 26, + 4 + ] + }, + "start_line": 26, + "summary": [], + "types": {} + }, + "__init_subclass__": { + "accessed_symbols": [ + { + "col_offset": 36, + "is_builtin": false, + "kind": "variable", + "lineno": 24, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 24, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "24:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 626, + 633 + ], + "end": [ + 24, + 15 + ], + "start": [ + 24, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.object.__init_subclass__", + "end_column": 43, + "end_line": 24, + "is_constructor_call": false, + "method_name": "__init_subclass__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 24 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 24, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 24 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 24, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 24, + "id": "can://python/class_hierarchy/main.py/Animal/__init_subclass__(cls,kwargs)", + "kind": "function", + "local_variables": [], + "name": "__init_subclass__", + "parameters": [ + { + "end_column": 29, + "end_line": 23, + "name": "cls", + "start_column": 26, + "start_line": 23, + "type": "Animal" + }, + { + "end_column": 39, + "end_line": 23, + "name": "kwargs", + "start_column": 33, + "start_line": 23, + "type": "dict" + } + ], + "signature": "main.Animal.__init_subclass__", + "span": { + "bytes": [ + 580, + 661 + ], + "end": [ + 24, + 43 + ], + "start": [ + 23, + 4 + ] + }, + "start_line": 23, + "summary": [], + "types": {} + }, + "create": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 36, + "name": "cls", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "36:15": { + "callee": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "kind": "call", + "span": { + "bytes": [ + 899, + 908 + ], + "end": [ + 36, + 24 + ], + "start": [ + 36, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 24, + "end_line": 36, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Animal", + "start_column": 15, + "start_line": 36 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 36, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "classmethod" + ], + "end_line": 36, + "id": "can://python/class_hierarchy/main.py/Animal/create(cls,name)", + "kind": "function", + "local_variables": [], + "name": "create", + "parameters": [ + { + "end_column": 18, + "end_line": 35, + "name": "cls", + "start_column": 15, + "start_line": 35, + "type": "Animal" + }, + { + "end_column": 29, + "end_line": 35, + "name": "name", + "start_column": 20, + "start_line": 35, + "type": "str" + } + ], + "return_type": "'Animal'", + "signature": "main.Animal.create", + "span": { + "bytes": [ + 844, + 908 + ], + "end": [ + 36, + 24 + ], + "start": [ + 35, + 4 + ] + }, + "start_line": 35, + "summary": [], + "types": {} + }, + "describe": { + "accessed_symbols": [ + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 42, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 43, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 36, + "is_builtin": false, + "kind": "variable", + "lineno": 43, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "43:36": { + "callee": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "kind": "call", + "span": { + "bytes": [ + 1048, + 1060 + ], + "end": [ + 43, + 48 + ], + "start": [ + 43, + 36 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.speak", + "end_column": 48, + "end_line": 43, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "self", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 36, + "start_line": 43 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 43, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 43, + "id": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "kind": "function", + "local_variables": [], + "name": "describe", + "parameters": [ + { + "end_column": 21, + "end_line": 42, + "name": "self", + "start_column": 17, + "start_line": 42, + "type": "Animal" + } + ], + "return_type": "str", + "signature": "main.Animal.describe", + "span": { + "bytes": [ + 985, + 1062 + ], + "end": [ + 43, + 50 + ], + "start": [ + 42, + 4 + ] + }, + "start_line": 42, + "summary": [], + "types": {} + }, + "kingdom": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 38, + "name": "staticmethod", + "qualified_name": "builtins.staticmethod", + "scope": "local", + "type": "staticmethod" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 39, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 40, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "staticmethod" + ], + "end_line": 40, + "id": "can://python/class_hierarchy/main.py/Animal/kingdom()", + "kind": "function", + "local_variables": [], + "name": "kingdom", + "parameters": [], + "return_type": "str", + "signature": "main.Animal.kingdom", + "span": { + "bytes": [ + 932, + 979 + ], + "end": [ + 40, + 25 + ], + "start": [ + 39, + 4 + ] + }, + "start_line": 39, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "function", + "lineno": 30, + "name": "abstractmethod", + "qualified_name": "abc.abstractmethod", + "scope": "local", + "type": "abstractmethod" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 31, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 32, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [ + "abstractmethod" + ], + "end_line": 32, + "id": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 31, + "name": "self", + "start_column": 14, + "start_line": 31, + "type": "Animal" + } + ], + "return_type": "str", + "signature": "main.Animal.speak", + "span": { + "bytes": [ + 786, + 821 + ], + "end": [ + 32, + 11 + ], + "start": [ + 31, + 4 + ] + }, + "start_line": 31, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 43, + "id": "can://python/class_hierarchy/main.py/Animal", + "kind": "class", + "name": "Animal", + "signature": "main.Animal", + "span": { + "bytes": [ + 521, + 1062 + ], + "end": [ + 43, + 50 + ], + "start": [ + 20, + 0 + ] + }, + "start_line": 20, + "types": {} + }, + "main.Cat": { + "attributes": {}, + "base_classes": [ + "Animal" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 62, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 63, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 63, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "63:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 1532, + 1539 + ], + "end": [ + 63, + 15 + ], + "start": [ + 63, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 30, + "end_line": 63, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 63 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 63, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 63 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 63, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 63, + "id": "can://python/class_hierarchy/main.py/Cat/__init__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 62, + "name": "self", + "start_column": 17, + "start_line": 62, + "type": "Cat" + }, + { + "end_column": 32, + "end_line": 62, + "name": "name", + "start_column": 23, + "start_line": 62, + "type": "str" + } + ], + "signature": "main.Cat.__init__", + "span": { + "bytes": [ + 1493, + 1554 + ], + "end": [ + 63, + 30 + ], + "start": [ + 62, + 4 + ] + }, + "start_line": 62, + "summary": [], + "types": {} + }, + "purr": { + "accessed_symbols": [ + { + "col_offset": 22, + "is_builtin": false, + "kind": "class", + "lineno": 68, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 69, + "name": "self", + "qualified_name": "main.Cat", + "scope": "local", + "type": "Cat" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 69, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 69, + "id": "can://python/class_hierarchy/main.py/Cat/purr(self)", + "kind": "function", + "local_variables": [], + "name": "purr", + "parameters": [ + { + "end_column": 17, + "end_line": 68, + "name": "self", + "start_column": 13, + "start_line": 68, + "type": "Cat" + } + ], + "return_type": "str", + "signature": "main.Cat.purr", + "span": { + "bytes": [ + 1611, + 1669 + ], + "end": [ + 69, + 35 + ], + "start": [ + 68, + 4 + ] + }, + "start_line": 68, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 65, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 66, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 66, + "id": "can://python/class_hierarchy/main.py/Cat/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 65, + "name": "self", + "start_column": 14, + "start_line": 65, + "type": "Cat" + } + ], + "return_type": "str", + "signature": "main.Cat.speak", + "span": { + "bytes": [ + 1560, + 1605 + ], + "end": [ + 66, + 21 + ], + "start": [ + 65, + 4 + ] + }, + "start_line": 65, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 69, + "id": "can://python/class_hierarchy/main.py/Cat", + "kind": "class", + "name": "Cat", + "signature": "main.Cat", + "span": { + "bytes": [ + 1470, + 1669 + ], + "end": [ + 69, + 35 + ], + "start": [ + 61, + 0 + ] + }, + "start_line": 61, + "types": {} + }, + "main.Config": { + "attributes": {}, + "base_classes": [], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 123, + "name": "host", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 124, + "name": "port", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 125, + "name": "debug", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 122, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 40, + "is_builtin": false, + "kind": "class", + "lineno": 122, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 52, + "is_builtin": false, + "kind": "class", + "lineno": 122, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 123, + "name": "self", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 124, + "name": "self", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 125, + "name": "self", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 123, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 125, + "id": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 123, + "initializer": "host", + "name": "host", + "scope": "class", + "start_column": 8, + "start_line": 123, + "type": "Config" + }, + { + "end_column": 17, + "end_line": 124, + "initializer": "port", + "name": "port", + "scope": "class", + "start_column": 8, + "start_line": 124, + "type": "Config" + }, + { + "end_column": 18, + "end_line": 125, + "initializer": "debug", + "name": "debug", + "scope": "class", + "start_column": 8, + "start_line": 125, + "type": "Config" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 122, + "name": "self", + "start_column": 17, + "start_line": 122, + "type": "Config" + }, + { + "end_column": 32, + "end_line": 122, + "name": "host", + "start_column": 23, + "start_line": 122, + "type": "str" + }, + { + "end_column": 43, + "end_line": 122, + "name": "port", + "start_column": 34, + "start_line": 122, + "type": "int" + }, + { + "default_value": "False", + "end_column": 56, + "end_line": 122, + "name": "debug", + "start_column": 45, + "start_line": 122, + "type": "bool" + } + ], + "signature": "main.Config.__init__", + "span": { + "bytes": [ + 3100, + 3239 + ], + "end": [ + 125, + 26 + ], + "start": [ + 122, + 4 + ] + }, + "start_line": 122, + "summary": [], + "types": {} + }, + "development": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 135, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 137, + "name": "cls", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + } + ], + "body": { + "137:15": { + "callee": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "kind": "call", + "span": { + "bytes": [ + 3539, + 3583 + ], + "end": [ + 137, + 59 + ], + "start": [ + 137, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Config.__init__", + "end_column": 59, + "end_line": 137, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Config", + "start_column": 15, + "start_line": 137 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 137, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "classmethod" + ], + "end_line": 137, + "id": "can://python/class_hierarchy/main.py/Config/development(cls)", + "kind": "function", + "local_variables": [], + "name": "development", + "parameters": [ + { + "end_column": 23, + "end_line": 136, + "name": "cls", + "start_column": 20, + "start_line": 136, + "type": "Config" + } + ], + "return_type": "'Config'", + "signature": "main.Config.development", + "span": { + "bytes": [ + 3490, + 3583 + ], + "end": [ + 137, + 59 + ], + "start": [ + 136, + 4 + ] + }, + "start_line": 136, + "summary": [], + "types": {} + }, + "from_dict": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 127, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 128, + "name": "dict", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 129, + "name": "cls", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "class", + "lineno": 131, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "class", + "lineno": 132, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "variable", + "lineno": 130, + "name": "d", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 131, + "name": "d", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 132, + "name": "d", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "129:15": { + "callee": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "kind": "call", + "span": { + "bytes": [ + 3318, + 3467 + ], + "end": [ + 133, + 9 + ], + "start": [ + 129, + 15 + ] + } + }, + "130:17": { + "callee": "can://python/class_hierarchy/@external/typing.Mapping/get", + "kind": "call", + "span": { + "bytes": [ + 3340, + 3366 + ], + "end": [ + 130, + 43 + ], + "start": [ + 130, + 17 + ] + } + }, + "131:17": { + "callee": "can://python/class_hierarchy/@external/builtins.int/__init__", + "kind": "call", + "span": { + "bytes": [ + 3385, + 3409 + ], + "end": [ + 131, + 41 + ], + "start": [ + 131, + 17 + ] + } + }, + "131:21": { + "callee": "can://python/class_hierarchy/@external/typing.Mapping/get", + "kind": "call", + "span": { + "bytes": [ + 3389, + 3408 + ], + "end": [ + 131, + 40 + ], + "start": [ + 131, + 21 + ] + } + }, + "132:18": { + "callee": "can://python/class_hierarchy/@external/builtins.bool/__init__", + "kind": "call", + "span": { + "bytes": [ + 3429, + 3456 + ], + "end": [ + 132, + 45 + ], + "start": [ + 132, + 18 + ] + } + }, + "132:23": { + "callee": "can://python/class_hierarchy/@external/typing.Mapping/get", + "kind": "call", + "span": { + "bytes": [ + 3434, + 3455 + ], + "end": [ + 132, + 44 + ], + "start": [ + 132, + 23 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Config.__init__", + "end_column": 9, + "end_line": 133, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Config", + "start_column": 15, + "start_line": 129 + }, + { + "argument_types": [ + "str", + "Constant" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + }, + { + "ast_kind": "Constant" + } + ], + "callee_signature": "typing.Mapping.get", + "end_column": 43, + "end_line": 130, + "is_constructor_call": false, + "method_name": "get", + "receiver_expr": "d", + "receiver_type": "dict", + "start_column": 17, + "start_line": 130 + }, + { + "argument_types": [ + "dict" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "dict" + } + ], + "callee_signature": "builtins.int.__init__", + "end_column": 41, + "end_line": 131, + "is_constructor_call": true, + "method_name": "int", + "return_type": "int", + "start_column": 17, + "start_line": 131 + }, + { + "argument_types": [ + "str", + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "typing.Mapping.get", + "end_column": 40, + "end_line": 131, + "is_constructor_call": false, + "method_name": "get", + "receiver_expr": "d", + "receiver_type": "dict", + "start_column": 21, + "start_line": 131 + }, + { + "argument_types": [ + "dict" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "dict" + } + ], + "callee_signature": "builtins.bool.__init__", + "end_column": 45, + "end_line": 132, + "is_constructor_call": true, + "method_name": "bool", + "return_type": "bool", + "start_column": 18, + "start_line": 132 + }, + { + "argument_types": [ + "str", + "Constant" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + }, + { + "ast_kind": "Constant" + } + ], + "callee_signature": "typing.Mapping.get", + "end_column": 44, + "end_line": 132, + "is_constructor_call": false, + "method_name": "get", + "receiver_expr": "d", + "receiver_type": "dict", + "start_column": 23, + "start_line": 132 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 129, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "classmethod" + ], + "end_line": 133, + "id": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "kind": "function", + "local_variables": [], + "name": "from_dict", + "parameters": [ + { + "end_column": 21, + "end_line": 128, + "name": "cls", + "start_column": 18, + "start_line": 128, + "type": "Config" + }, + { + "end_column": 30, + "end_line": 128, + "name": "d", + "start_column": 23, + "start_line": 128, + "type": "dict" + } + ], + "return_type": "'Config'", + "signature": "main.Config.from_dict", + "span": { + "bytes": [ + 3262, + 3467 + ], + "end": [ + 133, + 9 + ], + "start": [ + 128, + 4 + ] + }, + "start_line": 128, + "summary": [], + "types": {} + }, + "production": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 139, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 141, + "name": "cls", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + } + ], + "body": { + "141:15": { + "callee": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "kind": "call", + "span": { + "bytes": [ + 3654, + 3695 + ], + "end": [ + 141, + 56 + ], + "start": [ + 141, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Config.__init__", + "end_column": 56, + "end_line": 141, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Config", + "start_column": 15, + "start_line": 141 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 141, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "classmethod" + ], + "end_line": 141, + "id": "can://python/class_hierarchy/main.py/Config/production(cls)", + "kind": "function", + "local_variables": [], + "name": "production", + "parameters": [ + { + "end_column": 22, + "end_line": 140, + "name": "cls", + "start_column": 19, + "start_line": 140, + "type": "Config" + } + ], + "return_type": "'Config'", + "signature": "main.Config.production", + "span": { + "bytes": [ + 3606, + 3695 + ], + "end": [ + 141, + 56 + ], + "start": [ + 140, + 4 + ] + }, + "start_line": 140, + "summary": [], + "types": {} + }, + "validate_port": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 143, + "name": "staticmethod", + "qualified_name": "builtins.staticmethod", + "scope": "local", + "type": "staticmethod" + }, + { + "col_offset": 36, + "is_builtin": false, + "kind": "class", + "lineno": 144, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 28, + "is_builtin": false, + "kind": "class", + "lineno": 144, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 145, + "name": "port", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 145, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "staticmethod" + ], + "end_line": 145, + "id": "can://python/class_hierarchy/main.py/Config/validate_port(port)", + "kind": "function", + "local_variables": [], + "name": "validate_port", + "parameters": [ + { + "end_column": 31, + "end_line": 144, + "name": "port", + "start_column": 22, + "start_line": 144, + "type": "int" + } + ], + "return_type": "bool", + "signature": "main.Config.validate_port", + "span": { + "bytes": [ + 3719, + 3790 + ], + "end": [ + 145, + 33 + ], + "start": [ + 144, + 4 + ] + }, + "start_line": 144, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 145, + "id": "can://python/class_hierarchy/main.py/Config", + "kind": "class", + "name": "Config", + "signature": "main.Config", + "span": { + "bytes": [ + 3082, + 3790 + ], + "end": [ + 145, + 33 + ], + "start": [ + 121, + 0 + ] + }, + "start_line": 121, + "types": {} + }, + "main.Dog": { + "attributes": {}, + "base_classes": [ + "Animal" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 51, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 52, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 52, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "52:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 1309, + 1316 + ], + "end": [ + 52, + 15 + ], + "start": [ + 52, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 30, + "end_line": 52, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 52 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 52, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 52 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 52, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 52, + "id": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 51, + "name": "self", + "start_column": 17, + "start_line": 51, + "type": "Dog" + }, + { + "end_column": 32, + "end_line": 51, + "name": "name", + "start_column": 23, + "start_line": 51, + "type": "str" + } + ], + "signature": "main.Dog.__init__", + "span": { + "bytes": [ + 1270, + 1331 + ], + "end": [ + 52, + 30 + ], + "start": [ + 51, + 4 + ] + }, + "start_line": 51, + "summary": [], + "types": {} + }, + "fetch": { + "accessed_symbols": [ + { + "col_offset": 34, + "is_builtin": false, + "kind": "class", + "lineno": 57, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 57, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 38, + "is_builtin": false, + "kind": "variable", + "lineno": 58, + "name": "item", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 58, + "name": "self", + "qualified_name": "main.Dog", + "scope": "local", + "type": "Dog" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 58, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 58, + "id": "can://python/class_hierarchy/main.py/Dog/fetch(self,item)", + "kind": "function", + "local_variables": [], + "name": "fetch", + "parameters": [ + { + "end_column": 18, + "end_line": 57, + "name": "self", + "start_column": 14, + "start_line": 57, + "type": "Dog" + }, + { + "end_column": 29, + "end_line": 57, + "name": "item", + "start_column": 20, + "start_line": 57, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.Dog.fetch", + "span": { + "bytes": [ + 1388, + 1467 + ], + "end": [ + 58, + 44 + ], + "start": [ + 57, + 4 + ] + }, + "start_line": 57, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 54, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 55, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 55, + "id": "can://python/class_hierarchy/main.py/Dog/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 54, + "name": "self", + "start_column": 14, + "start_line": 54, + "type": "Dog" + } + ], + "return_type": "str", + "signature": "main.Dog.speak", + "span": { + "bytes": [ + 1337, + 1382 + ], + "end": [ + 55, + 21 + ], + "start": [ + 54, + 4 + ] + }, + "start_line": 54, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 58, + "id": "can://python/class_hierarchy/main.py/Dog", + "kind": "class", + "name": "Dog", + "signature": "main.Dog", + "span": { + "bytes": [ + 1247, + 1467 + ], + "end": [ + 58, + 44 + ], + "start": [ + 50, + 0 + ] + }, + "start_line": 50, + "types": {} + }, + "main.Duck": { + "attributes": {}, + "base_classes": [ + "Animal", + "Swimmer" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 83, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 84, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 84, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "84:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2014, + 2021 + ], + "end": [ + 84, + 15 + ], + "start": [ + 84, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 30, + "end_line": 84, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 84 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 84, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 84 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 84, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 84, + "id": "can://python/class_hierarchy/main.py/Duck/__init__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 83, + "name": "self", + "start_column": 17, + "start_line": 83, + "type": "Duck" + }, + { + "end_column": 32, + "end_line": 83, + "name": "name", + "start_column": 23, + "start_line": 83, + "type": "str" + } + ], + "signature": "main.Duck.__init__", + "span": { + "bytes": [ + 1975, + 2036 + ], + "end": [ + 84, + 30 + ], + "start": [ + 83, + 4 + ] + }, + "start_line": 83, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 86, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 87, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 87, + "id": "can://python/class_hierarchy/main.py/Duck/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 86, + "name": "self", + "start_column": 14, + "start_line": 86, + "type": "Duck" + } + ], + "return_type": "str", + "signature": "main.Duck.speak", + "span": { + "bytes": [ + 2042, + 2088 + ], + "end": [ + 87, + 22 + ], + "start": [ + 86, + 4 + ] + }, + "start_line": 86, + "summary": [], + "types": {} + }, + "swim": { + "accessed_symbols": [ + { + "col_offset": 22, + "is_builtin": false, + "kind": "class", + "lineno": 89, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 90, + "name": "self", + "qualified_name": "main.Duck", + "scope": "local", + "type": "Duck" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 90, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 90, + "id": "can://python/class_hierarchy/main.py/Duck/swim(self)", + "kind": "function", + "local_variables": [], + "name": "swim", + "parameters": [ + { + "end_column": 17, + "end_line": 89, + "name": "self", + "start_column": 13, + "start_line": 89, + "type": "Duck" + } + ], + "return_type": "str", + "signature": "main.Duck.swim", + "span": { + "bytes": [ + 2094, + 2154 + ], + "end": [ + 90, + 37 + ], + "start": [ + 89, + 4 + ] + }, + "start_line": 89, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 90, + "id": "can://python/class_hierarchy/main.py/Duck", + "kind": "class", + "name": "Duck", + "signature": "main.Duck", + "span": { + "bytes": [ + 1942, + 2154 + ], + "end": [ + 90, + 37 + ], + "start": [ + 82, + 0 + ] + }, + "start_line": 82, + "types": {} + }, + "main.PoliceDog": { + "attributes": {}, + "base_classes": [ + "Dog" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 100, + "name": "badge", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 99, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 100, + "name": "self", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 99, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "99:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2444, + 2451 + ], + "end": [ + 99, + 15 + ], + "start": [ + 99, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Dog.__init__", + "end_column": 30, + "end_line": 99, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 99 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 99, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 99 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 99, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 100, + "id": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 100, + "initializer": "badge", + "name": "badge", + "scope": "class", + "start_column": 8, + "start_line": 100, + "type": "PoliceDog" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 98, + "name": "self", + "start_column": 17, + "start_line": 98, + "type": "PoliceDog" + }, + { + "end_column": 32, + "end_line": 98, + "name": "name", + "start_column": 23, + "start_line": 98, + "type": "str" + }, + { + "end_column": 44, + "end_line": 98, + "name": "badge", + "start_column": 34, + "start_line": 98, + "type": "int" + } + ], + "signature": "main.PoliceDog.__init__", + "span": { + "bytes": [ + 2393, + 2493 + ], + "end": [ + 100, + 26 + ], + "start": [ + 98, + 4 + ] + }, + "start_line": 98, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 102, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 104, + "name": "base_bark", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "class", + "lineno": 103, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + }, + { + "col_offset": 40, + "is_builtin": false, + "kind": "variable", + "lineno": 104, + "name": "self", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + } + ], + "body": { + "103:20": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2543, + 2550 + ], + "end": [ + 103, + 27 + ], + "start": [ + 103, + 20 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Dog.speak", + "end_column": 35, + "end_line": 103, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "super()", + "receiver_type": "super", + "return_type": "str", + "start_column": 20, + "start_line": 103 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 27, + "end_line": 103, + "is_constructor_call": true, + "method_name": "super", + "start_column": 20, + "start_line": 103 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 103, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 104, + "id": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 103, + "initializer": "super().speak()", + "name": "base_bark", + "scope": "function", + "start_column": 8, + "start_line": 103, + "type": "str" + } + ], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 102, + "name": "self", + "start_column": 14, + "start_line": 102, + "type": "PoliceDog" + } + ], + "return_type": "str", + "signature": "main.PoliceDog.speak", + "span": { + "bytes": [ + 2499, + 2612 + ], + "end": [ + 104, + 53 + ], + "start": [ + 102, + 4 + ] + }, + "start_line": 102, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 104, + "id": "can://python/class_hierarchy/main.py/PoliceDog", + "kind": "class", + "name": "PoliceDog", + "signature": "main.PoliceDog", + "span": { + "bytes": [ + 2367, + 2612 + ], + "end": [ + 104, + 53 + ], + "start": [ + 97, + 0 + ] + }, + "start_line": 97, + "types": {} + }, + "main.RescuePoliceDog": { + "attributes": {}, + "base_classes": [ + "PoliceDog" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 110, + "name": "specialty", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 108, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "class", + "lineno": 108, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 57, + "is_builtin": false, + "kind": "class", + "lineno": 108, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 109, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 109, + "name": "badge", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 110, + "name": "self", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 109, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "109:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2720, + 2727 + ], + "end": [ + 109, + 15 + ], + "start": [ + 109, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + }, + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "main.PoliceDog.__init__", + "end_column": 37, + "end_line": 109, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 109 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 109, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 109 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 109, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 110, + "id": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "kind": "function", + "local_variables": [ + { + "end_column": 22, + "end_line": 110, + "initializer": "specialty", + "name": "specialty", + "scope": "class", + "start_column": 8, + "start_line": 110, + "type": "RescuePoliceDog" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 108, + "name": "self", + "start_column": 17, + "start_line": 108, + "type": "RescuePoliceDog" + }, + { + "end_column": 32, + "end_line": 108, + "name": "name", + "start_column": 23, + "start_line": 108, + "type": "str" + }, + { + "end_column": 44, + "end_line": 108, + "name": "badge", + "start_column": 34, + "start_line": 108, + "type": "int" + }, + { + "end_column": 60, + "end_line": 108, + "name": "specialty", + "start_column": 46, + "start_line": 108, + "type": "str" + } + ], + "signature": "main.RescuePoliceDog.__init__", + "span": { + "bytes": [ + 2653, + 2784 + ], + "end": [ + 110, + 34 + ], + "start": [ + 108, + 4 + ] + }, + "start_line": 108, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 112, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 114, + "name": "base", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 113, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 114, + "name": "self", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + } + ], + "body": { + "113:15": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2829, + 2836 + ], + "end": [ + 113, + 22 + ], + "start": [ + 113, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.PoliceDog.speak", + "end_column": 30, + "end_line": 113, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "super()", + "receiver_type": "super", + "return_type": "str", + "start_column": 15, + "start_line": 113 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 22, + "end_line": 113, + "is_constructor_call": true, + "method_name": "super", + "start_column": 15, + "start_line": 113 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 113, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 114, + "id": "can://python/class_hierarchy/main.py/RescuePoliceDog/speak(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 12, + "end_line": 113, + "initializer": "super().speak()", + "name": "base", + "scope": "function", + "start_column": 8, + "start_line": 113, + "type": "str" + } + ], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 112, + "name": "self", + "start_column": 14, + "start_line": 112, + "type": "RescuePoliceDog" + } + ], + "return_type": "str", + "signature": "main.RescuePoliceDog.speak", + "span": { + "bytes": [ + 2790, + 2888 + ], + "end": [ + 114, + 43 + ], + "start": [ + 112, + 4 + ] + }, + "start_line": 112, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 114, + "id": "can://python/class_hierarchy/main.py/RescuePoliceDog", + "kind": "class", + "name": "RescuePoliceDog", + "signature": "main.RescuePoliceDog", + "span": { + "bytes": [ + 2615, + 2888 + ], + "end": [ + 114, + 43 + ], + "start": [ + 107, + 0 + ] + }, + "start_line": 107, + "types": {} + }, + "main.Swimmer": { + "attributes": {}, + "base_classes": [ + "ABC" + ], + "callables": { + "swim": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "abstractmethod", + "qualified_name": "abc.abstractmethod", + "scope": "local", + "type": "abstractmethod" + }, + { + "col_offset": 22, + "is_builtin": false, + "kind": "class", + "lineno": 78, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 79, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [ + "abstractmethod" + ], + "end_line": 79, + "id": "can://python/class_hierarchy/main.py/Swimmer/swim(self)", + "kind": "function", + "local_variables": [], + "name": "swim", + "parameters": [ + { + "end_column": 17, + "end_line": 78, + "name": "self", + "start_column": 13, + "start_line": 78, + "type": "Swimmer" + } + ], + "return_type": "str", + "signature": "main.Swimmer.swim", + "span": { + "bytes": [ + 1905, + 1939 + ], + "end": [ + 79, + 11 + ], + "start": [ + 78, + 4 + ] + }, + "start_line": 78, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 79, + "id": "can://python/class_hierarchy/main.py/Swimmer", + "kind": "class", + "name": "Swimmer", + "signature": "main.Swimmer", + "span": { + "bytes": [ + 1861, + 1939 + ], + "end": [ + 79, + 11 + ], + "start": [ + 76, + 0 + ] + }, + "start_line": 76, + "types": {} + } + }, + "variables": [] + } + } + }, + "language": "python", + "max_level": 2, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/class_hierarchy.a3.json b/test/golden/pipeline_equivalence/class_hierarchy.a3.json new file mode 100644 index 0000000..aec0846 --- /dev/null +++ b/test/golden/pipeline_equivalence/class_hierarchy.a3.json @@ -0,0 +1,7450 @@ +{ + "analyzer": { + "config": { + "analysis_level": 3 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/class_hierarchy/@external/builtins.list/append", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.object/__init_subclass__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/__init_subclass__(cls,kwargs)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/__init_subclass__(cls,kwargs)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/create(cls,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Cat/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Cat/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/development(cls)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.bool/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.int/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/typing.Mapping/get", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "weight": 3 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Config/production(cls)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Duck/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/Duck/__init__(self,name)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Dog/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.super/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/RescuePoliceDog/speak(self)", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 6 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/kingdom()", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Config/validate_port(port)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/process_animals(animals)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "weight": 2 + }, + { + "dst": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "prov": [ + "jedi" + ], + "src": "can://python/class_hierarchy/main.py/process_animals(animals)", + "weight": 1 + } + ], + "external_symbols": { + "can://python/class_hierarchy/@external/builtins.bool/__init__": { + "id": "can://python/class_hierarchy/@external/builtins.bool/__init__", + "kind": "external", + "module": "builtins.bool", + "name": "__init__" + }, + "can://python/class_hierarchy/@external/builtins.classmethod/__get__": { + "id": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "external", + "module": "builtins.classmethod", + "name": "__get__" + }, + "can://python/class_hierarchy/@external/builtins.int/__init__": { + "id": "can://python/class_hierarchy/@external/builtins.int/__init__", + "kind": "external", + "module": "builtins.int", + "name": "__init__" + }, + "can://python/class_hierarchy/@external/builtins.list/append": { + "id": "can://python/class_hierarchy/@external/builtins.list/append", + "kind": "external", + "module": "builtins.list", + "name": "append" + }, + "can://python/class_hierarchy/@external/builtins.object/__init_subclass__": { + "id": "can://python/class_hierarchy/@external/builtins.object/__init_subclass__", + "kind": "external", + "module": "builtins.object", + "name": "__init_subclass__" + }, + "can://python/class_hierarchy/@external/builtins.super/__init__": { + "id": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "external", + "module": "builtins.super", + "name": "__init__" + }, + "can://python/class_hierarchy/@external/typing.Mapping/get": { + "id": "can://python/class_hierarchy/@external/typing.Mapping/get", + "kind": "external", + "module": "typing.Mapping", + "name": "get" + } + }, + "id": "can://python/class_hierarchy", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [ + { + "content": "Class hierarchy patterns.\n\nExercises:\n- Abstract base class (abc.ABC + @abstractmethod)\n- Multiple inheritance and MRO\n- super() in __init__ and regular methods\n- @classmethod as factory\n- @staticmethod utility\n- __init_subclass__ hook\n- Dynamic dispatch / polymorphism\n", + "end_column": 3, + "end_line": 11, + "is_docstring": true, + "start_column": 0, + "start_line": 1 + } + ], + "content_hash": "c09e8f66fc9464c0e77b889a31ca1bab0aeb2eea376d77016d132dc67e6bebd2", + "file_size": 5029, + "functions": { + "main": { + "accessed_symbols": [ + { + "col_offset": 9, + "is_builtin": false, + "kind": "class", + "lineno": 168, + "name": "PoliceDog", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "class", + "lineno": 169, + "name": "RescuePoliceDog", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "function", + "lineno": 171, + "name": "process_animals", + "qualified_name": "main.process_animals", + "scope": "local", + "type": "process_animals" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "descriptions", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "sounds", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "cfg_dev", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 42, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "cfg_prod", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 52, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "cfg_custom", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 64, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "valid", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 71, + "is_builtin": false, + "kind": "variable", + "lineno": 181, + "name": "kingdom", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 10, + "is_builtin": false, + "kind": "class", + "lineno": 165, + "name": "Dog", + "qualified_name": "main.Dog", + "scope": "local", + "type": "Dog" + }, + { + "col_offset": 10, + "is_builtin": false, + "kind": "class", + "lineno": 166, + "name": "Cat", + "qualified_name": "main.Cat", + "scope": "local", + "type": "Cat" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "class", + "lineno": 167, + "name": "Duck", + "qualified_name": "main.Duck", + "scope": "local", + "type": "Duck" + }, + { + "col_offset": 36, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "dog", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "cat", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 46, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "duck", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 52, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "k9", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + }, + { + "col_offset": 56, + "is_builtin": false, + "kind": "variable", + "lineno": 171, + "name": "elite", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "function", + "lineno": 172, + "name": "make_sound_twice", + "qualified_name": "main.make_sound_twice", + "scope": "local", + "type": "make_sound_twice" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 172, + "name": "a", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "class", + "lineno": 174, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 175, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "class", + "lineno": 176, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "class", + "lineno": 178, + "name": "Config", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 178, + "name": "cfg_dev", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "class", + "lineno": 179, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 44, + "is_builtin": false, + "kind": "variable", + "lineno": 172, + "name": "dog", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 49, + "is_builtin": false, + "kind": "variable", + "lineno": 172, + "name": "cat", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "165:10": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4377, + 4394 + ], + "end": [ + 165, + 27 + ], + "start": [ + 165, + 10 + ] + } + }, + "165:4": { + "kind": "statement", + "span": { + "bytes": [ + 4371, + 4394 + ], + "end": [ + 165, + 27 + ], + "start": [ + 165, + 4 + ] + } + }, + "166:10": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4405, + 4427 + ], + "end": [ + 166, + 32 + ], + "start": [ + 166, + 10 + ] + } + }, + "166:4": { + "kind": "statement", + "span": { + "bytes": [ + 4399, + 4427 + ], + "end": [ + 166, + 32 + ], + "start": [ + 166, + 4 + ] + } + }, + "167:11": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4439, + 4460 + ], + "end": [ + 167, + 32 + ], + "start": [ + 167, + 11 + ] + } + }, + "167:4": { + "kind": "statement", + "span": { + "bytes": [ + 4432, + 4460 + ], + "end": [ + 167, + 32 + ], + "start": [ + 167, + 4 + ] + } + }, + "168:4": { + "kind": "statement", + "span": { + "bytes": [ + 4465, + 4498 + ], + "end": [ + 168, + 37 + ], + "start": [ + 168, + 4 + ] + } + }, + "168:9": { + "callee": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "kind": "call", + "span": { + "bytes": [ + 4470, + 4498 + ], + "end": [ + 168, + 37 + ], + "start": [ + 168, + 9 + ] + } + }, + "169:12": { + "callee": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "kind": "call", + "span": { + "bytes": [ + 4511, + 4566 + ], + "end": [ + 169, + 67 + ], + "start": [ + 169, + 12 + ] + } + }, + "169:4": { + "kind": "statement", + "span": { + "bytes": [ + 4503, + 4566 + ], + "end": [ + 169, + 67 + ], + "start": [ + 169, + 4 + ] + } + }, + "171:19": { + "callee": "can://python/class_hierarchy/main.py/process_animals(animals)", + "kind": "call", + "span": { + "bytes": [ + 4587, + 4631 + ], + "end": [ + 171, + 63 + ], + "start": [ + 171, + 19 + ] + } + }, + "171:4": { + "kind": "statement", + "span": { + "bytes": [ + 4572, + 4631 + ], + "end": [ + 171, + 63 + ], + "start": [ + 171, + 4 + ] + } + }, + "172:14": { + "callee": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "kind": "call", + "span": { + "bytes": [ + 4646, + 4665 + ], + "end": [ + 172, + 33 + ], + "start": [ + 172, + 14 + ] + } + }, + "172:4": { + "kind": "statement", + "span": { + "bytes": [ + 4636, + 4686 + ], + "end": [ + 172, + 54 + ], + "start": [ + 172, + 4 + ] + } + }, + "174:14": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4702, + 4722 + ], + "end": [ + 174, + 34 + ], + "start": [ + 174, + 14 + ] + } + }, + "174:4": { + "kind": "statement", + "span": { + "bytes": [ + 4692, + 4722 + ], + "end": [ + 174, + 34 + ], + "start": [ + 174, + 4 + ] + } + }, + "175:15": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4738, + 4757 + ], + "end": [ + 175, + 34 + ], + "start": [ + 175, + 15 + ] + } + }, + "175:4": { + "kind": "statement", + "span": { + "bytes": [ + 4727, + 4757 + ], + "end": [ + 175, + 34 + ], + "start": [ + 175, + 4 + ] + } + }, + "176:17": { + "callee": "can://python/class_hierarchy/@external/builtins.classmethod/__get__", + "kind": "call", + "span": { + "bytes": [ + 4775, + 4829 + ], + "end": [ + 176, + 71 + ], + "start": [ + 176, + 17 + ] + } + }, + "176:4": { + "kind": "statement", + "span": { + "bytes": [ + 4762, + 4829 + ], + "end": [ + 176, + 71 + ], + "start": [ + 176, + 4 + ] + } + }, + "178:12": { + "callee": "can://python/class_hierarchy/main.py/Config/validate_port(port)", + "kind": "call", + "span": { + "bytes": [ + 4843, + 4877 + ], + "end": [ + 178, + 46 + ], + "start": [ + 178, + 12 + ] + } + }, + "178:4": { + "kind": "statement", + "span": { + "bytes": [ + 4835, + 4877 + ], + "end": [ + 178, + 46 + ], + "start": [ + 178, + 4 + ] + } + }, + "179:14": { + "callee": "can://python/class_hierarchy/main.py/Animal/kingdom()", + "kind": "call", + "span": { + "bytes": [ + 4892, + 4908 + ], + "end": [ + 179, + 30 + ], + "start": [ + 179, + 14 + ] + } + }, + "179:4": { + "kind": "statement", + "span": { + "bytes": [ + 4882, + 4908 + ], + "end": [ + 179, + 30 + ], + "start": [ + 179, + 4 + ] + } + }, + "181:4": { + "kind": "return", + "span": { + "bytes": [ + 4914, + 4988 + ], + "end": [ + 181, + 78 + ], + "start": [ + 181, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 27, + "end_line": 165, + "is_constructor_call": false, + "method_name": "create", + "receiver_expr": "Dog", + "receiver_type": "Dog", + "return_type": "Animal", + "start_column": 10, + "start_line": 165 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 32, + "end_line": 166, + "is_constructor_call": false, + "method_name": "create", + "receiver_expr": "Cat", + "receiver_type": "Cat", + "return_type": "Animal", + "start_column": 10, + "start_line": 166 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 32, + "end_line": 167, + "is_constructor_call": false, + "method_name": "create", + "receiver_expr": "Duck", + "receiver_type": "Duck", + "return_type": "Animal", + "start_column": 11, + "start_line": 167 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "main.PoliceDog.__init__", + "end_column": 37, + "end_line": 168, + "is_constructor_call": true, + "method_name": "PoliceDog", + "return_type": "PoliceDog", + "start_column": 9, + "start_line": 168 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "main.RescuePoliceDog.__init__", + "end_column": 67, + "end_line": 169, + "is_constructor_call": true, + "method_name": "RescuePoliceDog", + "return_type": "RescuePoliceDog", + "start_column": 12, + "start_line": 169 + }, + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "List", + "inferred_type": "list" + } + ], + "callee_signature": "main.process_animals", + "end_column": 63, + "end_line": 171, + "is_constructor_call": false, + "method_name": "process_animals", + "return_type": "list", + "start_column": 19, + "start_line": 171 + }, + { + "argument_types": [ + "Animal" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "Animal" + } + ], + "callee_signature": "main.make_sound_twice", + "end_column": 33, + "end_line": 172, + "is_constructor_call": false, + "method_name": "make_sound_twice", + "return_type": "str", + "start_column": 14, + "start_line": 172 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 34, + "end_line": 174, + "is_constructor_call": false, + "method_name": "development", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "Config", + "start_column": 14, + "start_line": 174 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 34, + "end_line": 175, + "is_constructor_call": false, + "method_name": "production", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "Config", + "start_column": 15, + "start_line": 175 + }, + { + "argument_types": [ + "Config" + ], + "arguments": [ + { + "ast_kind": "Dict", + "inferred_type": "Config" + } + ], + "callee_signature": "builtins.classmethod.__get__", + "end_column": 71, + "end_line": 176, + "is_constructor_call": false, + "method_name": "from_dict", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "Config", + "start_column": 17, + "start_line": 176 + }, + { + "argument_types": [ + "Config" + ], + "arguments": [ + { + "ast_kind": "Attribute", + "inferred_type": "Config" + } + ], + "callee_signature": "main.Config.validate_port", + "end_column": 46, + "end_line": 178, + "is_constructor_call": false, + "method_name": "validate_port", + "receiver_expr": "Config", + "receiver_type": "Config", + "return_type": "bool", + "start_column": 12, + "start_line": 178 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.kingdom", + "end_column": 30, + "end_line": 179, + "is_constructor_call": false, + "method_name": "kingdom", + "receiver_expr": "Animal", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 14, + "start_line": 179 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "165:4", + "src": "@entry" + }, + { + "dst": "166:4", + "src": "165:4" + }, + { + "dst": "167:4", + "src": "166:4" + }, + { + "dst": "168:4", + "src": "167:4" + }, + { + "dst": "169:4", + "src": "168:4" + }, + { + "dst": "171:4", + "src": "169:4" + }, + { + "dst": "172:4", + "src": "171:4" + }, + { + "dst": "174:4", + "src": "172:4" + }, + { + "dst": "175:4", + "src": "174:4" + }, + { + "dst": "176:4", + "src": "175:4" + }, + { + "dst": "178:4", + "src": "176:4" + }, + { + "dst": "179:4", + "src": "178:4" + }, + { + "dst": "181:4", + "src": "179:4" + } + ], + "cfg": [ + { + "dst": "165:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "166:4", + "kind": "fallthrough", + "src": "165:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "165:4" + }, + { + "dst": "167:4", + "kind": "fallthrough", + "src": "166:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "166:4" + }, + { + "dst": "168:4", + "kind": "fallthrough", + "src": "167:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "167:4" + }, + { + "dst": "169:4", + "kind": "fallthrough", + "src": "168:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "168:4" + }, + { + "dst": "171:4", + "kind": "fallthrough", + "src": "169:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "169:4" + }, + { + "dst": "172:4", + "kind": "fallthrough", + "src": "171:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "171:4" + }, + { + "dst": "174:4", + "kind": "fallthrough", + "src": "172:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "172:4" + }, + { + "dst": "175:4", + "kind": "fallthrough", + "src": "174:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "174:4" + }, + { + "dst": "176:4", + "kind": "fallthrough", + "src": "175:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "175:4" + }, + { + "dst": "178:4", + "kind": "fallthrough", + "src": "176:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "176:4" + }, + { + "dst": "179:4", + "kind": "fallthrough", + "src": "178:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "178:4" + }, + { + "dst": "181:4", + "kind": "fallthrough", + "src": "179:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "179:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "181:4" + } + ], + "code_start_line": 165, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "165:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Dog" + }, + { + "dst": "165:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Dog.create" + }, + { + "dst": "166:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Cat" + }, + { + "dst": "166:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Cat.create" + }, + { + "dst": "167:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Duck" + }, + { + "dst": "167:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Duck.create" + }, + { + "dst": "168:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::PoliceDog" + }, + { + "dst": "169:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::RescuePoliceDog" + }, + { + "dst": "171:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::process_animals" + }, + { + "dst": "172:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::make_sound_twice" + }, + { + "dst": "174:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Config" + }, + { + "dst": "174:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Config.development" + }, + { + "dst": "175:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Config" + }, + { + "dst": "175:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Config.production" + }, + { + "dst": "176:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Config" + }, + { + "dst": "176:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Config.from_dict" + }, + { + "dst": "178:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Config" + }, + { + "dst": "178:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Config.validate_port" + }, + { + "dst": "179:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Animal" + }, + { + "dst": "179:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Animal.kingdom" + }, + { + "dst": "171:4", + "prov": [ + "ssa" + ], + "src": "165:4", + "var": "dog" + }, + { + "dst": "172:4", + "prov": [ + "ssa" + ], + "src": "165:4", + "var": "dog" + }, + { + "dst": "171:4", + "prov": [ + "ssa" + ], + "src": "166:4", + "var": "cat" + }, + { + "dst": "172:4", + "prov": [ + "ssa" + ], + "src": "166:4", + "var": "cat" + }, + { + "dst": "171:4", + "prov": [ + "ssa" + ], + "src": "167:4", + "var": "duck" + }, + { + "dst": "171:4", + "prov": [ + "ssa" + ], + "src": "168:4", + "var": "k9" + }, + { + "dst": "171:4", + "prov": [ + "ssa" + ], + "src": "169:4", + "var": "elite" + }, + { + "dst": "181:4", + "prov": [ + "ssa" + ], + "src": "171:4", + "var": "descriptions" + }, + { + "dst": "181:4", + "prov": [ + "ssa" + ], + "src": "172:4", + "var": "sounds" + }, + { + "dst": "175:4", + "prov": [ + "ssa" + ], + "src": "174:4", + "var": "main::Config" + }, + { + "dst": "175:4", + "prov": [ + "ssa" + ], + "src": "174:4", + "var": "main::Config.production" + }, + { + "dst": "176:4", + "prov": [ + "ssa" + ], + "src": "174:4", + "var": "main::Config" + }, + { + "dst": "176:4", + "prov": [ + "ssa" + ], + "src": "174:4", + "var": "main::Config.from_dict" + }, + { + "dst": "178:4", + "prov": [ + "ssa" + ], + "src": "174:4", + "var": "cfg_dev.port" + }, + { + "dst": "178:4", + "prov": [ + "ssa" + ], + "src": "174:4", + "var": "main::Config" + }, + { + "dst": "178:4", + "prov": [ + "ssa" + ], + "src": "174:4", + "var": "main::Config.validate_port" + }, + { + "dst": "181:4", + "prov": [ + "ssa" + ], + "src": "174:4", + "var": "cfg_dev" + }, + { + "dst": "176:4", + "prov": [ + "ssa" + ], + "src": "175:4", + "var": "main::Config" + }, + { + "dst": "176:4", + "prov": [ + "ssa" + ], + "src": "175:4", + "var": "main::Config.from_dict" + }, + { + "dst": "178:4", + "prov": [ + "ssa" + ], + "src": "175:4", + "var": "main::Config" + }, + { + "dst": "178:4", + "prov": [ + "ssa" + ], + "src": "175:4", + "var": "main::Config.validate_port" + }, + { + "dst": "181:4", + "prov": [ + "ssa" + ], + "src": "175:4", + "var": "cfg_prod" + }, + { + "dst": "178:4", + "prov": [ + "ssa" + ], + "src": "176:4", + "var": "main::Config" + }, + { + "dst": "178:4", + "prov": [ + "ssa" + ], + "src": "176:4", + "var": "main::Config.validate_port" + }, + { + "dst": "181:4", + "prov": [ + "ssa" + ], + "src": "176:4", + "var": "cfg_custom" + }, + { + "dst": "181:4", + "prov": [ + "ssa" + ], + "src": "178:4", + "var": "cfg_dev" + }, + { + "dst": "181:4", + "prov": [ + "ssa" + ], + "src": "178:4", + "var": "valid" + }, + { + "dst": "181:4", + "prov": [ + "ssa" + ], + "src": "179:4", + "var": "kingdom" + } + ], + "decorators": [], + "end_line": 181, + "id": "can://python/class_hierarchy/main.py/main()", + "kind": "function", + "local_variables": [ + { + "end_column": 7, + "end_line": 165, + "initializer": "Dog.create('Rex')", + "name": "dog", + "scope": "function", + "start_column": 4, + "start_line": 165, + "type": "Animal" + }, + { + "end_column": 7, + "end_line": 166, + "initializer": "Cat.create('Whiskers')", + "name": "cat", + "scope": "function", + "start_column": 4, + "start_line": 166, + "type": "Animal" + }, + { + "end_column": 8, + "end_line": 167, + "initializer": "Duck.create('Donald')", + "name": "duck", + "scope": "function", + "start_column": 4, + "start_line": 167, + "type": "Animal" + }, + { + "end_column": 6, + "end_line": 168, + "initializer": "PoliceDog('Buddy', badge=42)", + "name": "k9", + "scope": "function", + "start_column": 4, + "start_line": 168, + "type": "PoliceDog" + }, + { + "end_column": 9, + "end_line": 169, + "initializer": "RescuePoliceDog('Max', badge=99, specialty='avalanche')", + "name": "elite", + "scope": "function", + "start_column": 4, + "start_line": 169, + "type": "RescuePoliceDog" + }, + { + "end_column": 16, + "end_line": 171, + "initializer": "process_animals([dog, cat, duck, k9, elite])", + "name": "descriptions", + "scope": "function", + "start_column": 4, + "start_line": 171, + "type": "list" + }, + { + "end_column": 10, + "end_line": 172, + "initializer": "[make_sound_twice(a) for a in [dog, cat]]", + "name": "sounds", + "scope": "function", + "start_column": 4, + "start_line": 172, + "type": "list" + }, + { + "end_column": 11, + "end_line": 174, + "initializer": "Config.development()", + "name": "cfg_dev", + "scope": "function", + "start_column": 4, + "start_line": 174, + "type": "Config" + }, + { + "end_column": 12, + "end_line": 175, + "initializer": "Config.production()", + "name": "cfg_prod", + "scope": "function", + "start_column": 4, + "start_line": 175, + "type": "Config" + }, + { + "end_column": 14, + "end_line": 176, + "initializer": "Config.from_dict({'host': '10.0.0.1', 'port': '9090'})", + "name": "cfg_custom", + "scope": "function", + "start_column": 4, + "start_line": 176, + "type": "Config" + }, + { + "end_column": 9, + "end_line": 178, + "initializer": "Config.validate_port(cfg_dev.port)", + "name": "valid", + "scope": "function", + "start_column": 4, + "start_line": 178, + "type": "bool" + }, + { + "end_column": 11, + "end_line": 179, + "initializer": "Animal.kingdom()", + "name": "kingdom", + "scope": "function", + "start_column": 4, + "start_line": 179, + "type": "str" + } + ], + "name": "main", + "parameters": [], + "signature": "main.main", + "span": { + "bytes": [ + 4355, + 4988 + ], + "end": [ + 181, + 78 + ], + "start": [ + 164, + 0 + ] + }, + "start_line": 164, + "summary": [], + "types": {} + }, + "make_sound_twice": { + "accessed_symbols": [ + { + "col_offset": 40, + "is_builtin": false, + "kind": "class", + "lineno": 156, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 156, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "variable", + "lineno": 157, + "name": "animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 157, + "name": "animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "157:14": { + "callee": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "kind": "call", + "span": { + "bytes": [ + 4150, + 4164 + ], + "end": [ + 157, + 28 + ], + "start": [ + 157, + 14 + ] + } + }, + "157:31": { + "callee": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "kind": "call", + "span": { + "bytes": [ + 4167, + 4181 + ], + "end": [ + 157, + 45 + ], + "start": [ + 157, + 31 + ] + } + }, + "157:4": { + "kind": "return", + "span": { + "bytes": [ + 4140, + 4183 + ], + "end": [ + 157, + 47 + ], + "start": [ + 157, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.speak", + "end_column": 28, + "end_line": 157, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "animal", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 14, + "start_line": 157 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.speak", + "end_column": 45, + "end_line": 157, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "animal", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 31, + "start_line": 157 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "157:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "157:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "157:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "157:4" + } + ], + "code_start_line": 157, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "157:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "animal" + }, + { + "dst": "157:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "animal.speak" + } + ], + "decorators": [], + "end_line": 157, + "id": "can://python/class_hierarchy/main.py/make_sound_twice(animal)", + "kind": "function", + "local_variables": [], + "name": "make_sound_twice", + "parameters": [ + { + "end_column": 35, + "end_line": 156, + "name": "animal", + "start_column": 21, + "start_line": 156, + "type": "Animal" + } + ], + "return_type": "str", + "signature": "main.make_sound_twice", + "span": { + "bytes": [ + 4091, + 4183 + ], + "end": [ + 157, + 47 + ], + "start": [ + 156, + 0 + ] + }, + "start_line": 156, + "summary": [], + "types": {} + }, + "process_animals": { + "accessed_symbols": [ + { + "col_offset": 46, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 51, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "List", + "qualified_name": "typing.list", + "scope": "local", + "type": "List" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "class", + "lineno": 152, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "variable", + "lineno": 153, + "name": "animals", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 12, + "is_builtin": false, + "kind": "variable", + "lineno": 153, + "name": "a", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "153:12": { + "callee": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "kind": "call", + "span": { + "bytes": [ + 4058, + 4070 + ], + "end": [ + 153, + 24 + ], + "start": [ + 153, + 12 + ] + } + }, + "153:4": { + "kind": "return", + "span": { + "bytes": [ + 4050, + 4088 + ], + "end": [ + 153, + 42 + ], + "start": [ + 153, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.describe", + "end_column": 24, + "end_line": 153, + "is_constructor_call": false, + "method_name": "describe", + "receiver_expr": "a", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 12, + "start_line": 153 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "153:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "153:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "153:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "153:4" + } + ], + "code_start_line": 153, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "153:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "animals" + }, + { + "dst": "153:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::a" + } + ], + "decorators": [], + "end_line": 153, + "id": "can://python/class_hierarchy/main.py/process_animals(animals)", + "kind": "function", + "local_variables": [], + "name": "process_animals", + "parameters": [ + { + "end_column": 41, + "end_line": 152, + "name": "animals", + "start_column": 20, + "start_line": 152, + "type": "List[Animal]" + } + ], + "return_type": "List[str]", + "signature": "main.process_animals", + "span": { + "bytes": [ + 3989, + 4088 + ], + "end": [ + 153, + 42 + ], + "start": [ + 152, + 0 + ] + }, + "start_line": 152, + "summary": [], + "types": {} + } + }, + "id": "can://python/class_hierarchy/main.py", + "imports": [ + { + "end_column": 35, + "end_line": 12, + "module": "abc", + "name": "ABC", + "start_column": 0, + "start_line": 12 + }, + { + "end_column": 35, + "end_line": 12, + "module": "abc", + "name": "abstractmethod", + "start_column": 0, + "start_line": 12 + }, + { + "end_column": 23, + "end_line": 13, + "module": "typing", + "name": "List", + "start_column": 0, + "start_line": 13 + } + ], + "kind": "module", + "module_name": "main", + "source": "\"\"\"Class hierarchy patterns.\n\nExercises:\n- Abstract base class (abc.ABC + @abstractmethod)\n- Multiple inheritance and MRO\n- super() in __init__ and regular methods\n- @classmethod as factory\n- @staticmethod utility\n- __init_subclass__ hook\n- Dynamic dispatch / polymorphism\n\"\"\"\nfrom abc import ABC, abstractmethod\nfrom typing import List\n\n\n# ---------------------------------------------------------------------------\n# 1. Abstract base class\n# ---------------------------------------------------------------------------\n\nclass Animal(ABC):\n _registry: List[\"Animal\"] = []\n\n def __init_subclass__(cls, **kwargs):\n super().__init_subclass__(**kwargs)\n\n def __init__(self, name: str):\n self.name = name\n Animal._registry.append(self)\n\n @abstractmethod\n def speak(self) -> str:\n ...\n\n @classmethod\n def create(cls, name: str) -> \"Animal\":\n return cls(name)\n\n @staticmethod\n def kingdom() -> str:\n return \"Animalia\"\n\n def describe(self) -> str:\n return f\"{self.name} says: {self.speak()}\"\n\n\n# ---------------------------------------------------------------------------\n# 2. Concrete subclasses\n# ---------------------------------------------------------------------------\n\nclass Dog(Animal):\n def __init__(self, name: str):\n super().__init__(name)\n\n def speak(self) -> str:\n return \"Woof\"\n\n def fetch(self, item: str) -> str:\n return f\"{self.name} fetches {item}\"\n\n\nclass Cat(Animal):\n def __init__(self, name: str):\n super().__init__(name)\n\n def speak(self) -> str:\n return \"Meow\"\n\n def purr(self) -> str:\n return f\"{self.name} purrs\"\n\n\n# ---------------------------------------------------------------------------\n# 3. Multiple inheritance + MRO\n# ---------------------------------------------------------------------------\n\nclass Swimmer(ABC):\n @abstractmethod\n def swim(self) -> str:\n ...\n\n\nclass Duck(Animal, Swimmer):\n def __init__(self, name: str):\n super().__init__(name)\n\n def speak(self) -> str:\n return \"Quack\"\n\n def swim(self) -> str:\n return f\"{self.name} paddles\"\n\n\n# ---------------------------------------------------------------------------\n# 4. Deep inheritance chain with super() method call\n# ---------------------------------------------------------------------------\n\nclass PoliceDog(Dog):\n def __init__(self, name: str, badge: int):\n super().__init__(name)\n self.badge = badge\n\n def speak(self) -> str:\n base_bark = super().speak()\n return f\"{base_bark} (K9 unit #{self.badge})\"\n\n\nclass RescuePoliceDog(PoliceDog):\n def __init__(self, name: str, badge: int, specialty: str):\n super().__init__(name, badge)\n self.specialty = specialty\n\n def speak(self) -> str:\n base = super().speak()\n return f\"{base} [{self.specialty}]\"\n\n\n# ---------------------------------------------------------------------------\n# 5. @classmethod factory pattern\n# ---------------------------------------------------------------------------\n\nclass Config:\n def __init__(self, host: str, port: int, debug: bool = False):\n self.host = host\n self.port = port\n self.debug = debug\n\n @classmethod\n def from_dict(cls, d: dict) -> \"Config\":\n return cls(\n host=d.get(\"host\", \"localhost\"),\n port=int(d.get(\"port\", 8080)),\n debug=bool(d.get(\"debug\", False)),\n )\n\n @classmethod\n def development(cls) -> \"Config\":\n return cls(host=\"127.0.0.1\", port=5000, debug=True)\n\n @classmethod\n def production(cls) -> \"Config\":\n return cls(host=\"0.0.0.0\", port=80, debug=False)\n\n @staticmethod\n def validate_port(port: int) -> bool:\n return 1 <= port <= 65535\n\n\n# ---------------------------------------------------------------------------\n# 6. Dynamic dispatch via polymorphism\n# ---------------------------------------------------------------------------\n\ndef process_animals(animals: List[Animal]) -> List[str]:\n return [a.describe() for a in animals]\n\n\ndef make_sound_twice(animal: Animal) -> str:\n return f\"{animal.speak()} {animal.speak()}\"\n\n\n# ---------------------------------------------------------------------------\n# 7. Driver\n# ---------------------------------------------------------------------------\n\ndef main():\n dog = Dog.create(\"Rex\")\n cat = Cat.create(\"Whiskers\")\n duck = Duck.create(\"Donald\")\n k9 = PoliceDog(\"Buddy\", badge=42)\n elite = RescuePoliceDog(\"Max\", badge=99, specialty=\"avalanche\")\n\n descriptions = process_animals([dog, cat, duck, k9, elite])\n sounds = [make_sound_twice(a) for a in [dog, cat]]\n\n cfg_dev = Config.development()\n cfg_prod = Config.production()\n cfg_custom = Config.from_dict({\"host\": \"10.0.0.1\", \"port\": \"9090\"})\n\n valid = Config.validate_port(cfg_dev.port)\n kingdom = Animal.kingdom()\n\n return descriptions, sounds, cfg_dev, cfg_prod, cfg_custom, valid, kingdom\n\n\nif __name__ == \"__main__\":\n main()\n", + "types": { + "main.Animal": { + "attributes": { + "_registry": { + "comments": [], + "end_line": 21, + "initializer": "[]", + "name": "_registry", + "start_line": 21, + "type": "List['Animal']" + } + }, + "base_classes": [ + "ABC" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 27, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 26, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 27, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 32, + "is_builtin": false, + "kind": "variable", + "lineno": 28, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 28, + "name": "Animal", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "27:8": { + "kind": "statement", + "span": { + "bytes": [ + 706, + 722 + ], + "end": [ + 27, + 24 + ], + "start": [ + 27, + 8 + ] + } + }, + "28:8": { + "callee": "can://python/class_hierarchy/@external/builtins.list/append", + "kind": "call", + "span": { + "bytes": [ + 731, + 760 + ], + "end": [ + 28, + 37 + ], + "start": [ + 28, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "Animal" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "Animal" + } + ], + "callee_signature": "builtins.list.append", + "end_column": 37, + "end_line": 28, + "is_constructor_call": false, + "method_name": "append", + "receiver_expr": "Animal._registry", + "receiver_type": "Animal", + "start_column": 8, + "start_line": 28 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "27:8", + "src": "@entry" + }, + { + "dst": "28:8", + "src": "27:8" + } + ], + "cfg": [ + { + "dst": "27:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "28:8", + "kind": "fallthrough", + "src": "27:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "27:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "28:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "28:8" + } + ], + "code_start_line": 27, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "27:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + }, + { + "dst": "27:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "28:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Animal._registry" + }, + { + "dst": "28:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Animal._registry.append" + }, + { + "dst": "28:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "28:8", + "prov": [ + "ssa" + ], + "src": "27:8", + "var": "self" + } + ], + "decorators": [], + "end_line": 28, + "id": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 27, + "initializer": "name", + "name": "name", + "scope": "class", + "start_column": 8, + "start_line": 27, + "type": "Animal" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 26, + "name": "self", + "start_column": 17, + "start_line": 26, + "type": "Animal" + }, + { + "end_column": 32, + "end_line": 26, + "name": "name", + "start_column": 23, + "start_line": 26, + "type": "str" + } + ], + "signature": "main.Animal.__init__", + "span": { + "bytes": [ + 667, + 760 + ], + "end": [ + 28, + 37 + ], + "start": [ + 26, + 4 + ] + }, + "start_line": 26, + "summary": [], + "types": {} + }, + "__init_subclass__": { + "accessed_symbols": [ + { + "col_offset": 36, + "is_builtin": false, + "kind": "variable", + "lineno": 24, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 24, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "24:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 626, + 633 + ], + "end": [ + 24, + 15 + ], + "start": [ + 24, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.object.__init_subclass__", + "end_column": 43, + "end_line": 24, + "is_constructor_call": false, + "method_name": "__init_subclass__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 24 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 24, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 24 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "24:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "24:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "24:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "24:8" + } + ], + "code_start_line": 24, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "24:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "kwargs" + }, + { + "dst": "24:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "super" + } + ], + "decorators": [], + "end_line": 24, + "id": "can://python/class_hierarchy/main.py/Animal/__init_subclass__(cls,kwargs)", + "kind": "function", + "local_variables": [], + "name": "__init_subclass__", + "parameters": [ + { + "end_column": 29, + "end_line": 23, + "name": "cls", + "start_column": 26, + "start_line": 23, + "type": "Animal" + }, + { + "end_column": 39, + "end_line": 23, + "name": "kwargs", + "start_column": 33, + "start_line": 23, + "type": "dict" + } + ], + "signature": "main.Animal.__init_subclass__", + "span": { + "bytes": [ + 580, + 661 + ], + "end": [ + 24, + 43 + ], + "start": [ + 23, + 4 + ] + }, + "start_line": 23, + "summary": [], + "types": {} + }, + "create": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 34, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 36, + "name": "cls", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "36:15": { + "callee": "can://python/class_hierarchy/main.py/Animal/__init__(self,name)", + "kind": "call", + "span": { + "bytes": [ + 899, + 908 + ], + "end": [ + 36, + 24 + ], + "start": [ + 36, + 15 + ] + } + }, + "36:8": { + "kind": "return", + "span": { + "bytes": [ + 892, + 908 + ], + "end": [ + 36, + 24 + ], + "start": [ + 36, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 24, + "end_line": 36, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Animal", + "start_column": 15, + "start_line": 36 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "36:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "36:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "36:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "36:8" + } + ], + "code_start_line": 36, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "36:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "cls" + }, + { + "dst": "36:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + } + ], + "decorators": [ + "classmethod" + ], + "end_line": 36, + "id": "can://python/class_hierarchy/main.py/Animal/create(cls,name)", + "kind": "function", + "local_variables": [], + "name": "create", + "parameters": [ + { + "end_column": 18, + "end_line": 35, + "name": "cls", + "start_column": 15, + "start_line": 35, + "type": "Animal" + }, + { + "end_column": 29, + "end_line": 35, + "name": "name", + "start_column": 20, + "start_line": 35, + "type": "str" + } + ], + "return_type": "'Animal'", + "signature": "main.Animal.create", + "span": { + "bytes": [ + 844, + 908 + ], + "end": [ + 36, + 24 + ], + "start": [ + 35, + 4 + ] + }, + "start_line": 35, + "summary": [], + "types": {} + }, + "describe": { + "accessed_symbols": [ + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 42, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 43, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + }, + { + "col_offset": 36, + "is_builtin": false, + "kind": "variable", + "lineno": 43, + "name": "self", + "qualified_name": "main.Animal", + "scope": "local", + "type": "Animal" + } + ], + "body": { + "43:36": { + "callee": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "kind": "call", + "span": { + "bytes": [ + 1048, + 1060 + ], + "end": [ + 43, + 48 + ], + "start": [ + 43, + 36 + ] + } + }, + "43:8": { + "kind": "return", + "span": { + "bytes": [ + 1020, + 1062 + ], + "end": [ + 43, + 50 + ], + "start": [ + 43, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Animal.speak", + "end_column": 48, + "end_line": 43, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "self", + "receiver_type": "Animal", + "return_type": "str", + "start_column": 36, + "start_line": 43 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "43:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "43:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "43:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "43:8" + } + ], + "code_start_line": 43, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "43:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "43:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.name" + }, + { + "dst": "43:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.speak" + } + ], + "decorators": [], + "end_line": 43, + "id": "can://python/class_hierarchy/main.py/Animal/describe(self)", + "kind": "function", + "local_variables": [], + "name": "describe", + "parameters": [ + { + "end_column": 21, + "end_line": 42, + "name": "self", + "start_column": 17, + "start_line": 42, + "type": "Animal" + } + ], + "return_type": "str", + "signature": "main.Animal.describe", + "span": { + "bytes": [ + 985, + 1062 + ], + "end": [ + 43, + 50 + ], + "start": [ + 42, + 4 + ] + }, + "start_line": 42, + "summary": [], + "types": {} + }, + "kingdom": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 38, + "name": "staticmethod", + "qualified_name": "builtins.staticmethod", + "scope": "local", + "type": "staticmethod" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 39, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "40:8": { + "kind": "return", + "span": { + "bytes": [ + 962, + 979 + ], + "end": [ + 40, + 25 + ], + "start": [ + 40, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "40:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "40:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "40:8" + } + ], + "code_start_line": 40, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "staticmethod" + ], + "end_line": 40, + "id": "can://python/class_hierarchy/main.py/Animal/kingdom()", + "kind": "function", + "local_variables": [], + "name": "kingdom", + "parameters": [], + "return_type": "str", + "signature": "main.Animal.kingdom", + "span": { + "bytes": [ + 932, + 979 + ], + "end": [ + 40, + 25 + ], + "start": [ + 39, + 4 + ] + }, + "start_line": 39, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "function", + "lineno": 30, + "name": "abstractmethod", + "qualified_name": "abc.abstractmethod", + "scope": "local", + "type": "abstractmethod" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 31, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "32:8": { + "kind": "statement", + "span": { + "bytes": [ + 818, + 821 + ], + "end": [ + 32, + 11 + ], + "start": [ + 32, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "32:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "32:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "32:8" + } + ], + "code_start_line": 32, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [ + "abstractmethod" + ], + "end_line": 32, + "id": "can://python/class_hierarchy/main.py/Animal/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 31, + "name": "self", + "start_column": 14, + "start_line": 31, + "type": "Animal" + } + ], + "return_type": "str", + "signature": "main.Animal.speak", + "span": { + "bytes": [ + 786, + 821 + ], + "end": [ + 32, + 11 + ], + "start": [ + 31, + 4 + ] + }, + "start_line": 31, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 43, + "id": "can://python/class_hierarchy/main.py/Animal", + "kind": "class", + "name": "Animal", + "signature": "main.Animal", + "span": { + "bytes": [ + 521, + 1062 + ], + "end": [ + 43, + 50 + ], + "start": [ + 20, + 0 + ] + }, + "start_line": 20, + "types": {} + }, + "main.Cat": { + "attributes": {}, + "base_classes": [ + "Animal" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 62, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 63, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 63, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "63:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 1532, + 1539 + ], + "end": [ + 63, + 15 + ], + "start": [ + 63, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 30, + "end_line": 63, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 63 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 63, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 63 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "63:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "63:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "63:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "63:8" + } + ], + "code_start_line": 63, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "63:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + }, + { + "dst": "63:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "super" + } + ], + "decorators": [], + "end_line": 63, + "id": "can://python/class_hierarchy/main.py/Cat/__init__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 62, + "name": "self", + "start_column": 17, + "start_line": 62, + "type": "Cat" + }, + { + "end_column": 32, + "end_line": 62, + "name": "name", + "start_column": 23, + "start_line": 62, + "type": "str" + } + ], + "signature": "main.Cat.__init__", + "span": { + "bytes": [ + 1493, + 1554 + ], + "end": [ + 63, + 30 + ], + "start": [ + 62, + 4 + ] + }, + "start_line": 62, + "summary": [], + "types": {} + }, + "purr": { + "accessed_symbols": [ + { + "col_offset": 22, + "is_builtin": false, + "kind": "class", + "lineno": 68, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 69, + "name": "self", + "qualified_name": "main.Cat", + "scope": "local", + "type": "Cat" + } + ], + "body": { + "69:8": { + "kind": "return", + "span": { + "bytes": [ + 1642, + 1669 + ], + "end": [ + 69, + 35 + ], + "start": [ + 69, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "69:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "69:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "69:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "69:8" + } + ], + "code_start_line": 69, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "69:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.name" + } + ], + "decorators": [], + "end_line": 69, + "id": "can://python/class_hierarchy/main.py/Cat/purr(self)", + "kind": "function", + "local_variables": [], + "name": "purr", + "parameters": [ + { + "end_column": 17, + "end_line": 68, + "name": "self", + "start_column": 13, + "start_line": 68, + "type": "Cat" + } + ], + "return_type": "str", + "signature": "main.Cat.purr", + "span": { + "bytes": [ + 1611, + 1669 + ], + "end": [ + 69, + 35 + ], + "start": [ + 68, + 4 + ] + }, + "start_line": 68, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 65, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "66:8": { + "kind": "return", + "span": { + "bytes": [ + 1592, + 1605 + ], + "end": [ + 66, + 21 + ], + "start": [ + 66, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "66:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "66:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "66:8" + } + ], + "code_start_line": 66, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 66, + "id": "can://python/class_hierarchy/main.py/Cat/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 65, + "name": "self", + "start_column": 14, + "start_line": 65, + "type": "Cat" + } + ], + "return_type": "str", + "signature": "main.Cat.speak", + "span": { + "bytes": [ + 1560, + 1605 + ], + "end": [ + 66, + 21 + ], + "start": [ + 65, + 4 + ] + }, + "start_line": 65, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 69, + "id": "can://python/class_hierarchy/main.py/Cat", + "kind": "class", + "name": "Cat", + "signature": "main.Cat", + "span": { + "bytes": [ + 1470, + 1669 + ], + "end": [ + 69, + 35 + ], + "start": [ + 61, + 0 + ] + }, + "start_line": 61, + "types": {} + }, + "main.Config": { + "attributes": {}, + "base_classes": [], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 123, + "name": "host", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 124, + "name": "port", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 125, + "name": "debug", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 122, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 40, + "is_builtin": false, + "kind": "class", + "lineno": 122, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 52, + "is_builtin": false, + "kind": "class", + "lineno": 122, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 123, + "name": "self", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 124, + "name": "self", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 125, + "name": "self", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + } + ], + "body": { + "123:8": { + "kind": "statement", + "span": { + "bytes": [ + 3171, + 3187 + ], + "end": [ + 123, + 24 + ], + "start": [ + 123, + 8 + ] + } + }, + "124:8": { + "kind": "statement", + "span": { + "bytes": [ + 3196, + 3212 + ], + "end": [ + 124, + 24 + ], + "start": [ + 124, + 8 + ] + } + }, + "125:8": { + "kind": "statement", + "span": { + "bytes": [ + 3221, + 3239 + ], + "end": [ + 125, + 26 + ], + "start": [ + 125, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "123:8", + "src": "@entry" + }, + { + "dst": "124:8", + "src": "123:8" + }, + { + "dst": "125:8", + "src": "124:8" + } + ], + "cfg": [ + { + "dst": "123:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "124:8", + "kind": "fallthrough", + "src": "123:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "123:8" + }, + { + "dst": "125:8", + "kind": "fallthrough", + "src": "124:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "124:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "125:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "125:8" + } + ], + "code_start_line": 123, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "123:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "host" + }, + { + "dst": "123:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "124:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "port" + }, + { + "dst": "124:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "125:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "debug" + }, + { + "dst": "125:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "124:8", + "prov": [ + "ssa" + ], + "src": "123:8", + "var": "self" + }, + { + "dst": "125:8", + "prov": [ + "ssa" + ], + "src": "123:8", + "var": "self" + }, + { + "dst": "125:8", + "prov": [ + "ssa" + ], + "src": "124:8", + "var": "self" + } + ], + "decorators": [], + "end_line": 125, + "id": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 123, + "initializer": "host", + "name": "host", + "scope": "class", + "start_column": 8, + "start_line": 123, + "type": "Config" + }, + { + "end_column": 17, + "end_line": 124, + "initializer": "port", + "name": "port", + "scope": "class", + "start_column": 8, + "start_line": 124, + "type": "Config" + }, + { + "end_column": 18, + "end_line": 125, + "initializer": "debug", + "name": "debug", + "scope": "class", + "start_column": 8, + "start_line": 125, + "type": "Config" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 122, + "name": "self", + "start_column": 17, + "start_line": 122, + "type": "Config" + }, + { + "end_column": 32, + "end_line": 122, + "name": "host", + "start_column": 23, + "start_line": 122, + "type": "str" + }, + { + "end_column": 43, + "end_line": 122, + "name": "port", + "start_column": 34, + "start_line": 122, + "type": "int" + }, + { + "default_value": "False", + "end_column": 56, + "end_line": 122, + "name": "debug", + "start_column": 45, + "start_line": 122, + "type": "bool" + } + ], + "signature": "main.Config.__init__", + "span": { + "bytes": [ + 3100, + 3239 + ], + "end": [ + 125, + 26 + ], + "start": [ + 122, + 4 + ] + }, + "start_line": 122, + "summary": [], + "types": {} + }, + "development": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 135, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 137, + "name": "cls", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + } + ], + "body": { + "137:15": { + "callee": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "kind": "call", + "span": { + "bytes": [ + 3539, + 3583 + ], + "end": [ + 137, + 59 + ], + "start": [ + 137, + 15 + ] + } + }, + "137:8": { + "kind": "return", + "span": { + "bytes": [ + 3532, + 3583 + ], + "end": [ + 137, + 59 + ], + "start": [ + 137, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Config.__init__", + "end_column": 59, + "end_line": 137, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Config", + "start_column": 15, + "start_line": 137 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "137:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "137:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "137:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "137:8" + } + ], + "code_start_line": 137, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "137:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "cls" + } + ], + "decorators": [ + "classmethod" + ], + "end_line": 137, + "id": "can://python/class_hierarchy/main.py/Config/development(cls)", + "kind": "function", + "local_variables": [], + "name": "development", + "parameters": [ + { + "end_column": 23, + "end_line": 136, + "name": "cls", + "start_column": 20, + "start_line": 136, + "type": "Config" + } + ], + "return_type": "'Config'", + "signature": "main.Config.development", + "span": { + "bytes": [ + 3490, + 3583 + ], + "end": [ + 137, + 59 + ], + "start": [ + 136, + 4 + ] + }, + "start_line": 136, + "summary": [], + "types": {} + }, + "from_dict": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 127, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 128, + "name": "dict", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 129, + "name": "cls", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "class", + "lineno": 131, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "class", + "lineno": 132, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "variable", + "lineno": 130, + "name": "d", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 131, + "name": "d", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 132, + "name": "d", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "129:15": { + "callee": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "kind": "call", + "span": { + "bytes": [ + 3318, + 3467 + ], + "end": [ + 133, + 9 + ], + "start": [ + 129, + 15 + ] + } + }, + "129:8": { + "kind": "return", + "span": { + "bytes": [ + 3311, + 3467 + ], + "end": [ + 133, + 9 + ], + "start": [ + 129, + 8 + ] + } + }, + "130:17": { + "callee": "can://python/class_hierarchy/@external/typing.Mapping/get", + "kind": "call", + "span": { + "bytes": [ + 3340, + 3366 + ], + "end": [ + 130, + 43 + ], + "start": [ + 130, + 17 + ] + } + }, + "131:17": { + "callee": "can://python/class_hierarchy/@external/builtins.int/__init__", + "kind": "call", + "span": { + "bytes": [ + 3385, + 3409 + ], + "end": [ + 131, + 41 + ], + "start": [ + 131, + 17 + ] + } + }, + "131:21": { + "callee": "can://python/class_hierarchy/@external/typing.Mapping/get", + "kind": "call", + "span": { + "bytes": [ + 3389, + 3408 + ], + "end": [ + 131, + 40 + ], + "start": [ + 131, + 21 + ] + } + }, + "132:18": { + "callee": "can://python/class_hierarchy/@external/builtins.bool/__init__", + "kind": "call", + "span": { + "bytes": [ + 3429, + 3456 + ], + "end": [ + 132, + 45 + ], + "start": [ + 132, + 18 + ] + } + }, + "132:23": { + "callee": "can://python/class_hierarchy/@external/typing.Mapping/get", + "kind": "call", + "span": { + "bytes": [ + 3434, + 3455 + ], + "end": [ + 132, + 44 + ], + "start": [ + 132, + 23 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Config.__init__", + "end_column": 9, + "end_line": 133, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Config", + "start_column": 15, + "start_line": 129 + }, + { + "argument_types": [ + "str", + "Constant" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + }, + { + "ast_kind": "Constant" + } + ], + "callee_signature": "typing.Mapping.get", + "end_column": 43, + "end_line": 130, + "is_constructor_call": false, + "method_name": "get", + "receiver_expr": "d", + "receiver_type": "dict", + "start_column": 17, + "start_line": 130 + }, + { + "argument_types": [ + "dict" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "dict" + } + ], + "callee_signature": "builtins.int.__init__", + "end_column": 41, + "end_line": 131, + "is_constructor_call": true, + "method_name": "int", + "return_type": "int", + "start_column": 17, + "start_line": 131 + }, + { + "argument_types": [ + "str", + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "typing.Mapping.get", + "end_column": 40, + "end_line": 131, + "is_constructor_call": false, + "method_name": "get", + "receiver_expr": "d", + "receiver_type": "dict", + "start_column": 21, + "start_line": 131 + }, + { + "argument_types": [ + "dict" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "dict" + } + ], + "callee_signature": "builtins.bool.__init__", + "end_column": 45, + "end_line": 132, + "is_constructor_call": true, + "method_name": "bool", + "return_type": "bool", + "start_column": 18, + "start_line": 132 + }, + { + "argument_types": [ + "str", + "Constant" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + }, + { + "ast_kind": "Constant" + } + ], + "callee_signature": "typing.Mapping.get", + "end_column": 44, + "end_line": 132, + "is_constructor_call": false, + "method_name": "get", + "receiver_expr": "d", + "receiver_type": "dict", + "start_column": 23, + "start_line": 132 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "129:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "129:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "129:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "129:8" + } + ], + "code_start_line": 129, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "129:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "bool" + }, + { + "dst": "129:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "cls" + }, + { + "dst": "129:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "d" + }, + { + "dst": "129:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "d.get" + }, + { + "dst": "129:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "int" + } + ], + "decorators": [ + "classmethod" + ], + "end_line": 133, + "id": "can://python/class_hierarchy/main.py/Config/from_dict(cls,d)", + "kind": "function", + "local_variables": [], + "name": "from_dict", + "parameters": [ + { + "end_column": 21, + "end_line": 128, + "name": "cls", + "start_column": 18, + "start_line": 128, + "type": "Config" + }, + { + "end_column": 30, + "end_line": 128, + "name": "d", + "start_column": 23, + "start_line": 128, + "type": "dict" + } + ], + "return_type": "'Config'", + "signature": "main.Config.from_dict", + "span": { + "bytes": [ + 3262, + 3467 + ], + "end": [ + 133, + 9 + ], + "start": [ + 128, + 4 + ] + }, + "start_line": 128, + "summary": [], + "types": {} + }, + "production": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 139, + "name": "classmethod", + "qualified_name": "builtins.classmethod", + "scope": "local", + "type": "classmethod" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 141, + "name": "cls", + "qualified_name": "main.Config", + "scope": "local", + "type": "Config" + } + ], + "body": { + "141:15": { + "callee": "can://python/class_hierarchy/main.py/Config/__init__(self,host,port,debug)", + "kind": "call", + "span": { + "bytes": [ + 3654, + 3695 + ], + "end": [ + 141, + 56 + ], + "start": [ + 141, + 15 + ] + } + }, + "141:8": { + "kind": "return", + "span": { + "bytes": [ + 3647, + 3695 + ], + "end": [ + 141, + 56 + ], + "start": [ + 141, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Config.__init__", + "end_column": 56, + "end_line": 141, + "is_constructor_call": true, + "method_name": "cls", + "return_type": "Config", + "start_column": 15, + "start_line": 141 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "141:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "141:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "141:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "141:8" + } + ], + "code_start_line": 141, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "141:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "cls" + } + ], + "decorators": [ + "classmethod" + ], + "end_line": 141, + "id": "can://python/class_hierarchy/main.py/Config/production(cls)", + "kind": "function", + "local_variables": [], + "name": "production", + "parameters": [ + { + "end_column": 22, + "end_line": 140, + "name": "cls", + "start_column": 19, + "start_line": 140, + "type": "Config" + } + ], + "return_type": "'Config'", + "signature": "main.Config.production", + "span": { + "bytes": [ + 3606, + 3695 + ], + "end": [ + 141, + 56 + ], + "start": [ + 140, + 4 + ] + }, + "start_line": 140, + "summary": [], + "types": {} + }, + "validate_port": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "class", + "lineno": 143, + "name": "staticmethod", + "qualified_name": "builtins.staticmethod", + "scope": "local", + "type": "staticmethod" + }, + { + "col_offset": 36, + "is_builtin": false, + "kind": "class", + "lineno": 144, + "name": "bool", + "qualified_name": "builtins.bool", + "scope": "local", + "type": "bool" + }, + { + "col_offset": 28, + "is_builtin": false, + "kind": "class", + "lineno": 144, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 145, + "name": "port", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "145:8": { + "kind": "return", + "span": { + "bytes": [ + 3765, + 3790 + ], + "end": [ + 145, + 33 + ], + "start": [ + 145, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "145:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "145:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "145:8" + } + ], + "code_start_line": 145, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "145:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "port" + } + ], + "decorators": [ + "staticmethod" + ], + "end_line": 145, + "id": "can://python/class_hierarchy/main.py/Config/validate_port(port)", + "kind": "function", + "local_variables": [], + "name": "validate_port", + "parameters": [ + { + "end_column": 31, + "end_line": 144, + "name": "port", + "start_column": 22, + "start_line": 144, + "type": "int" + } + ], + "return_type": "bool", + "signature": "main.Config.validate_port", + "span": { + "bytes": [ + 3719, + 3790 + ], + "end": [ + 145, + 33 + ], + "start": [ + 144, + 4 + ] + }, + "start_line": 144, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 145, + "id": "can://python/class_hierarchy/main.py/Config", + "kind": "class", + "name": "Config", + "signature": "main.Config", + "span": { + "bytes": [ + 3082, + 3790 + ], + "end": [ + 145, + 33 + ], + "start": [ + 121, + 0 + ] + }, + "start_line": 121, + "types": {} + }, + "main.Dog": { + "attributes": {}, + "base_classes": [ + "Animal" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 51, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 52, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 52, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "52:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 1309, + 1316 + ], + "end": [ + 52, + 15 + ], + "start": [ + 52, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 30, + "end_line": 52, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 52 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 52, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 52 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "52:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "52:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "52:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "52:8" + } + ], + "code_start_line": 52, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "52:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + }, + { + "dst": "52:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "super" + } + ], + "decorators": [], + "end_line": 52, + "id": "can://python/class_hierarchy/main.py/Dog/__init__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 51, + "name": "self", + "start_column": 17, + "start_line": 51, + "type": "Dog" + }, + { + "end_column": 32, + "end_line": 51, + "name": "name", + "start_column": 23, + "start_line": 51, + "type": "str" + } + ], + "signature": "main.Dog.__init__", + "span": { + "bytes": [ + 1270, + 1331 + ], + "end": [ + 52, + 30 + ], + "start": [ + 51, + 4 + ] + }, + "start_line": 51, + "summary": [], + "types": {} + }, + "fetch": { + "accessed_symbols": [ + { + "col_offset": 34, + "is_builtin": false, + "kind": "class", + "lineno": 57, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "class", + "lineno": 57, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 38, + "is_builtin": false, + "kind": "variable", + "lineno": 58, + "name": "item", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 58, + "name": "self", + "qualified_name": "main.Dog", + "scope": "local", + "type": "Dog" + } + ], + "body": { + "58:8": { + "kind": "return", + "span": { + "bytes": [ + 1431, + 1467 + ], + "end": [ + 58, + 44 + ], + "start": [ + 58, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "58:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "58:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "58:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "58:8" + } + ], + "code_start_line": 58, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "58:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "item" + }, + { + "dst": "58:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.name" + } + ], + "decorators": [], + "end_line": 58, + "id": "can://python/class_hierarchy/main.py/Dog/fetch(self,item)", + "kind": "function", + "local_variables": [], + "name": "fetch", + "parameters": [ + { + "end_column": 18, + "end_line": 57, + "name": "self", + "start_column": 14, + "start_line": 57, + "type": "Dog" + }, + { + "end_column": 29, + "end_line": 57, + "name": "item", + "start_column": 20, + "start_line": 57, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.Dog.fetch", + "span": { + "bytes": [ + 1388, + 1467 + ], + "end": [ + 58, + 44 + ], + "start": [ + 57, + 4 + ] + }, + "start_line": 57, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 54, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "55:8": { + "kind": "return", + "span": { + "bytes": [ + 1369, + 1382 + ], + "end": [ + 55, + 21 + ], + "start": [ + 55, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "55:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "55:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "55:8" + } + ], + "code_start_line": 55, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 55, + "id": "can://python/class_hierarchy/main.py/Dog/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 54, + "name": "self", + "start_column": 14, + "start_line": 54, + "type": "Dog" + } + ], + "return_type": "str", + "signature": "main.Dog.speak", + "span": { + "bytes": [ + 1337, + 1382 + ], + "end": [ + 55, + 21 + ], + "start": [ + 54, + 4 + ] + }, + "start_line": 54, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 58, + "id": "can://python/class_hierarchy/main.py/Dog", + "kind": "class", + "name": "Dog", + "signature": "main.Dog", + "span": { + "bytes": [ + 1247, + 1467 + ], + "end": [ + 58, + 44 + ], + "start": [ + 50, + 0 + ] + }, + "start_line": 50, + "types": {} + }, + "main.Duck": { + "attributes": {}, + "base_classes": [ + "Animal", + "Swimmer" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 83, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 84, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 84, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "84:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2014, + 2021 + ], + "end": [ + 84, + 15 + ], + "start": [ + 84, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Animal.__init__", + "end_column": 30, + "end_line": 84, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 84 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 84, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 84 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "84:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "84:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "84:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "84:8" + } + ], + "code_start_line": 84, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "84:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + }, + { + "dst": "84:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "super" + } + ], + "decorators": [], + "end_line": 84, + "id": "can://python/class_hierarchy/main.py/Duck/__init__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 83, + "name": "self", + "start_column": 17, + "start_line": 83, + "type": "Duck" + }, + { + "end_column": 32, + "end_line": 83, + "name": "name", + "start_column": 23, + "start_line": 83, + "type": "str" + } + ], + "signature": "main.Duck.__init__", + "span": { + "bytes": [ + 1975, + 2036 + ], + "end": [ + 84, + 30 + ], + "start": [ + 83, + 4 + ] + }, + "start_line": 83, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 86, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "87:8": { + "kind": "return", + "span": { + "bytes": [ + 2074, + 2088 + ], + "end": [ + 87, + 22 + ], + "start": [ + 87, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "87:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "87:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "87:8" + } + ], + "code_start_line": 87, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 87, + "id": "can://python/class_hierarchy/main.py/Duck/speak(self)", + "kind": "function", + "local_variables": [], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 86, + "name": "self", + "start_column": 14, + "start_line": 86, + "type": "Duck" + } + ], + "return_type": "str", + "signature": "main.Duck.speak", + "span": { + "bytes": [ + 2042, + 2088 + ], + "end": [ + 87, + 22 + ], + "start": [ + 86, + 4 + ] + }, + "start_line": 86, + "summary": [], + "types": {} + }, + "swim": { + "accessed_symbols": [ + { + "col_offset": 22, + "is_builtin": false, + "kind": "class", + "lineno": 89, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 90, + "name": "self", + "qualified_name": "main.Duck", + "scope": "local", + "type": "Duck" + } + ], + "body": { + "90:8": { + "kind": "return", + "span": { + "bytes": [ + 2125, + 2154 + ], + "end": [ + 90, + 37 + ], + "start": [ + 90, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "90:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "90:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "90:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "90:8" + } + ], + "code_start_line": 90, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "90:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.name" + } + ], + "decorators": [], + "end_line": 90, + "id": "can://python/class_hierarchy/main.py/Duck/swim(self)", + "kind": "function", + "local_variables": [], + "name": "swim", + "parameters": [ + { + "end_column": 17, + "end_line": 89, + "name": "self", + "start_column": 13, + "start_line": 89, + "type": "Duck" + } + ], + "return_type": "str", + "signature": "main.Duck.swim", + "span": { + "bytes": [ + 2094, + 2154 + ], + "end": [ + 90, + 37 + ], + "start": [ + 89, + 4 + ] + }, + "start_line": 89, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 90, + "id": "can://python/class_hierarchy/main.py/Duck", + "kind": "class", + "name": "Duck", + "signature": "main.Duck", + "span": { + "bytes": [ + 1942, + 2154 + ], + "end": [ + 90, + 37 + ], + "start": [ + 82, + 0 + ] + }, + "start_line": 82, + "types": {} + }, + "main.PoliceDog": { + "attributes": {}, + "base_classes": [ + "Dog" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 100, + "name": "badge", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "class", + "lineno": 98, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 99, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 100, + "name": "self", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 99, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "100:8": { + "kind": "statement", + "span": { + "bytes": [ + 2475, + 2493 + ], + "end": [ + 100, + 26 + ], + "start": [ + 100, + 8 + ] + } + }, + "99:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2444, + 2451 + ], + "end": [ + 99, + 15 + ], + "start": [ + 99, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + } + ], + "callee_signature": "main.Dog.__init__", + "end_column": 30, + "end_line": 99, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 99 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 99, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 99 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "99:8", + "src": "@entry" + }, + { + "dst": "100:8", + "src": "99:8" + } + ], + "cfg": [ + { + "dst": "99:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "100:8", + "kind": "fallthrough", + "src": "99:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "99:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "100:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "100:8" + } + ], + "code_start_line": 99, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "99:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + }, + { + "dst": "99:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "super" + }, + { + "dst": "100:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "badge" + }, + { + "dst": "100:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + } + ], + "decorators": [], + "end_line": 100, + "id": "can://python/class_hierarchy/main.py/PoliceDog/__init__(self,name,badge)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 100, + "initializer": "badge", + "name": "badge", + "scope": "class", + "start_column": 8, + "start_line": 100, + "type": "PoliceDog" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 98, + "name": "self", + "start_column": 17, + "start_line": 98, + "type": "PoliceDog" + }, + { + "end_column": 32, + "end_line": 98, + "name": "name", + "start_column": 23, + "start_line": 98, + "type": "str" + }, + { + "end_column": 44, + "end_line": 98, + "name": "badge", + "start_column": 34, + "start_line": 98, + "type": "int" + } + ], + "signature": "main.PoliceDog.__init__", + "span": { + "bytes": [ + 2393, + 2493 + ], + "end": [ + 100, + 26 + ], + "start": [ + 98, + 4 + ] + }, + "start_line": 98, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 102, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 104, + "name": "base_bark", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 20, + "is_builtin": false, + "kind": "class", + "lineno": 103, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + }, + { + "col_offset": 40, + "is_builtin": false, + "kind": "variable", + "lineno": 104, + "name": "self", + "qualified_name": "main.PoliceDog", + "scope": "local", + "type": "PoliceDog" + } + ], + "body": { + "103:20": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2543, + 2550 + ], + "end": [ + 103, + 27 + ], + "start": [ + 103, + 20 + ] + } + }, + "103:8": { + "kind": "statement", + "span": { + "bytes": [ + 2531, + 2558 + ], + "end": [ + 103, + 35 + ], + "start": [ + 103, + 8 + ] + } + }, + "104:8": { + "kind": "return", + "span": { + "bytes": [ + 2567, + 2612 + ], + "end": [ + 104, + 53 + ], + "start": [ + 104, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Dog.speak", + "end_column": 35, + "end_line": 103, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "super()", + "receiver_type": "super", + "return_type": "str", + "start_column": 20, + "start_line": 103 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 27, + "end_line": 103, + "is_constructor_call": true, + "method_name": "super", + "start_column": 20, + "start_line": 103 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "103:8", + "src": "@entry" + }, + { + "dst": "104:8", + "src": "103:8" + } + ], + "cfg": [ + { + "dst": "103:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "104:8", + "kind": "fallthrough", + "src": "103:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "103:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "104:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "104:8" + } + ], + "code_start_line": 103, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "103:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "super" + }, + { + "dst": "104:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.badge" + }, + { + "dst": "104:8", + "prov": [ + "ssa" + ], + "src": "103:8", + "var": "base_bark" + } + ], + "decorators": [], + "end_line": 104, + "id": "can://python/class_hierarchy/main.py/PoliceDog/speak(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 103, + "initializer": "super().speak()", + "name": "base_bark", + "scope": "function", + "start_column": 8, + "start_line": 103, + "type": "str" + } + ], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 102, + "name": "self", + "start_column": 14, + "start_line": 102, + "type": "PoliceDog" + } + ], + "return_type": "str", + "signature": "main.PoliceDog.speak", + "span": { + "bytes": [ + 2499, + 2612 + ], + "end": [ + 104, + 53 + ], + "start": [ + 102, + 4 + ] + }, + "start_line": 102, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 104, + "id": "can://python/class_hierarchy/main.py/PoliceDog", + "kind": "class", + "name": "PoliceDog", + "signature": "main.PoliceDog", + "span": { + "bytes": [ + 2367, + 2612 + ], + "end": [ + 104, + 53 + ], + "start": [ + 97, + 0 + ] + }, + "start_line": 97, + "types": {} + }, + "main.RescuePoliceDog": { + "attributes": {}, + "base_classes": [ + "PoliceDog" + ], + "callables": { + "__init__": { + "accessed_symbols": [ + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 110, + "name": "specialty", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 29, + "is_builtin": false, + "kind": "class", + "lineno": 108, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "class", + "lineno": 108, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 57, + "is_builtin": false, + "kind": "class", + "lineno": 108, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 109, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 109, + "name": "badge", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 110, + "name": "self", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "class", + "lineno": 109, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + } + ], + "body": { + "109:8": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2720, + 2727 + ], + "end": [ + 109, + 15 + ], + "start": [ + 109, + 8 + ] + } + }, + "110:8": { + "kind": "statement", + "span": { + "bytes": [ + 2758, + 2784 + ], + "end": [ + 110, + 34 + ], + "start": [ + 110, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "str" + }, + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "main.PoliceDog.__init__", + "end_column": 37, + "end_line": 109, + "is_constructor_call": false, + "method_name": "__init__", + "receiver_expr": "super()", + "receiver_type": "super", + "start_column": 8, + "start_line": 109 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 15, + "end_line": 109, + "is_constructor_call": true, + "method_name": "super", + "start_column": 8, + "start_line": 109 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "109:8", + "src": "@entry" + }, + { + "dst": "110:8", + "src": "109:8" + } + ], + "cfg": [ + { + "dst": "109:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "110:8", + "kind": "fallthrough", + "src": "109:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "109:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "110:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "110:8" + } + ], + "code_start_line": 109, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "109:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "badge" + }, + { + "dst": "109:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + }, + { + "dst": "109:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "super" + }, + { + "dst": "110:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "110:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "specialty" + } + ], + "decorators": [], + "end_line": 110, + "id": "can://python/class_hierarchy/main.py/RescuePoliceDog/__init__(self,name,badge,specialty)", + "kind": "function", + "local_variables": [ + { + "end_column": 22, + "end_line": 110, + "initializer": "specialty", + "name": "specialty", + "scope": "class", + "start_column": 8, + "start_line": 110, + "type": "RescuePoliceDog" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 108, + "name": "self", + "start_column": 17, + "start_line": 108, + "type": "RescuePoliceDog" + }, + { + "end_column": 32, + "end_line": 108, + "name": "name", + "start_column": 23, + "start_line": 108, + "type": "str" + }, + { + "end_column": 44, + "end_line": 108, + "name": "badge", + "start_column": 34, + "start_line": 108, + "type": "int" + }, + { + "end_column": 60, + "end_line": 108, + "name": "specialty", + "start_column": 46, + "start_line": 108, + "type": "str" + } + ], + "signature": "main.RescuePoliceDog.__init__", + "span": { + "bytes": [ + 2653, + 2784 + ], + "end": [ + 110, + 34 + ], + "start": [ + 108, + 4 + ] + }, + "start_line": 108, + "summary": [], + "types": {} + }, + "speak": { + "accessed_symbols": [ + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 112, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "variable", + "lineno": 114, + "name": "base", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 113, + "name": "super", + "qualified_name": "builtins.super", + "scope": "local", + "type": "super" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 114, + "name": "self", + "qualified_name": "main.RescuePoliceDog", + "scope": "local", + "type": "RescuePoliceDog" + } + ], + "body": { + "113:15": { + "callee": "can://python/class_hierarchy/@external/builtins.super/__init__", + "kind": "call", + "span": { + "bytes": [ + 2829, + 2836 + ], + "end": [ + 113, + 22 + ], + "start": [ + 113, + 15 + ] + } + }, + "113:8": { + "kind": "statement", + "span": { + "bytes": [ + 2822, + 2844 + ], + "end": [ + 113, + 30 + ], + "start": [ + 113, + 8 + ] + } + }, + "114:8": { + "kind": "return", + "span": { + "bytes": [ + 2853, + 2888 + ], + "end": [ + 114, + 43 + ], + "start": [ + 114, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.PoliceDog.speak", + "end_column": 30, + "end_line": 113, + "is_constructor_call": false, + "method_name": "speak", + "receiver_expr": "super()", + "receiver_type": "super", + "return_type": "str", + "start_column": 15, + "start_line": 113 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "builtins.super.__init__", + "end_column": 22, + "end_line": 113, + "is_constructor_call": true, + "method_name": "super", + "start_column": 15, + "start_line": 113 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "113:8", + "src": "@entry" + }, + { + "dst": "114:8", + "src": "113:8" + } + ], + "cfg": [ + { + "dst": "113:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "114:8", + "kind": "fallthrough", + "src": "113:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "113:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "114:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "114:8" + } + ], + "code_start_line": 113, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "113:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "super" + }, + { + "dst": "114:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.specialty" + }, + { + "dst": "114:8", + "prov": [ + "ssa" + ], + "src": "113:8", + "var": "base" + } + ], + "decorators": [], + "end_line": 114, + "id": "can://python/class_hierarchy/main.py/RescuePoliceDog/speak(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 12, + "end_line": 113, + "initializer": "super().speak()", + "name": "base", + "scope": "function", + "start_column": 8, + "start_line": 113, + "type": "str" + } + ], + "name": "speak", + "parameters": [ + { + "end_column": 18, + "end_line": 112, + "name": "self", + "start_column": 14, + "start_line": 112, + "type": "RescuePoliceDog" + } + ], + "return_type": "str", + "signature": "main.RescuePoliceDog.speak", + "span": { + "bytes": [ + 2790, + 2888 + ], + "end": [ + 114, + 43 + ], + "start": [ + 112, + 4 + ] + }, + "start_line": 112, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 114, + "id": "can://python/class_hierarchy/main.py/RescuePoliceDog", + "kind": "class", + "name": "RescuePoliceDog", + "signature": "main.RescuePoliceDog", + "span": { + "bytes": [ + 2615, + 2888 + ], + "end": [ + 114, + 43 + ], + "start": [ + 107, + 0 + ] + }, + "start_line": 107, + "types": {} + }, + "main.Swimmer": { + "attributes": {}, + "base_classes": [ + "ABC" + ], + "callables": { + "swim": { + "accessed_symbols": [ + { + "col_offset": 5, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "abstractmethod", + "qualified_name": "abc.abstractmethod", + "scope": "local", + "type": "abstractmethod" + }, + { + "col_offset": 22, + "is_builtin": false, + "kind": "class", + "lineno": 78, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "79:8": { + "kind": "statement", + "span": { + "bytes": [ + 1936, + 1939 + ], + "end": [ + 79, + 11 + ], + "start": [ + 79, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "79:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "79:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "79:8" + } + ], + "code_start_line": 79, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [ + "abstractmethod" + ], + "end_line": 79, + "id": "can://python/class_hierarchy/main.py/Swimmer/swim(self)", + "kind": "function", + "local_variables": [], + "name": "swim", + "parameters": [ + { + "end_column": 17, + "end_line": 78, + "name": "self", + "start_column": 13, + "start_line": 78, + "type": "Swimmer" + } + ], + "return_type": "str", + "signature": "main.Swimmer.swim", + "span": { + "bytes": [ + 1905, + 1939 + ], + "end": [ + 79, + 11 + ], + "start": [ + 78, + 4 + ] + }, + "start_line": 78, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 79, + "id": "can://python/class_hierarchy/main.py/Swimmer", + "kind": "class", + "name": "Swimmer", + "signature": "main.Swimmer", + "span": { + "bytes": [ + 1861, + 1939 + ], + "end": [ + 79, + 11 + ], + "start": [ + 76, + 0 + ] + }, + "start_line": 76, + "types": {} + } + }, + "variables": [] + } + } + }, + "k_limit": 3, + "language": "python", + "max_level": 3, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/decorators_and_hof.a1.json b/test/golden/pipeline_equivalence/decorators_and_hof.a1.json new file mode 100644 index 0000000..c57e429 --- /dev/null +++ b/test/golden/pipeline_equivalence/decorators_and_hof.a1.json @@ -0,0 +1,3362 @@ +{ + "analyzer": { + "config": { + "analysis_level": 1 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/decorators_and_hof/@external/functools/update_wrapper", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/Timer/__init__(self,func)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/double(x)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/apply(func,value)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/double(x)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/compose(f,g)/h(x)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/triple(x)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/compose(f,g)/h(x)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/functools/wraps", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/log_call(func)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/Timer", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/apply(func,value)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 2 + }, + { + "dst": "can://python/decorators_and_hof/main.py/compose(f,g)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/make_adder(n)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/make_multiplier(n)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/functools/wraps", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/builtins.range/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)/wrapper(args,kwargs)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/builtins/print", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/say_hello()", + "weight": 1 + } + ], + "external_symbols": { + "can://python/decorators_and_hof/@external/builtins.range/__init__": { + "id": "can://python/decorators_and_hof/@external/builtins.range/__init__", + "kind": "external", + "module": "builtins.range", + "name": "__init__" + }, + "can://python/decorators_and_hof/@external/builtins/print": { + "id": "can://python/decorators_and_hof/@external/builtins/print", + "kind": "external", + "module": "builtins", + "name": "print" + }, + "can://python/decorators_and_hof/@external/functools/update_wrapper": { + "id": "can://python/decorators_and_hof/@external/functools/update_wrapper", + "kind": "external", + "module": "functools", + "name": "update_wrapper" + }, + "can://python/decorators_and_hof/@external/functools/wraps": { + "id": "can://python/decorators_and_hof/@external/functools/wraps", + "kind": "external", + "module": "functools", + "name": "wraps" + } + }, + "id": "can://python/decorators_and_hof", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [ + { + "content": "Decorator and higher-order function patterns.\n\nExercises:\n- Simple function wrapper (functools.wraps)\n- Parameterised decorator factory\n- Class-based decorator (__call__)\n- Higher-order function (function passed as argument)\n- Closure / function factory\n- Decorator stacking\n", + "end_column": 3, + "end_line": 10, + "is_docstring": true, + "start_column": 0, + "start_line": 1 + } + ], + "content_hash": "705388028325c87c86d513288b2b45d6668d0da3fd6370d90515b334c41d969a", + "file_size": 3407, + "functions": { + "apply": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 63, + "name": "func", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 16, + "is_builtin": false, + "kind": "variable", + "lineno": 63, + "name": "value", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "63:11": { + "kind": "call", + "span": { + "bytes": [ + 1853, + 1864 + ], + "end": [ + 63, + 22 + ], + "start": [ + 63, + 11 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "main.double", + "end_column": 22, + "end_line": 63, + "is_constructor_call": false, + "method_name": "func", + "return_type": "int", + "start_column": 11, + "start_line": 63 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 62, + "comments": [ + { + "content": "Call *func* with *value* and return the result.", + "end_column": 57, + "end_line": 62, + "is_docstring": true, + "start_column": 4, + "start_line": 62 + } + ], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 63, + "id": "can://python/decorators_and_hof/main.py/apply(func,value)", + "kind": "function", + "local_variables": [], + "name": "apply", + "parameters": [ + { + "end_column": 14, + "end_line": 61, + "name": "func", + "start_column": 10, + "start_line": 61, + "type": "double" + }, + { + "end_column": 21, + "end_line": 61, + "name": "value", + "start_column": 16, + "start_line": 61, + "type": "int" + } + ], + "signature": "main.apply", + "span": { + "bytes": [ + 1760, + 1864 + ], + "end": [ + 63, + 22 + ], + "start": [ + 61, + 0 + ] + }, + "start_line": 61, + "summary": [], + "types": {} + }, + "compose": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 78, + "name": "h", + "scope": "local", + "type": "h" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "f", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "g", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 77, + "name": "x", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": { + "h": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "f", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "g", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 77, + "name": "x", + "scope": "local" + } + ], + "body": { + "77:15": { + "kind": "call", + "span": { + "bytes": [ + 2031, + 2038 + ], + "end": [ + 77, + 22 + ], + "start": [ + 77, + 15 + ] + } + }, + "77:17": { + "kind": "call", + "span": { + "bytes": [ + 2033, + 2037 + ], + "end": [ + 77, + 21 + ], + "start": [ + 77, + 17 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "double" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "double" + } + ], + "callee_signature": "main.triple", + "end_column": 22, + "end_line": 77, + "is_constructor_call": false, + "method_name": "f", + "return_type": "int", + "start_column": 15, + "start_line": 77 + }, + { + "argument_types": [ + "Name" + ], + "arguments": [ + { + "ast_kind": "Name" + } + ], + "callee_signature": "main.double", + "end_column": 21, + "end_line": 77, + "is_constructor_call": false, + "method_name": "g", + "return_type": "int", + "start_column": 17, + "start_line": 77 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 77, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 77, + "id": "can://python/decorators_and_hof/main.py/compose(f,g)/h(x)", + "kind": "function", + "local_variables": [], + "name": "h", + "parameters": [ + { + "end_column": 11, + "end_line": 76, + "name": "x", + "start_column": 10, + "start_line": 76 + } + ], + "signature": "main.compose.h", + "span": { + "bytes": [ + 2006, + 2038 + ], + "end": [ + 77, + 22 + ], + "start": [ + 76, + 4 + ] + }, + "start_line": 76, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 75, + "comments": [ + { + "content": "Return a new function h(x) = f(g(x)).", + "end_column": 47, + "end_line": 75, + "is_docstring": true, + "start_column": 4, + "start_line": 75 + } + ], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 78, + "id": "can://python/decorators_and_hof/main.py/compose(f,g)", + "kind": "function", + "local_variables": [], + "name": "compose", + "parameters": [ + { + "end_column": 13, + "end_line": 74, + "name": "f", + "start_column": 12, + "start_line": 74, + "type": "triple" + }, + { + "end_column": 16, + "end_line": 74, + "name": "g", + "start_column": 15, + "start_line": 74, + "type": "double" + } + ], + "signature": "main.compose", + "span": { + "bytes": [ + 1935, + 2051 + ], + "end": [ + 78, + 12 + ], + "start": [ + 74, + 0 + ] + }, + "start_line": 74, + "summary": [], + "types": {} + }, + "compute": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "class", + "lineno": 111, + "name": "Timer", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 113, + "name": "x", + "scope": "local" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 113, + "name": "y", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 113, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "Timer" + ], + "end_line": 113, + "id": "can://python/decorators_and_hof/main.py/compute(x,y)", + "kind": "function", + "local_variables": [], + "name": "compute", + "parameters": [ + { + "end_column": 13, + "end_line": 112, + "name": "x", + "start_column": 12, + "start_line": 112 + }, + { + "end_column": 16, + "end_line": 112, + "name": "y", + "start_column": 15, + "start_line": 112 + } + ], + "signature": "main.compute", + "span": { + "bytes": [ + 2729, + 2764 + ], + "end": [ + 113, + 16 + ], + "start": [ + 112, + 0 + ] + }, + "start_line": 112, + "summary": [], + "types": {} + }, + "double": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 67, + "name": "x", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 67, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 67, + "id": "can://python/decorators_and_hof/main.py/double(x)", + "kind": "function", + "local_variables": [], + "name": "double", + "parameters": [ + { + "end_column": 12, + "end_line": 66, + "name": "x", + "start_column": 11, + "start_line": 66 + } + ], + "signature": "main.double", + "span": { + "bytes": [ + 1867, + 1898 + ], + "end": [ + 67, + 16 + ], + "start": [ + 66, + 0 + ] + }, + "start_line": 66, + "summary": [], + "types": {} + }, + "greet": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 101, + "name": "log_call", + "qualified_name": "main.log_call", + "scope": "local", + "type": "log_call" + }, + { + "col_offset": 24, + "is_builtin": false, + "kind": "class", + "lineno": 102, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 16, + "is_builtin": false, + "kind": "class", + "lineno": 102, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 103, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 103, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "log_call" + ], + "end_line": 103, + "id": "can://python/decorators_and_hof/main.py/greet(name)", + "kind": "function", + "local_variables": [], + "name": "greet", + "parameters": [ + { + "end_column": 19, + "end_line": 102, + "name": "name", + "start_column": 10, + "start_line": 102, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.greet", + "span": { + "bytes": [ + 2614, + 2670 + ], + "end": [ + 103, + 27 + ], + "start": [ + 102, + 0 + ] + }, + "start_line": 102, + "summary": [], + "types": {} + }, + "log_call": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 23, + "name": "wrapper", + "scope": "local", + "type": "wrapper" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "result", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 19, + "name": "func", + "scope": "local" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "func", + "scope": "local" + }, + { + "col_offset": 5, + "is_builtin": false, + "kind": "module", + "lineno": 19, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "19:5": { + "kind": "call", + "span": { + "bytes": [ + 513, + 534 + ], + "end": [ + 19, + 26 + ], + "start": [ + 19, + 5 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Name" + ], + "arguments": [ + { + "ast_kind": "Name" + } + ], + "callee_signature": "functools.wraps", + "end_column": 26, + "end_line": 19, + "is_constructor_call": false, + "method_name": "wraps", + "receiver_expr": "functools", + "receiver_type": "functools", + "start_column": 5, + "start_line": 19 + } + ], + "callables": { + "wrapper": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "result", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 19, + "name": "func", + "scope": "local" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "func", + "scope": "local" + }, + { + "col_offset": 5, + "is_builtin": false, + "kind": "module", + "lineno": 19, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "21:17": { + "kind": "call", + "span": { + "bytes": [ + 586, + 607 + ], + "end": [ + 21, + 38 + ], + "start": [ + 21, + 17 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Starred" + ], + "arguments": [ + { + "ast_kind": "Starred" + } + ], + "end_column": 38, + "end_line": 21, + "is_constructor_call": false, + "method_name": "func", + "start_column": 17, + "start_line": 21 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 21, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "functools.wraps(func)" + ], + "end_line": 22, + "id": "can://python/decorators_and_hof/main.py/log_call(func)/wrapper(args,kwargs)", + "kind": "function", + "local_variables": [ + { + "end_column": 14, + "end_line": 21, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 8, + "start_line": 21 + } + ], + "name": "wrapper", + "parameters": [ + { + "end_column": 21, + "end_line": 20, + "name": "args", + "start_column": 17, + "start_line": 20, + "type": "tuple" + }, + { + "end_column": 31, + "end_line": 20, + "name": "kwargs", + "start_column": 25, + "start_line": 20, + "type": "dict" + } + ], + "signature": "main.log_call.wrapper", + "span": { + "bytes": [ + 539, + 629 + ], + "end": [ + 22, + 21 + ], + "start": [ + 20, + 4 + ] + }, + "start_line": 20, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 20, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 23, + "id": "can://python/decorators_and_hof/main.py/log_call(func)", + "kind": "function", + "local_variables": [ + { + "end_column": 14, + "end_line": 21, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 8, + "start_line": 21 + } + ], + "name": "log_call", + "parameters": [ + { + "end_column": 17, + "end_line": 18, + "name": "func", + "start_column": 13, + "start_line": 18 + } + ], + "signature": "main.log_call", + "span": { + "bytes": [ + 488, + 648 + ], + "end": [ + 23, + 18 + ], + "start": [ + 18, + 0 + ] + }, + "start_line": 18, + "summary": [], + "types": {} + }, + "main": { + "accessed_symbols": [ + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 127, + "name": "apply", + "qualified_name": "main.apply", + "scope": "local", + "type": "apply" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 127, + "name": "double", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 128, + "name": "apply", + "qualified_name": "main.apply", + "scope": "local", + "type": "apply" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 128, + "name": "triple", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "function", + "lineno": 130, + "name": "compose", + "qualified_name": "main.compose", + "scope": "local", + "type": "compose" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "function", + "lineno": 130, + "name": "triple", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "function", + "lineno": 130, + "name": "double", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 131, + "name": "double_then_triple", + "scope": "local", + "type": "h" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 133, + "name": "make_adder", + "qualified_name": "main.make_adder", + "scope": "local", + "type": "make_adder" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 134, + "name": "make_multiplier", + "qualified_name": "main.make_multiplier", + "scope": "local", + "type": "make_multiplier" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 135, + "name": "add5", + "scope": "local", + "type": "adder" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 136, + "name": "mul3", + "scope": "local", + "type": "multiplier" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 138, + "name": "greet", + "scope": "local", + "type": "greet" + }, + { + "col_offset": 4, + "is_builtin": false, + "kind": "function", + "lineno": 139, + "name": "say_hello", + "scope": "local", + "type": "say_hello" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "variable", + "lineno": 140, + "name": "compute", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 141, + "name": "stacked", + "scope": "local", + "type": "stacked" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r1", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r2", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r3", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r4", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r5", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r6", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 35, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r7", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r8", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "127:9": { + "kind": "call", + "span": { + "bytes": [ + 3022, + 3039 + ], + "end": [ + 127, + 26 + ], + "start": [ + 127, + 9 + ] + } + }, + "128:9": { + "kind": "call", + "span": { + "bytes": [ + 3049, + 3066 + ], + "end": [ + 128, + 26 + ], + "start": [ + 128, + 9 + ] + } + }, + "130:25": { + "kind": "call", + "span": { + "bytes": [ + 3093, + 3116 + ], + "end": [ + 130, + 48 + ], + "start": [ + 130, + 25 + ] + } + }, + "131:9": { + "kind": "call", + "span": { + "bytes": [ + 3126, + 3147 + ], + "end": [ + 131, + 30 + ], + "start": [ + 131, + 9 + ] + } + }, + "133:11": { + "kind": "call", + "span": { + "bytes": [ + 3160, + 3173 + ], + "end": [ + 133, + 24 + ], + "start": [ + 133, + 11 + ] + } + }, + "134:11": { + "kind": "call", + "span": { + "bytes": [ + 3185, + 3203 + ], + "end": [ + 134, + 29 + ], + "start": [ + 134, + 11 + ] + } + }, + "135:9": { + "kind": "call", + "span": { + "bytes": [ + 3213, + 3221 + ], + "end": [ + 135, + 17 + ], + "start": [ + 135, + 9 + ] + } + }, + "136:9": { + "kind": "call", + "span": { + "bytes": [ + 3231, + 3239 + ], + "end": [ + 136, + 17 + ], + "start": [ + 136, + 9 + ] + } + }, + "138:9": { + "kind": "call", + "span": { + "bytes": [ + 3250, + 3264 + ], + "end": [ + 138, + 23 + ], + "start": [ + 138, + 9 + ] + } + }, + "139:4": { + "kind": "call", + "span": { + "bytes": [ + 3269, + 3280 + ], + "end": [ + 139, + 15 + ], + "start": [ + 139, + 4 + ] + } + }, + "140:9": { + "kind": "call", + "span": { + "bytes": [ + 3290, + 3303 + ], + "end": [ + 140, + 22 + ], + "start": [ + 140, + 9 + ] + } + }, + "141:9": { + "kind": "call", + "span": { + "bytes": [ + 3313, + 3323 + ], + "end": [ + 141, + 19 + ], + "start": [ + 141, + 9 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "double", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "double" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.apply", + "end_column": 26, + "end_line": 127, + "is_constructor_call": false, + "method_name": "apply", + "start_column": 9, + "start_line": 127 + }, + { + "argument_types": [ + "triple", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "triple" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.apply", + "end_column": 26, + "end_line": 128, + "is_constructor_call": false, + "method_name": "apply", + "start_column": 9, + "start_line": 128 + }, + { + "argument_types": [ + "triple", + "double" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "triple" + }, + { + "ast_kind": "Name", + "inferred_type": "double" + } + ], + "callee_signature": "main.compose", + "end_column": 48, + "end_line": 130, + "is_constructor_call": false, + "method_name": "compose", + "return_type": "h", + "start_column": 25, + "start_line": 130 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 30, + "end_line": 131, + "is_constructor_call": false, + "method_name": "double_then_triple", + "return_type": "int", + "start_column": 9, + "start_line": 131 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.make_adder", + "end_column": 24, + "end_line": 133, + "is_constructor_call": false, + "method_name": "make_adder", + "return_type": "adder", + "start_column": 11, + "start_line": 133 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.make_multiplier", + "end_column": 29, + "end_line": 134, + "is_constructor_call": false, + "method_name": "make_multiplier", + "return_type": "multiplier", + "start_column": 11, + "start_line": 134 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 17, + "end_line": 135, + "is_constructor_call": false, + "method_name": "add5", + "return_type": "int", + "start_column": 9, + "start_line": 135 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 17, + "end_line": 136, + "is_constructor_call": false, + "method_name": "mul3", + "return_type": "int", + "start_column": 9, + "start_line": 136 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "end_column": 23, + "end_line": 138, + "is_constructor_call": false, + "method_name": "greet", + "return_type": "str", + "start_column": 9, + "start_line": 138 + }, + { + "argument_types": [], + "arguments": [], + "end_column": 15, + "end_line": 139, + "is_constructor_call": false, + "method_name": "say_hello", + "start_column": 4, + "start_line": 139 + }, + { + "argument_types": [ + "int", + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.Timer", + "end_column": 22, + "end_line": 140, + "is_constructor_call": false, + "method_name": "compute", + "start_column": 9, + "start_line": 140 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 19, + "end_line": 141, + "is_constructor_call": false, + "method_name": "stacked", + "return_type": "int", + "start_column": 9, + "start_line": 141 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 127, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 143, + "id": "can://python/decorators_and_hof/main.py/main()", + "kind": "function", + "local_variables": [ + { + "end_column": 6, + "end_line": 127, + "initializer": "apply(double, 10)", + "name": "r1", + "scope": "function", + "start_column": 4, + "start_line": 127, + "type": "int" + }, + { + "end_column": 6, + "end_line": 128, + "initializer": "apply(triple, 10)", + "name": "r2", + "scope": "function", + "start_column": 4, + "start_line": 128, + "type": "int" + }, + { + "end_column": 22, + "end_line": 130, + "initializer": "compose(triple, double)", + "name": "double_then_triple", + "scope": "function", + "start_column": 4, + "start_line": 130, + "type": "h" + }, + { + "end_column": 6, + "end_line": 131, + "initializer": "double_then_triple(5)", + "name": "r3", + "scope": "function", + "start_column": 4, + "start_line": 131, + "type": "int" + }, + { + "end_column": 8, + "end_line": 133, + "initializer": "make_adder(5)", + "name": "add5", + "scope": "function", + "start_column": 4, + "start_line": 133, + "type": "adder" + }, + { + "end_column": 8, + "end_line": 134, + "initializer": "make_multiplier(3)", + "name": "mul3", + "scope": "function", + "start_column": 4, + "start_line": 134, + "type": "multiplier" + }, + { + "end_column": 6, + "end_line": 135, + "initializer": "add5(10)", + "name": "r4", + "scope": "function", + "start_column": 4, + "start_line": 135, + "type": "int" + }, + { + "end_column": 6, + "end_line": 136, + "initializer": "mul3(10)", + "name": "r5", + "scope": "function", + "start_column": 4, + "start_line": 136, + "type": "int" + }, + { + "end_column": 6, + "end_line": 138, + "initializer": "greet('world')", + "name": "r6", + "scope": "function", + "start_column": 4, + "start_line": 138, + "type": "str" + }, + { + "end_column": 6, + "end_line": 140, + "initializer": "compute(2, 3)", + "name": "r7", + "scope": "function", + "start_column": 4, + "start_line": 140, + "type": "int" + }, + { + "end_column": 6, + "end_line": 141, + "initializer": "stacked(7)", + "name": "r8", + "scope": "function", + "start_column": 4, + "start_line": 141, + "type": "int" + } + ], + "name": "main", + "parameters": [], + "signature": "main.main", + "span": { + "bytes": [ + 3001, + 3366 + ], + "end": [ + 143, + 41 + ], + "start": [ + 126, + 0 + ] + }, + "start_line": 126, + "summary": [], + "types": {} + }, + "make_adder": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 88, + "name": "adder", + "scope": "local", + "type": "adder" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "class", + "lineno": 85, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": { + "adder": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 87, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 87, + "id": "can://python/decorators_and_hof/main.py/make_adder(n)/adder(x)", + "kind": "function", + "local_variables": [], + "name": "adder", + "parameters": [ + { + "end_column": 15, + "end_line": 86, + "name": "x", + "start_column": 14, + "start_line": 86 + } + ], + "signature": "main.make_adder.adder", + "span": { + "bytes": [ + 2271, + 2305 + ], + "end": [ + 87, + 20 + ], + "start": [ + 86, + 4 + ] + }, + "start_line": 86, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 86, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 88, + "id": "can://python/decorators_and_hof/main.py/make_adder(n)", + "kind": "function", + "local_variables": [], + "name": "make_adder", + "parameters": [ + { + "end_column": 21, + "end_line": 85, + "name": "n", + "start_column": 15, + "start_line": 85, + "type": "int" + } + ], + "signature": "main.make_adder", + "span": { + "bytes": [ + 2243, + 2322 + ], + "end": [ + 88, + 16 + ], + "start": [ + 85, + 0 + ] + }, + "start_line": 85, + "summary": [], + "types": {} + }, + "make_multiplier": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 94, + "name": "multiplier", + "scope": "local", + "type": "multiplier" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 91, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": { + "multiplier": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 93, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 93, + "id": "can://python/decorators_and_hof/main.py/make_multiplier(n)/multiplier(x)", + "kind": "function", + "local_variables": [], + "name": "multiplier", + "parameters": [ + { + "end_column": 20, + "end_line": 92, + "name": "x", + "start_column": 19, + "start_line": 92 + } + ], + "signature": "main.make_multiplier.multiplier", + "span": { + "bytes": [ + 2358, + 2397 + ], + "end": [ + 93, + 20 + ], + "start": [ + 92, + 4 + ] + }, + "start_line": 92, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 92, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 94, + "id": "can://python/decorators_and_hof/main.py/make_multiplier(n)", + "kind": "function", + "local_variables": [], + "name": "make_multiplier", + "parameters": [ + { + "end_column": 26, + "end_line": 91, + "name": "n", + "start_column": 20, + "start_line": 91, + "type": "int" + } + ], + "signature": "main.make_multiplier", + "span": { + "bytes": [ + 2325, + 2419 + ], + "end": [ + 94, + 21 + ], + "start": [ + 91, + 0 + ] + }, + "start_line": 91, + "summary": [], + "types": {} + }, + "repeat": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 39, + "name": "decorator", + "scope": "local", + "type": "decorator" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "class", + "lineno": 30, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 38, + "name": "wrapper", + "scope": "local", + "type": "wrapper" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 37, + "name": "result", + "scope": "local" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 32, + "name": "func", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "module", + "lineno": 32, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "func", + "scope": "local" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": {}, + "call_sites": [], + "callables": { + "decorator": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 38, + "name": "wrapper", + "scope": "local", + "type": "wrapper" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 37, + "name": "result", + "scope": "local" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 32, + "name": "func", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "module", + "lineno": 32, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "func", + "scope": "local" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "32:9": { + "kind": "call", + "span": { + "bytes": [ + 899, + 920 + ], + "end": [ + 32, + 30 + ], + "start": [ + 32, + 9 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Name" + ], + "arguments": [ + { + "ast_kind": "Name" + } + ], + "callee_signature": "functools.wraps", + "end_column": 30, + "end_line": 32, + "is_constructor_call": false, + "method_name": "wraps", + "receiver_expr": "functools", + "receiver_type": "functools", + "start_column": 9, + "start_line": 32 + } + ], + "callables": { + "wrapper": { + "accessed_symbols": [ + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 37, + "name": "result", + "scope": "local" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 32, + "name": "func", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "module", + "lineno": 32, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "func", + "scope": "local" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "35:21": { + "kind": "call", + "span": { + "bytes": [ + 1006, + 1014 + ], + "end": [ + 35, + 29 + ], + "start": [ + 35, + 21 + ] + } + }, + "36:25": { + "kind": "call", + "span": { + "bytes": [ + 1041, + 1062 + ], + "end": [ + 36, + 46 + ], + "start": [ + 36, + 25 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "builtins.range.__init__", + "end_column": 29, + "end_line": 35, + "is_constructor_call": true, + "method_name": "range", + "return_type": "range", + "start_column": 21, + "start_line": 35 + }, + { + "argument_types": [ + "Starred" + ], + "arguments": [ + { + "ast_kind": "Starred" + } + ], + "end_column": 46, + "end_line": 36, + "is_constructor_call": false, + "method_name": "func", + "start_column": 25, + "start_line": 36 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 34, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [ + "functools.wraps(func)" + ], + "end_line": 37, + "id": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)/wrapper(args,kwargs)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 34, + "initializer": "None", + "name": "result", + "scope": "function", + "start_column": 12, + "start_line": 34, + "type": "NoneType" + }, + { + "end_column": 22, + "end_line": 36, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 16, + "start_line": 36 + } + ], + "name": "wrapper", + "parameters": [ + { + "end_column": 25, + "end_line": 33, + "name": "args", + "start_column": 21, + "start_line": 33, + "type": "tuple" + }, + { + "end_column": 35, + "end_line": 33, + "name": "kwargs", + "start_column": 29, + "start_line": 33, + "type": "dict" + } + ], + "signature": "main.repeat.decorator.wrapper", + "span": { + "bytes": [ + 929, + 1088 + ], + "end": [ + 37, + 25 + ], + "start": [ + 33, + 8 + ] + }, + "start_line": 33, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 33, + "comments": [], + "cyclomatic_complexity": 4, + "ddg": [], + "decorators": [], + "end_line": 38, + "id": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 34, + "initializer": "None", + "name": "result", + "scope": "function", + "start_column": 12, + "start_line": 34, + "type": "NoneType" + }, + { + "end_column": 22, + "end_line": 36, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 16, + "start_line": 36 + } + ], + "name": "decorator", + "parameters": [ + { + "end_column": 22, + "end_line": 31, + "name": "func", + "start_column": 18, + "start_line": 31 + } + ], + "signature": "main.repeat.decorator", + "span": { + "bytes": [ + 869, + 1111 + ], + "end": [ + 38, + 22 + ], + "start": [ + 31, + 4 + ] + }, + "start_line": 31, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 31, + "comments": [], + "cyclomatic_complexity": 5, + "ddg": [], + "decorators": [], + "end_line": 39, + "id": "can://python/decorators_and_hof/main.py/repeat(n)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 34, + "initializer": "None", + "name": "result", + "scope": "function", + "start_column": 12, + "start_line": 34, + "type": "NoneType" + }, + { + "end_column": 22, + "end_line": 36, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 16, + "start_line": 36 + } + ], + "name": "repeat", + "parameters": [ + { + "end_column": 17, + "end_line": 30, + "name": "n", + "start_column": 11, + "start_line": 30, + "type": "int" + } + ], + "signature": "main.repeat", + "span": { + "bytes": [ + 845, + 1132 + ], + "end": [ + 39, + 20 + ], + "start": [ + 30, + 0 + ] + }, + "start_line": 30, + "summary": [], + "types": {} + }, + "say_hello": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 106, + "name": "repeat", + "qualified_name": "main.repeat", + "scope": "local", + "type": "repeat" + }, + { + "col_offset": 4, + "is_builtin": false, + "kind": "function", + "lineno": 108, + "name": "print", + "qualified_name": "builtins.print", + "scope": "local", + "type": "print" + } + ], + "body": { + "108:4": { + "kind": "call", + "span": { + "bytes": [ + 2705, + 2719 + ], + "end": [ + 108, + 18 + ], + "start": [ + 108, + 4 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.print", + "end_column": 18, + "end_line": 108, + "is_constructor_call": false, + "method_name": "print", + "start_column": 4, + "start_line": 108 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 108, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [ + "repeat(3)" + ], + "end_line": 108, + "id": "can://python/decorators_and_hof/main.py/say_hello()", + "kind": "function", + "local_variables": [], + "name": "say_hello", + "parameters": [], + "signature": "main.say_hello", + "span": { + "bytes": [ + 2684, + 2719 + ], + "end": [ + 108, + 18 + ], + "start": [ + 107, + 0 + ] + }, + "start_line": 107, + "summary": [], + "types": {} + }, + "stacked": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 116, + "name": "log_call", + "qualified_name": "main.log_call", + "scope": "local", + "type": "log_call" + }, + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 117, + "name": "repeat", + "qualified_name": "main.repeat", + "scope": "local", + "type": "repeat" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 119, + "name": "value", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 119, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "log_call", + "repeat(2)" + ], + "end_line": 119, + "id": "can://python/decorators_and_hof/main.py/stacked(value)", + "kind": "function", + "local_variables": [], + "name": "stacked", + "parameters": [ + { + "end_column": 17, + "end_line": 118, + "name": "value", + "start_column": 12, + "start_line": 118 + } + ], + "signature": "main.stacked", + "span": { + "bytes": [ + 2788, + 2829 + ], + "end": [ + 119, + 21 + ], + "start": [ + 118, + 0 + ] + }, + "start_line": 118, + "summary": [], + "types": {} + }, + "triple": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 71, + "name": "x", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 71, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 71, + "id": "can://python/decorators_and_hof/main.py/triple(x)", + "kind": "function", + "local_variables": [], + "name": "triple", + "parameters": [ + { + "end_column": 12, + "end_line": 70, + "name": "x", + "start_column": 11, + "start_line": 70 + } + ], + "signature": "main.triple", + "span": { + "bytes": [ + 1901, + 1932 + ], + "end": [ + 71, + 16 + ], + "start": [ + 70, + 0 + ] + }, + "start_line": 70, + "summary": [], + "types": {} + } + }, + "id": "can://python/decorators_and_hof/main.py", + "imports": [ + { + "end_column": 16, + "end_line": 11, + "module": "functools", + "name": "functools", + "start_column": 0, + "start_line": 11 + } + ], + "kind": "module", + "module_name": "main", + "source": "\"\"\"Decorator and higher-order function patterns.\n\nExercises:\n- Simple function wrapper (functools.wraps)\n- Parameterised decorator factory\n- Class-based decorator (__call__)\n- Higher-order function (function passed as argument)\n- Closure / function factory\n- Decorator stacking\n\"\"\"\nimport functools\n\n\n# ---------------------------------------------------------------------------\n# 1. Simple wrapper decorator\n# ---------------------------------------------------------------------------\n\ndef log_call(func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n result = func(*args, **kwargs)\n return result\n return wrapper\n\n\n# ---------------------------------------------------------------------------\n# 2. Parameterised decorator factory\n# ---------------------------------------------------------------------------\n\ndef repeat(n: int):\n def decorator(func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n result = None\n for _ in range(n):\n result = func(*args, **kwargs)\n return result\n return wrapper\n return decorator\n\n\n# ---------------------------------------------------------------------------\n# 3. Class-based decorator\n# ---------------------------------------------------------------------------\n\nclass Timer:\n def __init__(self, func):\n functools.update_wrapper(self, func)\n self.func = func\n self.call_count = 0\n\n def __call__(self, *args, **kwargs):\n self.call_count += 1\n return self.func(*args, **kwargs)\n\n\n# ---------------------------------------------------------------------------\n# 4. Higher-order functions\n# ---------------------------------------------------------------------------\n\ndef apply(func, value):\n \"\"\"Call *func* with *value* and return the result.\"\"\"\n return func(value)\n\n\ndef double(x):\n return x * 2\n\n\ndef triple(x):\n return x * 3\n\n\ndef compose(f, g):\n \"\"\"Return a new function h(x) = f(g(x)).\"\"\"\n def h(x):\n return f(g(x))\n return h\n\n\n# ---------------------------------------------------------------------------\n# 5. Closure / function factory\n# ---------------------------------------------------------------------------\n\ndef make_adder(n: int):\n def adder(x):\n return x + n\n return adder\n\n\ndef make_multiplier(n: int):\n def multiplier(x):\n return x * n\n return multiplier\n\n\n# ---------------------------------------------------------------------------\n# 6. Decorated callables\n# ---------------------------------------------------------------------------\n\n@log_call\ndef greet(name: str) -> str:\n return f\"Hello, {name}\"\n\n\n@repeat(3)\ndef say_hello():\n print(\"hello\")\n\n\n@Timer\ndef compute(x, y):\n return x + y\n\n\n@log_call\n@repeat(2)\ndef stacked(value):\n return value * 10\n\n\n# ---------------------------------------------------------------------------\n# 7. Driver\n# ---------------------------------------------------------------------------\n\ndef main():\n r1 = apply(double, 10)\n r2 = apply(triple, 10)\n\n double_then_triple = compose(triple, double)\n r3 = double_then_triple(5)\n\n add5 = make_adder(5)\n mul3 = make_multiplier(3)\n r4 = add5(10)\n r5 = mul3(10)\n\n r6 = greet(\"world\")\n say_hello()\n r7 = compute(2, 3)\n r8 = stacked(7)\n\n return r1, r2, r3, r4, r5, r6, r7, r8\n\n\nif __name__ == \"__main__\":\n main()\n", + "types": { + "main.Timer": { + "attributes": {}, + "base_classes": [], + "callables": { + "__call__": { + "accessed_symbols": [ + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 53, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 54, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 54, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "variable", + "lineno": 54, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "54:15": { + "kind": "call", + "span": { + "bytes": [ + 1546, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 54, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Starred" + ], + "arguments": [ + { + "ast_kind": "Starred" + } + ], + "end_column": 41, + "end_line": 54, + "is_constructor_call": false, + "method_name": "func", + "receiver_expr": "self", + "receiver_type": "Timer", + "start_column": 15, + "start_line": 54 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 53, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 54, + "id": "can://python/decorators_and_hof/main.py/Timer/__call__(self,args,kwargs)", + "kind": "function", + "local_variables": [], + "name": "__call__", + "parameters": [ + { + "end_column": 21, + "end_line": 52, + "name": "self", + "start_column": 17, + "start_line": 52, + "type": "Timer" + }, + { + "end_column": 28, + "end_line": 52, + "name": "args", + "start_column": 24, + "start_line": 52, + "type": "tuple" + }, + { + "end_column": 38, + "end_line": 52, + "name": "kwargs", + "start_column": 32, + "start_line": 52, + "type": "dict" + } + ], + "signature": "main.Timer.__call__", + "span": { + "bytes": [ + 1465, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 52, + 4 + ] + }, + "start_line": 52, + "summary": [], + "types": {} + }, + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 49, + "name": "func", + "scope": "local" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 48, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 48, + "name": "func", + "scope": "local" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 49, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 50, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "module", + "lineno": 48, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + } + ], + "body": { + "48:8": { + "kind": "call", + "span": { + "bytes": [ + 1370, + 1406 + ], + "end": [ + 48, + 44 + ], + "start": [ + 48, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Timer", + "Name" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "Timer" + }, + { + "ast_kind": "Name" + } + ], + "callee_signature": "functools.update_wrapper", + "end_column": 44, + "end_line": 48, + "is_constructor_call": false, + "method_name": "update_wrapper", + "receiver_expr": "functools", + "receiver_type": "functools", + "start_column": 8, + "start_line": 48 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 48, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 50, + "id": "can://python/decorators_and_hof/main.py/Timer/__init__(self,func)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 49, + "initializer": "func", + "name": "func", + "scope": "class", + "start_column": 8, + "start_line": 49, + "type": "Timer" + }, + { + "end_column": 23, + "end_line": 50, + "initializer": "0", + "name": "call_count", + "scope": "class", + "start_column": 8, + "start_line": 50, + "type": "Timer" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 47, + "name": "self", + "start_column": 17, + "start_line": 47, + "type": "Timer" + }, + { + "end_column": 27, + "end_line": 47, + "name": "func", + "start_column": 23, + "start_line": 47 + } + ], + "signature": "main.Timer.__init__", + "span": { + "bytes": [ + 1336, + 1459 + ], + "end": [ + 50, + 27 + ], + "start": [ + 47, + 4 + ] + }, + "start_line": 47, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 54, + "id": "can://python/decorators_and_hof/main.py/Timer", + "kind": "class", + "name": "Timer", + "signature": "main.Timer", + "span": { + "bytes": [ + 1319, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 46, + 0 + ] + }, + "start_line": 46, + "types": {} + } + }, + "variables": [] + } + } + }, + "language": "python", + "max_level": 1, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/decorators_and_hof.a2.json b/test/golden/pipeline_equivalence/decorators_and_hof.a2.json new file mode 100644 index 0000000..96d0d17 --- /dev/null +++ b/test/golden/pipeline_equivalence/decorators_and_hof.a2.json @@ -0,0 +1,3376 @@ +{ + "analyzer": { + "config": { + "analysis_level": 2 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/decorators_and_hof/@external/functools/update_wrapper", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/Timer/__init__(self,func)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/double(x)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/apply(func,value)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/double(x)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/compose(f,g)/h(x)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/triple(x)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/compose(f,g)/h(x)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/functools/wraps", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/log_call(func)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/Timer", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/apply(func,value)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 2 + }, + { + "dst": "can://python/decorators_and_hof/main.py/compose(f,g)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/make_adder(n)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/make_multiplier(n)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/functools/wraps", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/builtins.range/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)/wrapper(args,kwargs)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/builtins/print", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/say_hello()", + "weight": 1 + } + ], + "external_symbols": { + "can://python/decorators_and_hof/@external/builtins.range/__init__": { + "id": "can://python/decorators_and_hof/@external/builtins.range/__init__", + "kind": "external", + "module": "builtins.range", + "name": "__init__" + }, + "can://python/decorators_and_hof/@external/builtins/print": { + "id": "can://python/decorators_and_hof/@external/builtins/print", + "kind": "external", + "module": "builtins", + "name": "print" + }, + "can://python/decorators_and_hof/@external/functools/update_wrapper": { + "id": "can://python/decorators_and_hof/@external/functools/update_wrapper", + "kind": "external", + "module": "functools", + "name": "update_wrapper" + }, + "can://python/decorators_and_hof/@external/functools/wraps": { + "id": "can://python/decorators_and_hof/@external/functools/wraps", + "kind": "external", + "module": "functools", + "name": "wraps" + } + }, + "id": "can://python/decorators_and_hof", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [ + { + "content": "Decorator and higher-order function patterns.\n\nExercises:\n- Simple function wrapper (functools.wraps)\n- Parameterised decorator factory\n- Class-based decorator (__call__)\n- Higher-order function (function passed as argument)\n- Closure / function factory\n- Decorator stacking\n", + "end_column": 3, + "end_line": 10, + "is_docstring": true, + "start_column": 0, + "start_line": 1 + } + ], + "content_hash": "705388028325c87c86d513288b2b45d6668d0da3fd6370d90515b334c41d969a", + "file_size": 3407, + "functions": { + "apply": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 63, + "name": "func", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 16, + "is_builtin": false, + "kind": "variable", + "lineno": 63, + "name": "value", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "63:11": { + "callee": "can://python/decorators_and_hof/main.py/double(x)", + "kind": "call", + "span": { + "bytes": [ + 1853, + 1864 + ], + "end": [ + 63, + 22 + ], + "start": [ + 63, + 11 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "main.double", + "end_column": 22, + "end_line": 63, + "is_constructor_call": false, + "method_name": "func", + "return_type": "int", + "start_column": 11, + "start_line": 63 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 62, + "comments": [ + { + "content": "Call *func* with *value* and return the result.", + "end_column": 57, + "end_line": 62, + "is_docstring": true, + "start_column": 4, + "start_line": 62 + } + ], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 63, + "id": "can://python/decorators_and_hof/main.py/apply(func,value)", + "kind": "function", + "local_variables": [], + "name": "apply", + "parameters": [ + { + "end_column": 14, + "end_line": 61, + "name": "func", + "start_column": 10, + "start_line": 61, + "type": "double" + }, + { + "end_column": 21, + "end_line": 61, + "name": "value", + "start_column": 16, + "start_line": 61, + "type": "int" + } + ], + "signature": "main.apply", + "span": { + "bytes": [ + 1760, + 1864 + ], + "end": [ + 63, + 22 + ], + "start": [ + 61, + 0 + ] + }, + "start_line": 61, + "summary": [], + "types": {} + }, + "compose": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 78, + "name": "h", + "scope": "local", + "type": "h" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "f", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "g", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 77, + "name": "x", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": { + "h": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "f", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "g", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 77, + "name": "x", + "scope": "local" + } + ], + "body": { + "77:15": { + "callee": "can://python/decorators_and_hof/main.py/triple(x)", + "kind": "call", + "span": { + "bytes": [ + 2031, + 2038 + ], + "end": [ + 77, + 22 + ], + "start": [ + 77, + 15 + ] + } + }, + "77:17": { + "callee": "can://python/decorators_and_hof/main.py/double(x)", + "kind": "call", + "span": { + "bytes": [ + 2033, + 2037 + ], + "end": [ + 77, + 21 + ], + "start": [ + 77, + 17 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "double" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "double" + } + ], + "callee_signature": "main.triple", + "end_column": 22, + "end_line": 77, + "is_constructor_call": false, + "method_name": "f", + "return_type": "int", + "start_column": 15, + "start_line": 77 + }, + { + "argument_types": [ + "Name" + ], + "arguments": [ + { + "ast_kind": "Name" + } + ], + "callee_signature": "main.double", + "end_column": 21, + "end_line": 77, + "is_constructor_call": false, + "method_name": "g", + "return_type": "int", + "start_column": 17, + "start_line": 77 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 77, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 77, + "id": "can://python/decorators_and_hof/main.py/compose(f,g)/h(x)", + "kind": "function", + "local_variables": [], + "name": "h", + "parameters": [ + { + "end_column": 11, + "end_line": 76, + "name": "x", + "start_column": 10, + "start_line": 76 + } + ], + "signature": "main.compose.h", + "span": { + "bytes": [ + 2006, + 2038 + ], + "end": [ + 77, + 22 + ], + "start": [ + 76, + 4 + ] + }, + "start_line": 76, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 75, + "comments": [ + { + "content": "Return a new function h(x) = f(g(x)).", + "end_column": 47, + "end_line": 75, + "is_docstring": true, + "start_column": 4, + "start_line": 75 + } + ], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 78, + "id": "can://python/decorators_and_hof/main.py/compose(f,g)", + "kind": "function", + "local_variables": [], + "name": "compose", + "parameters": [ + { + "end_column": 13, + "end_line": 74, + "name": "f", + "start_column": 12, + "start_line": 74, + "type": "triple" + }, + { + "end_column": 16, + "end_line": 74, + "name": "g", + "start_column": 15, + "start_line": 74, + "type": "double" + } + ], + "signature": "main.compose", + "span": { + "bytes": [ + 1935, + 2051 + ], + "end": [ + 78, + 12 + ], + "start": [ + 74, + 0 + ] + }, + "start_line": 74, + "summary": [], + "types": {} + }, + "compute": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "class", + "lineno": 111, + "name": "Timer", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 113, + "name": "x", + "scope": "local" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 113, + "name": "y", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 113, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "Timer" + ], + "end_line": 113, + "id": "can://python/decorators_and_hof/main.py/compute(x,y)", + "kind": "function", + "local_variables": [], + "name": "compute", + "parameters": [ + { + "end_column": 13, + "end_line": 112, + "name": "x", + "start_column": 12, + "start_line": 112 + }, + { + "end_column": 16, + "end_line": 112, + "name": "y", + "start_column": 15, + "start_line": 112 + } + ], + "signature": "main.compute", + "span": { + "bytes": [ + 2729, + 2764 + ], + "end": [ + 113, + 16 + ], + "start": [ + 112, + 0 + ] + }, + "start_line": 112, + "summary": [], + "types": {} + }, + "double": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 67, + "name": "x", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 67, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 67, + "id": "can://python/decorators_and_hof/main.py/double(x)", + "kind": "function", + "local_variables": [], + "name": "double", + "parameters": [ + { + "end_column": 12, + "end_line": 66, + "name": "x", + "start_column": 11, + "start_line": 66 + } + ], + "signature": "main.double", + "span": { + "bytes": [ + 1867, + 1898 + ], + "end": [ + 67, + 16 + ], + "start": [ + 66, + 0 + ] + }, + "start_line": 66, + "summary": [], + "types": {} + }, + "greet": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 101, + "name": "log_call", + "qualified_name": "main.log_call", + "scope": "local", + "type": "log_call" + }, + { + "col_offset": 24, + "is_builtin": false, + "kind": "class", + "lineno": 102, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 16, + "is_builtin": false, + "kind": "class", + "lineno": 102, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 103, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 103, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "log_call" + ], + "end_line": 103, + "id": "can://python/decorators_and_hof/main.py/greet(name)", + "kind": "function", + "local_variables": [], + "name": "greet", + "parameters": [ + { + "end_column": 19, + "end_line": 102, + "name": "name", + "start_column": 10, + "start_line": 102, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.greet", + "span": { + "bytes": [ + 2614, + 2670 + ], + "end": [ + 103, + 27 + ], + "start": [ + 102, + 0 + ] + }, + "start_line": 102, + "summary": [], + "types": {} + }, + "log_call": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 23, + "name": "wrapper", + "scope": "local", + "type": "wrapper" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "result", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 19, + "name": "func", + "scope": "local" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "func", + "scope": "local" + }, + { + "col_offset": 5, + "is_builtin": false, + "kind": "module", + "lineno": 19, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "19:5": { + "callee": "can://python/decorators_and_hof/@external/functools/wraps", + "kind": "call", + "span": { + "bytes": [ + 513, + 534 + ], + "end": [ + 19, + 26 + ], + "start": [ + 19, + 5 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Name" + ], + "arguments": [ + { + "ast_kind": "Name" + } + ], + "callee_signature": "functools.wraps", + "end_column": 26, + "end_line": 19, + "is_constructor_call": false, + "method_name": "wraps", + "receiver_expr": "functools", + "receiver_type": "functools", + "start_column": 5, + "start_line": 19 + } + ], + "callables": { + "wrapper": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "result", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 19, + "name": "func", + "scope": "local" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "func", + "scope": "local" + }, + { + "col_offset": 5, + "is_builtin": false, + "kind": "module", + "lineno": 19, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "21:17": { + "kind": "call", + "span": { + "bytes": [ + 586, + 607 + ], + "end": [ + 21, + 38 + ], + "start": [ + 21, + 17 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Starred" + ], + "arguments": [ + { + "ast_kind": "Starred" + } + ], + "end_column": 38, + "end_line": 21, + "is_constructor_call": false, + "method_name": "func", + "start_column": 17, + "start_line": 21 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 21, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "functools.wraps(func)" + ], + "end_line": 22, + "id": "can://python/decorators_and_hof/main.py/log_call(func)/wrapper(args,kwargs)", + "kind": "function", + "local_variables": [ + { + "end_column": 14, + "end_line": 21, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 8, + "start_line": 21 + } + ], + "name": "wrapper", + "parameters": [ + { + "end_column": 21, + "end_line": 20, + "name": "args", + "start_column": 17, + "start_line": 20, + "type": "tuple" + }, + { + "end_column": 31, + "end_line": 20, + "name": "kwargs", + "start_column": 25, + "start_line": 20, + "type": "dict" + } + ], + "signature": "main.log_call.wrapper", + "span": { + "bytes": [ + 539, + 629 + ], + "end": [ + 22, + 21 + ], + "start": [ + 20, + 4 + ] + }, + "start_line": 20, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 20, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 23, + "id": "can://python/decorators_and_hof/main.py/log_call(func)", + "kind": "function", + "local_variables": [ + { + "end_column": 14, + "end_line": 21, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 8, + "start_line": 21 + } + ], + "name": "log_call", + "parameters": [ + { + "end_column": 17, + "end_line": 18, + "name": "func", + "start_column": 13, + "start_line": 18 + } + ], + "signature": "main.log_call", + "span": { + "bytes": [ + 488, + 648 + ], + "end": [ + 23, + 18 + ], + "start": [ + 18, + 0 + ] + }, + "start_line": 18, + "summary": [], + "types": {} + }, + "main": { + "accessed_symbols": [ + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 127, + "name": "apply", + "qualified_name": "main.apply", + "scope": "local", + "type": "apply" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 127, + "name": "double", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 128, + "name": "apply", + "qualified_name": "main.apply", + "scope": "local", + "type": "apply" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 128, + "name": "triple", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "function", + "lineno": 130, + "name": "compose", + "qualified_name": "main.compose", + "scope": "local", + "type": "compose" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "function", + "lineno": 130, + "name": "triple", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "function", + "lineno": 130, + "name": "double", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 131, + "name": "double_then_triple", + "scope": "local", + "type": "h" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 133, + "name": "make_adder", + "qualified_name": "main.make_adder", + "scope": "local", + "type": "make_adder" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 134, + "name": "make_multiplier", + "qualified_name": "main.make_multiplier", + "scope": "local", + "type": "make_multiplier" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 135, + "name": "add5", + "scope": "local", + "type": "adder" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 136, + "name": "mul3", + "scope": "local", + "type": "multiplier" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 138, + "name": "greet", + "scope": "local", + "type": "greet" + }, + { + "col_offset": 4, + "is_builtin": false, + "kind": "function", + "lineno": 139, + "name": "say_hello", + "scope": "local", + "type": "say_hello" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "variable", + "lineno": 140, + "name": "compute", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 141, + "name": "stacked", + "scope": "local", + "type": "stacked" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r1", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r2", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r3", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r4", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r5", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r6", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 35, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r7", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r8", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "127:9": { + "callee": "can://python/decorators_and_hof/main.py/apply(func,value)", + "kind": "call", + "span": { + "bytes": [ + 3022, + 3039 + ], + "end": [ + 127, + 26 + ], + "start": [ + 127, + 9 + ] + } + }, + "128:9": { + "callee": "can://python/decorators_and_hof/main.py/apply(func,value)", + "kind": "call", + "span": { + "bytes": [ + 3049, + 3066 + ], + "end": [ + 128, + 26 + ], + "start": [ + 128, + 9 + ] + } + }, + "130:25": { + "callee": "can://python/decorators_and_hof/main.py/compose(f,g)", + "kind": "call", + "span": { + "bytes": [ + 3093, + 3116 + ], + "end": [ + 130, + 48 + ], + "start": [ + 130, + 25 + ] + } + }, + "131:9": { + "kind": "call", + "span": { + "bytes": [ + 3126, + 3147 + ], + "end": [ + 131, + 30 + ], + "start": [ + 131, + 9 + ] + } + }, + "133:11": { + "callee": "can://python/decorators_and_hof/main.py/make_adder(n)", + "kind": "call", + "span": { + "bytes": [ + 3160, + 3173 + ], + "end": [ + 133, + 24 + ], + "start": [ + 133, + 11 + ] + } + }, + "134:11": { + "callee": "can://python/decorators_and_hof/main.py/make_multiplier(n)", + "kind": "call", + "span": { + "bytes": [ + 3185, + 3203 + ], + "end": [ + 134, + 29 + ], + "start": [ + 134, + 11 + ] + } + }, + "135:9": { + "kind": "call", + "span": { + "bytes": [ + 3213, + 3221 + ], + "end": [ + 135, + 17 + ], + "start": [ + 135, + 9 + ] + } + }, + "136:9": { + "kind": "call", + "span": { + "bytes": [ + 3231, + 3239 + ], + "end": [ + 136, + 17 + ], + "start": [ + 136, + 9 + ] + } + }, + "138:9": { + "kind": "call", + "span": { + "bytes": [ + 3250, + 3264 + ], + "end": [ + 138, + 23 + ], + "start": [ + 138, + 9 + ] + } + }, + "139:4": { + "kind": "call", + "span": { + "bytes": [ + 3269, + 3280 + ], + "end": [ + 139, + 15 + ], + "start": [ + 139, + 4 + ] + } + }, + "140:9": { + "callee": "can://python/decorators_and_hof/main.py/Timer", + "kind": "call", + "span": { + "bytes": [ + 3290, + 3303 + ], + "end": [ + 140, + 22 + ], + "start": [ + 140, + 9 + ] + } + }, + "141:9": { + "kind": "call", + "span": { + "bytes": [ + 3313, + 3323 + ], + "end": [ + 141, + 19 + ], + "start": [ + 141, + 9 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "double", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "double" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.apply", + "end_column": 26, + "end_line": 127, + "is_constructor_call": false, + "method_name": "apply", + "start_column": 9, + "start_line": 127 + }, + { + "argument_types": [ + "triple", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "triple" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.apply", + "end_column": 26, + "end_line": 128, + "is_constructor_call": false, + "method_name": "apply", + "start_column": 9, + "start_line": 128 + }, + { + "argument_types": [ + "triple", + "double" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "triple" + }, + { + "ast_kind": "Name", + "inferred_type": "double" + } + ], + "callee_signature": "main.compose", + "end_column": 48, + "end_line": 130, + "is_constructor_call": false, + "method_name": "compose", + "return_type": "h", + "start_column": 25, + "start_line": 130 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 30, + "end_line": 131, + "is_constructor_call": false, + "method_name": "double_then_triple", + "return_type": "int", + "start_column": 9, + "start_line": 131 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.make_adder", + "end_column": 24, + "end_line": 133, + "is_constructor_call": false, + "method_name": "make_adder", + "return_type": "adder", + "start_column": 11, + "start_line": 133 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.make_multiplier", + "end_column": 29, + "end_line": 134, + "is_constructor_call": false, + "method_name": "make_multiplier", + "return_type": "multiplier", + "start_column": 11, + "start_line": 134 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 17, + "end_line": 135, + "is_constructor_call": false, + "method_name": "add5", + "return_type": "int", + "start_column": 9, + "start_line": 135 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 17, + "end_line": 136, + "is_constructor_call": false, + "method_name": "mul3", + "return_type": "int", + "start_column": 9, + "start_line": 136 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "end_column": 23, + "end_line": 138, + "is_constructor_call": false, + "method_name": "greet", + "return_type": "str", + "start_column": 9, + "start_line": 138 + }, + { + "argument_types": [], + "arguments": [], + "end_column": 15, + "end_line": 139, + "is_constructor_call": false, + "method_name": "say_hello", + "start_column": 4, + "start_line": 139 + }, + { + "argument_types": [ + "int", + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.Timer", + "end_column": 22, + "end_line": 140, + "is_constructor_call": false, + "method_name": "compute", + "start_column": 9, + "start_line": 140 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 19, + "end_line": 141, + "is_constructor_call": false, + "method_name": "stacked", + "return_type": "int", + "start_column": 9, + "start_line": 141 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 127, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 143, + "id": "can://python/decorators_and_hof/main.py/main()", + "kind": "function", + "local_variables": [ + { + "end_column": 6, + "end_line": 127, + "initializer": "apply(double, 10)", + "name": "r1", + "scope": "function", + "start_column": 4, + "start_line": 127, + "type": "int" + }, + { + "end_column": 6, + "end_line": 128, + "initializer": "apply(triple, 10)", + "name": "r2", + "scope": "function", + "start_column": 4, + "start_line": 128, + "type": "int" + }, + { + "end_column": 22, + "end_line": 130, + "initializer": "compose(triple, double)", + "name": "double_then_triple", + "scope": "function", + "start_column": 4, + "start_line": 130, + "type": "h" + }, + { + "end_column": 6, + "end_line": 131, + "initializer": "double_then_triple(5)", + "name": "r3", + "scope": "function", + "start_column": 4, + "start_line": 131, + "type": "int" + }, + { + "end_column": 8, + "end_line": 133, + "initializer": "make_adder(5)", + "name": "add5", + "scope": "function", + "start_column": 4, + "start_line": 133, + "type": "adder" + }, + { + "end_column": 8, + "end_line": 134, + "initializer": "make_multiplier(3)", + "name": "mul3", + "scope": "function", + "start_column": 4, + "start_line": 134, + "type": "multiplier" + }, + { + "end_column": 6, + "end_line": 135, + "initializer": "add5(10)", + "name": "r4", + "scope": "function", + "start_column": 4, + "start_line": 135, + "type": "int" + }, + { + "end_column": 6, + "end_line": 136, + "initializer": "mul3(10)", + "name": "r5", + "scope": "function", + "start_column": 4, + "start_line": 136, + "type": "int" + }, + { + "end_column": 6, + "end_line": 138, + "initializer": "greet('world')", + "name": "r6", + "scope": "function", + "start_column": 4, + "start_line": 138, + "type": "str" + }, + { + "end_column": 6, + "end_line": 140, + "initializer": "compute(2, 3)", + "name": "r7", + "scope": "function", + "start_column": 4, + "start_line": 140, + "type": "int" + }, + { + "end_column": 6, + "end_line": 141, + "initializer": "stacked(7)", + "name": "r8", + "scope": "function", + "start_column": 4, + "start_line": 141, + "type": "int" + } + ], + "name": "main", + "parameters": [], + "signature": "main.main", + "span": { + "bytes": [ + 3001, + 3366 + ], + "end": [ + 143, + 41 + ], + "start": [ + 126, + 0 + ] + }, + "start_line": 126, + "summary": [], + "types": {} + }, + "make_adder": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 88, + "name": "adder", + "scope": "local", + "type": "adder" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "class", + "lineno": 85, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": { + "adder": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 87, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 87, + "id": "can://python/decorators_and_hof/main.py/make_adder(n)/adder(x)", + "kind": "function", + "local_variables": [], + "name": "adder", + "parameters": [ + { + "end_column": 15, + "end_line": 86, + "name": "x", + "start_column": 14, + "start_line": 86 + } + ], + "signature": "main.make_adder.adder", + "span": { + "bytes": [ + 2271, + 2305 + ], + "end": [ + 87, + 20 + ], + "start": [ + 86, + 4 + ] + }, + "start_line": 86, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 86, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 88, + "id": "can://python/decorators_and_hof/main.py/make_adder(n)", + "kind": "function", + "local_variables": [], + "name": "make_adder", + "parameters": [ + { + "end_column": 21, + "end_line": 85, + "name": "n", + "start_column": 15, + "start_line": 85, + "type": "int" + } + ], + "signature": "main.make_adder", + "span": { + "bytes": [ + 2243, + 2322 + ], + "end": [ + 88, + 16 + ], + "start": [ + 85, + 0 + ] + }, + "start_line": 85, + "summary": [], + "types": {} + }, + "make_multiplier": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 94, + "name": "multiplier", + "scope": "local", + "type": "multiplier" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 91, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": { + "multiplier": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 93, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 93, + "id": "can://python/decorators_and_hof/main.py/make_multiplier(n)/multiplier(x)", + "kind": "function", + "local_variables": [], + "name": "multiplier", + "parameters": [ + { + "end_column": 20, + "end_line": 92, + "name": "x", + "start_column": 19, + "start_line": 92 + } + ], + "signature": "main.make_multiplier.multiplier", + "span": { + "bytes": [ + 2358, + 2397 + ], + "end": [ + 93, + 20 + ], + "start": [ + 92, + 4 + ] + }, + "start_line": 92, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 92, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [], + "end_line": 94, + "id": "can://python/decorators_and_hof/main.py/make_multiplier(n)", + "kind": "function", + "local_variables": [], + "name": "make_multiplier", + "parameters": [ + { + "end_column": 26, + "end_line": 91, + "name": "n", + "start_column": 20, + "start_line": 91, + "type": "int" + } + ], + "signature": "main.make_multiplier", + "span": { + "bytes": [ + 2325, + 2419 + ], + "end": [ + 94, + 21 + ], + "start": [ + 91, + 0 + ] + }, + "start_line": 91, + "summary": [], + "types": {} + }, + "repeat": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 39, + "name": "decorator", + "scope": "local", + "type": "decorator" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "class", + "lineno": 30, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 38, + "name": "wrapper", + "scope": "local", + "type": "wrapper" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 37, + "name": "result", + "scope": "local" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 32, + "name": "func", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "module", + "lineno": 32, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "func", + "scope": "local" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": {}, + "call_sites": [], + "callables": { + "decorator": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 38, + "name": "wrapper", + "scope": "local", + "type": "wrapper" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 37, + "name": "result", + "scope": "local" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 32, + "name": "func", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "module", + "lineno": 32, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "func", + "scope": "local" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "32:9": { + "callee": "can://python/decorators_and_hof/@external/functools/wraps", + "kind": "call", + "span": { + "bytes": [ + 899, + 920 + ], + "end": [ + 32, + 30 + ], + "start": [ + 32, + 9 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Name" + ], + "arguments": [ + { + "ast_kind": "Name" + } + ], + "callee_signature": "functools.wraps", + "end_column": 30, + "end_line": 32, + "is_constructor_call": false, + "method_name": "wraps", + "receiver_expr": "functools", + "receiver_type": "functools", + "start_column": 9, + "start_line": 32 + } + ], + "callables": { + "wrapper": { + "accessed_symbols": [ + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 37, + "name": "result", + "scope": "local" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 32, + "name": "func", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "module", + "lineno": 32, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "func", + "scope": "local" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "35:21": { + "callee": "can://python/decorators_and_hof/@external/builtins.range/__init__", + "kind": "call", + "span": { + "bytes": [ + 1006, + 1014 + ], + "end": [ + 35, + 29 + ], + "start": [ + 35, + 21 + ] + } + }, + "36:25": { + "kind": "call", + "span": { + "bytes": [ + 1041, + 1062 + ], + "end": [ + 36, + 46 + ], + "start": [ + 36, + 25 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "builtins.range.__init__", + "end_column": 29, + "end_line": 35, + "is_constructor_call": true, + "method_name": "range", + "return_type": "range", + "start_column": 21, + "start_line": 35 + }, + { + "argument_types": [ + "Starred" + ], + "arguments": [ + { + "ast_kind": "Starred" + } + ], + "end_column": 46, + "end_line": 36, + "is_constructor_call": false, + "method_name": "func", + "start_column": 25, + "start_line": 36 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 34, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [], + "decorators": [ + "functools.wraps(func)" + ], + "end_line": 37, + "id": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)/wrapper(args,kwargs)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 34, + "initializer": "None", + "name": "result", + "scope": "function", + "start_column": 12, + "start_line": 34, + "type": "NoneType" + }, + { + "end_column": 22, + "end_line": 36, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 16, + "start_line": 36 + } + ], + "name": "wrapper", + "parameters": [ + { + "end_column": 25, + "end_line": 33, + "name": "args", + "start_column": 21, + "start_line": 33, + "type": "tuple" + }, + { + "end_column": 35, + "end_line": 33, + "name": "kwargs", + "start_column": 29, + "start_line": 33, + "type": "dict" + } + ], + "signature": "main.repeat.decorator.wrapper", + "span": { + "bytes": [ + 929, + 1088 + ], + "end": [ + 37, + 25 + ], + "start": [ + 33, + 8 + ] + }, + "start_line": 33, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 33, + "comments": [], + "cyclomatic_complexity": 4, + "ddg": [], + "decorators": [], + "end_line": 38, + "id": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 34, + "initializer": "None", + "name": "result", + "scope": "function", + "start_column": 12, + "start_line": 34, + "type": "NoneType" + }, + { + "end_column": 22, + "end_line": 36, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 16, + "start_line": 36 + } + ], + "name": "decorator", + "parameters": [ + { + "end_column": 22, + "end_line": 31, + "name": "func", + "start_column": 18, + "start_line": 31 + } + ], + "signature": "main.repeat.decorator", + "span": { + "bytes": [ + 869, + 1111 + ], + "end": [ + 38, + 22 + ], + "start": [ + 31, + 4 + ] + }, + "start_line": 31, + "summary": [], + "types": {} + } + }, + "cdg": [], + "cfg": [], + "code_start_line": 31, + "comments": [], + "cyclomatic_complexity": 5, + "ddg": [], + "decorators": [], + "end_line": 39, + "id": "can://python/decorators_and_hof/main.py/repeat(n)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 34, + "initializer": "None", + "name": "result", + "scope": "function", + "start_column": 12, + "start_line": 34, + "type": "NoneType" + }, + { + "end_column": 22, + "end_line": 36, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 16, + "start_line": 36 + } + ], + "name": "repeat", + "parameters": [ + { + "end_column": 17, + "end_line": 30, + "name": "n", + "start_column": 11, + "start_line": 30, + "type": "int" + } + ], + "signature": "main.repeat", + "span": { + "bytes": [ + 845, + 1132 + ], + "end": [ + 39, + 20 + ], + "start": [ + 30, + 0 + ] + }, + "start_line": 30, + "summary": [], + "types": {} + }, + "say_hello": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 106, + "name": "repeat", + "qualified_name": "main.repeat", + "scope": "local", + "type": "repeat" + }, + { + "col_offset": 4, + "is_builtin": false, + "kind": "function", + "lineno": 108, + "name": "print", + "qualified_name": "builtins.print", + "scope": "local", + "type": "print" + } + ], + "body": { + "108:4": { + "callee": "can://python/decorators_and_hof/@external/builtins/print", + "kind": "call", + "span": { + "bytes": [ + 2705, + 2719 + ], + "end": [ + 108, + 18 + ], + "start": [ + 108, + 4 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.print", + "end_column": 18, + "end_line": 108, + "is_constructor_call": false, + "method_name": "print", + "start_column": 4, + "start_line": 108 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 108, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [ + "repeat(3)" + ], + "end_line": 108, + "id": "can://python/decorators_and_hof/main.py/say_hello()", + "kind": "function", + "local_variables": [], + "name": "say_hello", + "parameters": [], + "signature": "main.say_hello", + "span": { + "bytes": [ + 2684, + 2719 + ], + "end": [ + 108, + 18 + ], + "start": [ + 107, + 0 + ] + }, + "start_line": 107, + "summary": [], + "types": {} + }, + "stacked": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 116, + "name": "log_call", + "qualified_name": "main.log_call", + "scope": "local", + "type": "log_call" + }, + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 117, + "name": "repeat", + "qualified_name": "main.repeat", + "scope": "local", + "type": "repeat" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 119, + "name": "value", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 119, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [ + "log_call", + "repeat(2)" + ], + "end_line": 119, + "id": "can://python/decorators_and_hof/main.py/stacked(value)", + "kind": "function", + "local_variables": [], + "name": "stacked", + "parameters": [ + { + "end_column": 17, + "end_line": 118, + "name": "value", + "start_column": 12, + "start_line": 118 + } + ], + "signature": "main.stacked", + "span": { + "bytes": [ + 2788, + 2829 + ], + "end": [ + 119, + 21 + ], + "start": [ + 118, + 0 + ] + }, + "start_line": 118, + "summary": [], + "types": {} + }, + "triple": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 71, + "name": "x", + "scope": "local" + } + ], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 71, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 71, + "id": "can://python/decorators_and_hof/main.py/triple(x)", + "kind": "function", + "local_variables": [], + "name": "triple", + "parameters": [ + { + "end_column": 12, + "end_line": 70, + "name": "x", + "start_column": 11, + "start_line": 70 + } + ], + "signature": "main.triple", + "span": { + "bytes": [ + 1901, + 1932 + ], + "end": [ + 71, + 16 + ], + "start": [ + 70, + 0 + ] + }, + "start_line": 70, + "summary": [], + "types": {} + } + }, + "id": "can://python/decorators_and_hof/main.py", + "imports": [ + { + "end_column": 16, + "end_line": 11, + "module": "functools", + "name": "functools", + "start_column": 0, + "start_line": 11 + } + ], + "kind": "module", + "module_name": "main", + "source": "\"\"\"Decorator and higher-order function patterns.\n\nExercises:\n- Simple function wrapper (functools.wraps)\n- Parameterised decorator factory\n- Class-based decorator (__call__)\n- Higher-order function (function passed as argument)\n- Closure / function factory\n- Decorator stacking\n\"\"\"\nimport functools\n\n\n# ---------------------------------------------------------------------------\n# 1. Simple wrapper decorator\n# ---------------------------------------------------------------------------\n\ndef log_call(func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n result = func(*args, **kwargs)\n return result\n return wrapper\n\n\n# ---------------------------------------------------------------------------\n# 2. Parameterised decorator factory\n# ---------------------------------------------------------------------------\n\ndef repeat(n: int):\n def decorator(func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n result = None\n for _ in range(n):\n result = func(*args, **kwargs)\n return result\n return wrapper\n return decorator\n\n\n# ---------------------------------------------------------------------------\n# 3. Class-based decorator\n# ---------------------------------------------------------------------------\n\nclass Timer:\n def __init__(self, func):\n functools.update_wrapper(self, func)\n self.func = func\n self.call_count = 0\n\n def __call__(self, *args, **kwargs):\n self.call_count += 1\n return self.func(*args, **kwargs)\n\n\n# ---------------------------------------------------------------------------\n# 4. Higher-order functions\n# ---------------------------------------------------------------------------\n\ndef apply(func, value):\n \"\"\"Call *func* with *value* and return the result.\"\"\"\n return func(value)\n\n\ndef double(x):\n return x * 2\n\n\ndef triple(x):\n return x * 3\n\n\ndef compose(f, g):\n \"\"\"Return a new function h(x) = f(g(x)).\"\"\"\n def h(x):\n return f(g(x))\n return h\n\n\n# ---------------------------------------------------------------------------\n# 5. Closure / function factory\n# ---------------------------------------------------------------------------\n\ndef make_adder(n: int):\n def adder(x):\n return x + n\n return adder\n\n\ndef make_multiplier(n: int):\n def multiplier(x):\n return x * n\n return multiplier\n\n\n# ---------------------------------------------------------------------------\n# 6. Decorated callables\n# ---------------------------------------------------------------------------\n\n@log_call\ndef greet(name: str) -> str:\n return f\"Hello, {name}\"\n\n\n@repeat(3)\ndef say_hello():\n print(\"hello\")\n\n\n@Timer\ndef compute(x, y):\n return x + y\n\n\n@log_call\n@repeat(2)\ndef stacked(value):\n return value * 10\n\n\n# ---------------------------------------------------------------------------\n# 7. Driver\n# ---------------------------------------------------------------------------\n\ndef main():\n r1 = apply(double, 10)\n r2 = apply(triple, 10)\n\n double_then_triple = compose(triple, double)\n r3 = double_then_triple(5)\n\n add5 = make_adder(5)\n mul3 = make_multiplier(3)\n r4 = add5(10)\n r5 = mul3(10)\n\n r6 = greet(\"world\")\n say_hello()\n r7 = compute(2, 3)\n r8 = stacked(7)\n\n return r1, r2, r3, r4, r5, r6, r7, r8\n\n\nif __name__ == \"__main__\":\n main()\n", + "types": { + "main.Timer": { + "attributes": {}, + "base_classes": [], + "callables": { + "__call__": { + "accessed_symbols": [ + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 53, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 54, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 54, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "variable", + "lineno": 54, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "54:15": { + "kind": "call", + "span": { + "bytes": [ + 1546, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 54, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Starred" + ], + "arguments": [ + { + "ast_kind": "Starred" + } + ], + "end_column": 41, + "end_line": 54, + "is_constructor_call": false, + "method_name": "func", + "receiver_expr": "self", + "receiver_type": "Timer", + "start_column": 15, + "start_line": 54 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 53, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 54, + "id": "can://python/decorators_and_hof/main.py/Timer/__call__(self,args,kwargs)", + "kind": "function", + "local_variables": [], + "name": "__call__", + "parameters": [ + { + "end_column": 21, + "end_line": 52, + "name": "self", + "start_column": 17, + "start_line": 52, + "type": "Timer" + }, + { + "end_column": 28, + "end_line": 52, + "name": "args", + "start_column": 24, + "start_line": 52, + "type": "tuple" + }, + { + "end_column": 38, + "end_line": 52, + "name": "kwargs", + "start_column": 32, + "start_line": 52, + "type": "dict" + } + ], + "signature": "main.Timer.__call__", + "span": { + "bytes": [ + 1465, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 52, + 4 + ] + }, + "start_line": 52, + "summary": [], + "types": {} + }, + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 49, + "name": "func", + "scope": "local" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 48, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 48, + "name": "func", + "scope": "local" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 49, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 50, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "module", + "lineno": 48, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + } + ], + "body": { + "48:8": { + "callee": "can://python/decorators_and_hof/@external/functools/update_wrapper", + "kind": "call", + "span": { + "bytes": [ + 1370, + 1406 + ], + "end": [ + 48, + 44 + ], + "start": [ + 48, + 8 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "Timer", + "Name" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "Timer" + }, + { + "ast_kind": "Name" + } + ], + "callee_signature": "functools.update_wrapper", + "end_column": 44, + "end_line": 48, + "is_constructor_call": false, + "method_name": "update_wrapper", + "receiver_expr": "functools", + "receiver_type": "functools", + "start_column": 8, + "start_line": 48 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 48, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [], + "decorators": [], + "end_line": 50, + "id": "can://python/decorators_and_hof/main.py/Timer/__init__(self,func)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 49, + "initializer": "func", + "name": "func", + "scope": "class", + "start_column": 8, + "start_line": 49, + "type": "Timer" + }, + { + "end_column": 23, + "end_line": 50, + "initializer": "0", + "name": "call_count", + "scope": "class", + "start_column": 8, + "start_line": 50, + "type": "Timer" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 47, + "name": "self", + "start_column": 17, + "start_line": 47, + "type": "Timer" + }, + { + "end_column": 27, + "end_line": 47, + "name": "func", + "start_column": 23, + "start_line": 47 + } + ], + "signature": "main.Timer.__init__", + "span": { + "bytes": [ + 1336, + 1459 + ], + "end": [ + 50, + 27 + ], + "start": [ + 47, + 4 + ] + }, + "start_line": 47, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 54, + "id": "can://python/decorators_and_hof/main.py/Timer", + "kind": "class", + "name": "Timer", + "signature": "main.Timer", + "span": { + "bytes": [ + 1319, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 46, + 0 + ] + }, + "start_line": 46, + "types": {} + } + }, + "variables": [] + } + } + }, + "language": "python", + "max_level": 2, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/decorators_and_hof.a3.json b/test/golden/pipeline_equivalence/decorators_and_hof.a3.json new file mode 100644 index 0000000..2c73bc4 --- /dev/null +++ b/test/golden/pipeline_equivalence/decorators_and_hof.a3.json @@ -0,0 +1,5709 @@ +{ + "analyzer": { + "config": { + "analysis_level": 3 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/decorators_and_hof/@external/functools/update_wrapper", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/Timer/__init__(self,func)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/double(x)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/apply(func,value)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/double(x)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/compose(f,g)/h(x)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/triple(x)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/compose(f,g)/h(x)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/functools/wraps", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/log_call(func)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/Timer", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/apply(func,value)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 2 + }, + { + "dst": "can://python/decorators_and_hof/main.py/compose(f,g)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/make_adder(n)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/main.py/make_multiplier(n)", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/main()", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/functools/wraps", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/builtins.range/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)/wrapper(args,kwargs)", + "weight": 1 + }, + { + "dst": "can://python/decorators_and_hof/@external/builtins/print", + "prov": [ + "jedi" + ], + "src": "can://python/decorators_and_hof/main.py/say_hello()", + "weight": 1 + } + ], + "external_symbols": { + "can://python/decorators_and_hof/@external/builtins.range/__init__": { + "id": "can://python/decorators_and_hof/@external/builtins.range/__init__", + "kind": "external", + "module": "builtins.range", + "name": "__init__" + }, + "can://python/decorators_and_hof/@external/builtins/print": { + "id": "can://python/decorators_and_hof/@external/builtins/print", + "kind": "external", + "module": "builtins", + "name": "print" + }, + "can://python/decorators_and_hof/@external/functools/update_wrapper": { + "id": "can://python/decorators_and_hof/@external/functools/update_wrapper", + "kind": "external", + "module": "functools", + "name": "update_wrapper" + }, + "can://python/decorators_and_hof/@external/functools/wraps": { + "id": "can://python/decorators_and_hof/@external/functools/wraps", + "kind": "external", + "module": "functools", + "name": "wraps" + } + }, + "id": "can://python/decorators_and_hof", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [ + { + "content": "Decorator and higher-order function patterns.\n\nExercises:\n- Simple function wrapper (functools.wraps)\n- Parameterised decorator factory\n- Class-based decorator (__call__)\n- Higher-order function (function passed as argument)\n- Closure / function factory\n- Decorator stacking\n", + "end_column": 3, + "end_line": 10, + "is_docstring": true, + "start_column": 0, + "start_line": 1 + } + ], + "content_hash": "705388028325c87c86d513288b2b45d6668d0da3fd6370d90515b334c41d969a", + "file_size": 3407, + "functions": { + "apply": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 63, + "name": "func", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 16, + "is_builtin": false, + "kind": "variable", + "lineno": 63, + "name": "value", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "62:4": { + "kind": "statement", + "span": { + "bytes": [ + 1788, + 1841 + ], + "end": [ + 62, + 57 + ], + "start": [ + 62, + 4 + ] + } + }, + "63:11": { + "callee": "can://python/decorators_and_hof/main.py/double(x)", + "kind": "call", + "span": { + "bytes": [ + 1853, + 1864 + ], + "end": [ + 63, + 22 + ], + "start": [ + 63, + 11 + ] + } + }, + "63:4": { + "kind": "return", + "span": { + "bytes": [ + 1846, + 1864 + ], + "end": [ + 63, + 22 + ], + "start": [ + 63, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "main.double", + "end_column": 22, + "end_line": 63, + "is_constructor_call": false, + "method_name": "func", + "return_type": "int", + "start_column": 11, + "start_line": 63 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "62:4", + "src": "@entry" + }, + { + "dst": "63:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "62:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "63:4", + "kind": "fallthrough", + "src": "62:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "63:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "63:4" + } + ], + "code_start_line": 62, + "comments": [ + { + "content": "Call *func* with *value* and return the result.", + "end_column": 57, + "end_line": 62, + "is_docstring": true, + "start_column": 4, + "start_line": 62 + } + ], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "63:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "func" + }, + { + "dst": "63:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "value" + } + ], + "decorators": [], + "end_line": 63, + "id": "can://python/decorators_and_hof/main.py/apply(func,value)", + "kind": "function", + "local_variables": [], + "name": "apply", + "parameters": [ + { + "end_column": 14, + "end_line": 61, + "name": "func", + "start_column": 10, + "start_line": 61, + "type": "double" + }, + { + "end_column": 21, + "end_line": 61, + "name": "value", + "start_column": 16, + "start_line": 61, + "type": "int" + } + ], + "signature": "main.apply", + "span": { + "bytes": [ + 1760, + 1864 + ], + "end": [ + 63, + 22 + ], + "start": [ + 61, + 0 + ] + }, + "start_line": 61, + "summary": [], + "types": {} + }, + "compose": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 78, + "name": "h", + "scope": "local", + "type": "h" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "f", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "g", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 77, + "name": "x", + "scope": "local" + } + ], + "body": { + "75:4": { + "kind": "statement", + "span": { + "bytes": [ + 1958, + 2001 + ], + "end": [ + 75, + 47 + ], + "start": [ + 75, + 4 + ] + } + }, + "76:4": { + "kind": "statement", + "span": { + "bytes": [ + 2006, + 2038 + ], + "end": [ + 77, + 22 + ], + "start": [ + 76, + 4 + ] + } + }, + "78:4": { + "kind": "return", + "span": { + "bytes": [ + 2043, + 2051 + ], + "end": [ + 78, + 12 + ], + "start": [ + 78, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": { + "h": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "f", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "function", + "lineno": 77, + "name": "g", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 77, + "name": "x", + "scope": "local" + } + ], + "body": { + "77:15": { + "callee": "can://python/decorators_and_hof/main.py/triple(x)", + "kind": "call", + "span": { + "bytes": [ + 2031, + 2038 + ], + "end": [ + 77, + 22 + ], + "start": [ + 77, + 15 + ] + } + }, + "77:17": { + "callee": "can://python/decorators_and_hof/main.py/double(x)", + "kind": "call", + "span": { + "bytes": [ + 2033, + 2037 + ], + "end": [ + 77, + 21 + ], + "start": [ + 77, + 17 + ] + } + }, + "77:8": { + "kind": "return", + "span": { + "bytes": [ + 2024, + 2038 + ], + "end": [ + 77, + 22 + ], + "start": [ + 77, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "double" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "double" + } + ], + "callee_signature": "main.triple", + "end_column": 22, + "end_line": 77, + "is_constructor_call": false, + "method_name": "f", + "return_type": "int", + "start_column": 15, + "start_line": 77 + }, + { + "argument_types": [ + "Name" + ], + "arguments": [ + { + "ast_kind": "Name" + } + ], + "callee_signature": "main.double", + "end_column": 21, + "end_line": 77, + "is_constructor_call": false, + "method_name": "g", + "return_type": "int", + "start_column": 17, + "start_line": 77 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "77:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "77:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "77:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "77:8" + } + ], + "code_start_line": 77, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "77:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "f" + }, + { + "dst": "77:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "g" + }, + { + "dst": "77:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "x" + } + ], + "decorators": [], + "end_line": 77, + "id": "can://python/decorators_and_hof/main.py/compose(f,g)/h(x)", + "kind": "function", + "local_variables": [], + "name": "h", + "parameters": [ + { + "end_column": 11, + "end_line": 76, + "name": "x", + "start_column": 10, + "start_line": 76 + } + ], + "signature": "main.compose.h", + "span": { + "bytes": [ + 2006, + 2038 + ], + "end": [ + 77, + 22 + ], + "start": [ + 76, + 4 + ] + }, + "start_line": 76, + "summary": [], + "types": {} + } + }, + "cdg": [ + { + "dst": "75:4", + "src": "@entry" + }, + { + "dst": "76:4", + "src": "@entry" + }, + { + "dst": "78:4", + "src": "76:4" + } + ], + "cfg": [ + { + "dst": "75:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "76:4", + "kind": "fallthrough", + "src": "75:4" + }, + { + "dst": "78:4", + "kind": "fallthrough", + "src": "76:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "76:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "78:4" + } + ], + "code_start_line": 75, + "comments": [ + { + "content": "Return a new function h(x) = f(g(x)).", + "end_column": 47, + "end_line": 75, + "is_docstring": true, + "start_column": 4, + "start_line": 75 + } + ], + "cyclomatic_complexity": 3, + "ddg": [ + { + "dst": "76:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "f" + }, + { + "dst": "76:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "g" + }, + { + "dst": "78:4", + "prov": [ + "ssa" + ], + "src": "76:4", + "var": "h" + } + ], + "decorators": [], + "end_line": 78, + "id": "can://python/decorators_and_hof/main.py/compose(f,g)", + "kind": "function", + "local_variables": [], + "name": "compose", + "parameters": [ + { + "end_column": 13, + "end_line": 74, + "name": "f", + "start_column": 12, + "start_line": 74, + "type": "triple" + }, + { + "end_column": 16, + "end_line": 74, + "name": "g", + "start_column": 15, + "start_line": 74, + "type": "double" + } + ], + "signature": "main.compose", + "span": { + "bytes": [ + 1935, + 2051 + ], + "end": [ + 78, + 12 + ], + "start": [ + 74, + 0 + ] + }, + "start_line": 74, + "summary": [], + "types": {} + }, + "compute": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "class", + "lineno": 111, + "name": "Timer", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 113, + "name": "x", + "scope": "local" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 113, + "name": "y", + "scope": "local" + } + ], + "body": { + "113:4": { + "kind": "return", + "span": { + "bytes": [ + 2752, + 2764 + ], + "end": [ + 113, + 16 + ], + "start": [ + 113, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "113:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "113:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "113:4" + } + ], + "code_start_line": 113, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "113:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "x" + }, + { + "dst": "113:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "y" + } + ], + "decorators": [ + "Timer" + ], + "end_line": 113, + "id": "can://python/decorators_and_hof/main.py/compute(x,y)", + "kind": "function", + "local_variables": [], + "name": "compute", + "parameters": [ + { + "end_column": 13, + "end_line": 112, + "name": "x", + "start_column": 12, + "start_line": 112 + }, + { + "end_column": 16, + "end_line": 112, + "name": "y", + "start_column": 15, + "start_line": 112 + } + ], + "signature": "main.compute", + "span": { + "bytes": [ + 2729, + 2764 + ], + "end": [ + 113, + 16 + ], + "start": [ + 112, + 0 + ] + }, + "start_line": 112, + "summary": [], + "types": {} + }, + "double": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 67, + "name": "x", + "scope": "local" + } + ], + "body": { + "67:4": { + "kind": "return", + "span": { + "bytes": [ + 1886, + 1898 + ], + "end": [ + 67, + 16 + ], + "start": [ + 67, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "67:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "67:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "67:4" + } + ], + "code_start_line": 67, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "67:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "x" + } + ], + "decorators": [], + "end_line": 67, + "id": "can://python/decorators_and_hof/main.py/double(x)", + "kind": "function", + "local_variables": [], + "name": "double", + "parameters": [ + { + "end_column": 12, + "end_line": 66, + "name": "x", + "start_column": 11, + "start_line": 66 + } + ], + "signature": "main.double", + "span": { + "bytes": [ + 1867, + 1898 + ], + "end": [ + 67, + 16 + ], + "start": [ + 66, + 0 + ] + }, + "start_line": 66, + "summary": [], + "types": {} + }, + "greet": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 101, + "name": "log_call", + "qualified_name": "main.log_call", + "scope": "local", + "type": "log_call" + }, + { + "col_offset": 24, + "is_builtin": false, + "kind": "class", + "lineno": 102, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 16, + "is_builtin": false, + "kind": "class", + "lineno": 102, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 103, + "name": "name", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + } + ], + "body": { + "103:4": { + "kind": "return", + "span": { + "bytes": [ + 2647, + 2670 + ], + "end": [ + 103, + 27 + ], + "start": [ + 103, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "103:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "103:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "103:4" + } + ], + "code_start_line": 103, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "103:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "name" + } + ], + "decorators": [ + "log_call" + ], + "end_line": 103, + "id": "can://python/decorators_and_hof/main.py/greet(name)", + "kind": "function", + "local_variables": [], + "name": "greet", + "parameters": [ + { + "end_column": 19, + "end_line": 102, + "name": "name", + "start_column": 10, + "start_line": 102, + "type": "str" + } + ], + "return_type": "str", + "signature": "main.greet", + "span": { + "bytes": [ + 2614, + 2670 + ], + "end": [ + 103, + 27 + ], + "start": [ + 102, + 0 + ] + }, + "start_line": 102, + "summary": [], + "types": {} + }, + "log_call": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 23, + "name": "wrapper", + "scope": "local", + "type": "wrapper" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "result", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 19, + "name": "func", + "scope": "local" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "func", + "scope": "local" + }, + { + "col_offset": 5, + "is_builtin": false, + "kind": "module", + "lineno": 19, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "19:5": { + "callee": "can://python/decorators_and_hof/@external/functools/wraps", + "kind": "call", + "span": { + "bytes": [ + 513, + 534 + ], + "end": [ + 19, + 26 + ], + "start": [ + 19, + 5 + ] + } + }, + "20:4": { + "kind": "statement", + "span": { + "bytes": [ + 539, + 629 + ], + "end": [ + 22, + 21 + ], + "start": [ + 20, + 4 + ] + } + }, + "23:4": { + "kind": "return", + "span": { + "bytes": [ + 634, + 648 + ], + "end": [ + 23, + 18 + ], + "start": [ + 23, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "Name" + ], + "arguments": [ + { + "ast_kind": "Name" + } + ], + "callee_signature": "functools.wraps", + "end_column": 26, + "end_line": 19, + "is_constructor_call": false, + "method_name": "wraps", + "receiver_expr": "functools", + "receiver_type": "functools", + "start_column": 5, + "start_line": 19 + } + ], + "callables": { + "wrapper": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "result", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "variable", + "lineno": 19, + "name": "func", + "scope": "local" + }, + { + "col_offset": 17, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "func", + "scope": "local" + }, + { + "col_offset": 5, + "is_builtin": false, + "kind": "module", + "lineno": 19, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "21:17": { + "kind": "call", + "span": { + "bytes": [ + 586, + 607 + ], + "end": [ + 21, + 38 + ], + "start": [ + 21, + 17 + ] + } + }, + "21:8": { + "kind": "statement", + "span": { + "bytes": [ + 577, + 607 + ], + "end": [ + 21, + 38 + ], + "start": [ + 21, + 8 + ] + } + }, + "22:8": { + "kind": "return", + "span": { + "bytes": [ + 616, + 629 + ], + "end": [ + 22, + 21 + ], + "start": [ + 22, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "Starred" + ], + "arguments": [ + { + "ast_kind": "Starred" + } + ], + "end_column": 38, + "end_line": 21, + "is_constructor_call": false, + "method_name": "func", + "start_column": 17, + "start_line": 21 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "21:8", + "src": "@entry" + }, + { + "dst": "22:8", + "src": "21:8" + } + ], + "cfg": [ + { + "dst": "21:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "22:8", + "kind": "fallthrough", + "src": "21:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "21:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "22:8" + } + ], + "code_start_line": 21, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "21:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "args" + }, + { + "dst": "21:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "func" + }, + { + "dst": "21:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "kwargs" + }, + { + "dst": "22:8", + "prov": [ + "ssa" + ], + "src": "21:8", + "var": "result" + } + ], + "decorators": [ + "functools.wraps(func)" + ], + "end_line": 22, + "id": "can://python/decorators_and_hof/main.py/log_call(func)/wrapper(args,kwargs)", + "kind": "function", + "local_variables": [ + { + "end_column": 14, + "end_line": 21, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 8, + "start_line": 21 + } + ], + "name": "wrapper", + "parameters": [ + { + "end_column": 21, + "end_line": 20, + "name": "args", + "start_column": 17, + "start_line": 20, + "type": "tuple" + }, + { + "end_column": 31, + "end_line": 20, + "name": "kwargs", + "start_column": 25, + "start_line": 20, + "type": "dict" + } + ], + "signature": "main.log_call.wrapper", + "span": { + "bytes": [ + 539, + 629 + ], + "end": [ + 22, + 21 + ], + "start": [ + 20, + 4 + ] + }, + "start_line": 20, + "summary": [], + "types": {} + } + }, + "cdg": [ + { + "dst": "20:4", + "src": "@entry" + }, + { + "dst": "23:4", + "src": "20:4" + } + ], + "cfg": [ + { + "dst": "20:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "23:4", + "kind": "fallthrough", + "src": "20:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "20:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "23:4" + } + ], + "code_start_line": 20, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [ + { + "dst": "20:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "func" + }, + { + "dst": "20:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::functools.wraps" + }, + { + "dst": "23:4", + "prov": [ + "ssa" + ], + "src": "20:4", + "var": "wrapper" + } + ], + "decorators": [], + "end_line": 23, + "id": "can://python/decorators_and_hof/main.py/log_call(func)", + "kind": "function", + "local_variables": [ + { + "end_column": 14, + "end_line": 21, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 8, + "start_line": 21 + } + ], + "name": "log_call", + "parameters": [ + { + "end_column": 17, + "end_line": 18, + "name": "func", + "start_column": 13, + "start_line": 18 + } + ], + "signature": "main.log_call", + "span": { + "bytes": [ + 488, + 648 + ], + "end": [ + 23, + 18 + ], + "start": [ + 18, + 0 + ] + }, + "start_line": 18, + "summary": [], + "types": {} + }, + "main": { + "accessed_symbols": [ + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 127, + "name": "apply", + "qualified_name": "main.apply", + "scope": "local", + "type": "apply" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 127, + "name": "double", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 128, + "name": "apply", + "qualified_name": "main.apply", + "scope": "local", + "type": "apply" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 128, + "name": "triple", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "function", + "lineno": 130, + "name": "compose", + "qualified_name": "main.compose", + "scope": "local", + "type": "compose" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "function", + "lineno": 130, + "name": "triple", + "qualified_name": "main.triple", + "scope": "local", + "type": "triple" + }, + { + "col_offset": 41, + "is_builtin": false, + "kind": "function", + "lineno": 130, + "name": "double", + "qualified_name": "main.double", + "scope": "local", + "type": "double" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 131, + "name": "double_then_triple", + "scope": "local", + "type": "h" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 133, + "name": "make_adder", + "qualified_name": "main.make_adder", + "scope": "local", + "type": "make_adder" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 134, + "name": "make_multiplier", + "qualified_name": "main.make_multiplier", + "scope": "local", + "type": "make_multiplier" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 135, + "name": "add5", + "scope": "local", + "type": "adder" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 136, + "name": "mul3", + "scope": "local", + "type": "multiplier" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 138, + "name": "greet", + "scope": "local", + "type": "greet" + }, + { + "col_offset": 4, + "is_builtin": false, + "kind": "function", + "lineno": 139, + "name": "say_hello", + "scope": "local", + "type": "say_hello" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "variable", + "lineno": 140, + "name": "compute", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "function", + "lineno": 141, + "name": "stacked", + "scope": "local", + "type": "stacked" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r1", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r2", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r3", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r4", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r5", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r6", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 35, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r7", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 143, + "name": "r8", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "127:4": { + "kind": "statement", + "span": { + "bytes": [ + 3017, + 3039 + ], + "end": [ + 127, + 26 + ], + "start": [ + 127, + 4 + ] + } + }, + "127:9": { + "callee": "can://python/decorators_and_hof/main.py/apply(func,value)", + "kind": "call", + "span": { + "bytes": [ + 3022, + 3039 + ], + "end": [ + 127, + 26 + ], + "start": [ + 127, + 9 + ] + } + }, + "128:4": { + "kind": "statement", + "span": { + "bytes": [ + 3044, + 3066 + ], + "end": [ + 128, + 26 + ], + "start": [ + 128, + 4 + ] + } + }, + "128:9": { + "callee": "can://python/decorators_and_hof/main.py/apply(func,value)", + "kind": "call", + "span": { + "bytes": [ + 3049, + 3066 + ], + "end": [ + 128, + 26 + ], + "start": [ + 128, + 9 + ] + } + }, + "130:25": { + "callee": "can://python/decorators_and_hof/main.py/compose(f,g)", + "kind": "call", + "span": { + "bytes": [ + 3093, + 3116 + ], + "end": [ + 130, + 48 + ], + "start": [ + 130, + 25 + ] + } + }, + "130:4": { + "kind": "statement", + "span": { + "bytes": [ + 3072, + 3116 + ], + "end": [ + 130, + 48 + ], + "start": [ + 130, + 4 + ] + } + }, + "131:4": { + "kind": "statement", + "span": { + "bytes": [ + 3121, + 3147 + ], + "end": [ + 131, + 30 + ], + "start": [ + 131, + 4 + ] + } + }, + "131:9": { + "kind": "call", + "span": { + "bytes": [ + 3126, + 3147 + ], + "end": [ + 131, + 30 + ], + "start": [ + 131, + 9 + ] + } + }, + "133:11": { + "callee": "can://python/decorators_and_hof/main.py/make_adder(n)", + "kind": "call", + "span": { + "bytes": [ + 3160, + 3173 + ], + "end": [ + 133, + 24 + ], + "start": [ + 133, + 11 + ] + } + }, + "133:4": { + "kind": "statement", + "span": { + "bytes": [ + 3153, + 3173 + ], + "end": [ + 133, + 24 + ], + "start": [ + 133, + 4 + ] + } + }, + "134:11": { + "callee": "can://python/decorators_and_hof/main.py/make_multiplier(n)", + "kind": "call", + "span": { + "bytes": [ + 3185, + 3203 + ], + "end": [ + 134, + 29 + ], + "start": [ + 134, + 11 + ] + } + }, + "134:4": { + "kind": "statement", + "span": { + "bytes": [ + 3178, + 3203 + ], + "end": [ + 134, + 29 + ], + "start": [ + 134, + 4 + ] + } + }, + "135:4": { + "kind": "statement", + "span": { + "bytes": [ + 3208, + 3221 + ], + "end": [ + 135, + 17 + ], + "start": [ + 135, + 4 + ] + } + }, + "135:9": { + "kind": "call", + "span": { + "bytes": [ + 3213, + 3221 + ], + "end": [ + 135, + 17 + ], + "start": [ + 135, + 9 + ] + } + }, + "136:4": { + "kind": "statement", + "span": { + "bytes": [ + 3226, + 3239 + ], + "end": [ + 136, + 17 + ], + "start": [ + 136, + 4 + ] + } + }, + "136:9": { + "kind": "call", + "span": { + "bytes": [ + 3231, + 3239 + ], + "end": [ + 136, + 17 + ], + "start": [ + 136, + 9 + ] + } + }, + "138:4": { + "kind": "statement", + "span": { + "bytes": [ + 3245, + 3264 + ], + "end": [ + 138, + 23 + ], + "start": [ + 138, + 4 + ] + } + }, + "138:9": { + "kind": "call", + "span": { + "bytes": [ + 3250, + 3264 + ], + "end": [ + 138, + 23 + ], + "start": [ + 138, + 9 + ] + } + }, + "139:4": { + "kind": "call", + "span": { + "bytes": [ + 3269, + 3280 + ], + "end": [ + 139, + 15 + ], + "start": [ + 139, + 4 + ] + } + }, + "140:4": { + "kind": "statement", + "span": { + "bytes": [ + 3285, + 3303 + ], + "end": [ + 140, + 22 + ], + "start": [ + 140, + 4 + ] + } + }, + "140:9": { + "callee": "can://python/decorators_and_hof/main.py/Timer", + "kind": "call", + "span": { + "bytes": [ + 3290, + 3303 + ], + "end": [ + 140, + 22 + ], + "start": [ + 140, + 9 + ] + } + }, + "141:4": { + "kind": "statement", + "span": { + "bytes": [ + 3308, + 3323 + ], + "end": [ + 141, + 19 + ], + "start": [ + 141, + 4 + ] + } + }, + "141:9": { + "kind": "call", + "span": { + "bytes": [ + 3313, + 3323 + ], + "end": [ + 141, + 19 + ], + "start": [ + 141, + 9 + ] + } + }, + "143:4": { + "kind": "return", + "span": { + "bytes": [ + 3329, + 3366 + ], + "end": [ + 143, + 41 + ], + "start": [ + 143, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "double", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "double" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.apply", + "end_column": 26, + "end_line": 127, + "is_constructor_call": false, + "method_name": "apply", + "start_column": 9, + "start_line": 127 + }, + { + "argument_types": [ + "triple", + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "triple" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.apply", + "end_column": 26, + "end_line": 128, + "is_constructor_call": false, + "method_name": "apply", + "start_column": 9, + "start_line": 128 + }, + { + "argument_types": [ + "triple", + "double" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "triple" + }, + { + "ast_kind": "Name", + "inferred_type": "double" + } + ], + "callee_signature": "main.compose", + "end_column": 48, + "end_line": 130, + "is_constructor_call": false, + "method_name": "compose", + "return_type": "h", + "start_column": 25, + "start_line": 130 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 30, + "end_line": 131, + "is_constructor_call": false, + "method_name": "double_then_triple", + "return_type": "int", + "start_column": 9, + "start_line": 131 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.make_adder", + "end_column": 24, + "end_line": 133, + "is_constructor_call": false, + "method_name": "make_adder", + "return_type": "adder", + "start_column": 11, + "start_line": 133 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.make_multiplier", + "end_column": 29, + "end_line": 134, + "is_constructor_call": false, + "method_name": "make_multiplier", + "return_type": "multiplier", + "start_column": 11, + "start_line": 134 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 17, + "end_line": 135, + "is_constructor_call": false, + "method_name": "add5", + "return_type": "int", + "start_column": 9, + "start_line": 135 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 17, + "end_line": 136, + "is_constructor_call": false, + "method_name": "mul3", + "return_type": "int", + "start_column": 9, + "start_line": 136 + }, + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "end_column": 23, + "end_line": 138, + "is_constructor_call": false, + "method_name": "greet", + "return_type": "str", + "start_column": 9, + "start_line": 138 + }, + { + "argument_types": [], + "arguments": [], + "end_column": 15, + "end_line": 139, + "is_constructor_call": false, + "method_name": "say_hello", + "start_column": 4, + "start_line": 139 + }, + { + "argument_types": [ + "int", + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + }, + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "callee_signature": "main.Timer", + "end_column": 22, + "end_line": 140, + "is_constructor_call": false, + "method_name": "compute", + "start_column": 9, + "start_line": 140 + }, + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "int" + } + ], + "end_column": 19, + "end_line": 141, + "is_constructor_call": false, + "method_name": "stacked", + "return_type": "int", + "start_column": 9, + "start_line": 141 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "127:4", + "src": "@entry" + }, + { + "dst": "128:4", + "src": "127:4" + }, + { + "dst": "130:4", + "src": "128:4" + }, + { + "dst": "131:4", + "src": "130:4" + }, + { + "dst": "133:4", + "src": "131:4" + }, + { + "dst": "134:4", + "src": "133:4" + }, + { + "dst": "135:4", + "src": "134:4" + }, + { + "dst": "136:4", + "src": "135:4" + }, + { + "dst": "138:4", + "src": "136:4" + }, + { + "dst": "139:4", + "src": "138:4" + }, + { + "dst": "140:4", + "src": "139:4" + }, + { + "dst": "141:4", + "src": "140:4" + }, + { + "dst": "143:4", + "src": "141:4" + } + ], + "cfg": [ + { + "dst": "127:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "128:4", + "kind": "fallthrough", + "src": "127:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "127:4" + }, + { + "dst": "130:4", + "kind": "fallthrough", + "src": "128:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "128:4" + }, + { + "dst": "131:4", + "kind": "fallthrough", + "src": "130:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "130:4" + }, + { + "dst": "133:4", + "kind": "fallthrough", + "src": "131:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "131:4" + }, + { + "dst": "134:4", + "kind": "fallthrough", + "src": "133:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "133:4" + }, + { + "dst": "135:4", + "kind": "fallthrough", + "src": "134:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "134:4" + }, + { + "dst": "136:4", + "kind": "fallthrough", + "src": "135:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "135:4" + }, + { + "dst": "138:4", + "kind": "fallthrough", + "src": "136:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "136:4" + }, + { + "dst": "139:4", + "kind": "fallthrough", + "src": "138:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "138:4" + }, + { + "dst": "140:4", + "kind": "fallthrough", + "src": "139:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "139:4" + }, + { + "dst": "141:4", + "kind": "fallthrough", + "src": "140:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "140:4" + }, + { + "dst": "143:4", + "kind": "fallthrough", + "src": "141:4" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "141:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "143:4" + } + ], + "code_start_line": 127, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "127:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::apply" + }, + { + "dst": "127:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::double" + }, + { + "dst": "128:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::apply" + }, + { + "dst": "128:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::triple" + }, + { + "dst": "130:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::compose" + }, + { + "dst": "130:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::double" + }, + { + "dst": "130:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::triple" + }, + { + "dst": "133:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::make_adder" + }, + { + "dst": "134:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::make_multiplier" + }, + { + "dst": "138:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::greet" + }, + { + "dst": "139:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::say_hello" + }, + { + "dst": "140:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::compute" + }, + { + "dst": "141:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::stacked" + }, + { + "dst": "130:4", + "prov": [ + "ssa" + ], + "src": "127:4", + "var": "main::double" + }, + { + "dst": "143:4", + "prov": [ + "ssa" + ], + "src": "127:4", + "var": "r1" + }, + { + "dst": "130:4", + "prov": [ + "ssa" + ], + "src": "128:4", + "var": "main::triple" + }, + { + "dst": "143:4", + "prov": [ + "ssa" + ], + "src": "128:4", + "var": "r2" + }, + { + "dst": "131:4", + "prov": [ + "ssa" + ], + "src": "130:4", + "var": "double_then_triple" + }, + { + "dst": "143:4", + "prov": [ + "ssa" + ], + "src": "131:4", + "var": "r3" + }, + { + "dst": "135:4", + "prov": [ + "ssa" + ], + "src": "133:4", + "var": "add5" + }, + { + "dst": "136:4", + "prov": [ + "ssa" + ], + "src": "134:4", + "var": "mul3" + }, + { + "dst": "143:4", + "prov": [ + "ssa" + ], + "src": "135:4", + "var": "r4" + }, + { + "dst": "143:4", + "prov": [ + "ssa" + ], + "src": "136:4", + "var": "r5" + }, + { + "dst": "143:4", + "prov": [ + "ssa" + ], + "src": "138:4", + "var": "r6" + }, + { + "dst": "143:4", + "prov": [ + "ssa" + ], + "src": "140:4", + "var": "r7" + }, + { + "dst": "143:4", + "prov": [ + "ssa" + ], + "src": "141:4", + "var": "r8" + } + ], + "decorators": [], + "end_line": 143, + "id": "can://python/decorators_and_hof/main.py/main()", + "kind": "function", + "local_variables": [ + { + "end_column": 6, + "end_line": 127, + "initializer": "apply(double, 10)", + "name": "r1", + "scope": "function", + "start_column": 4, + "start_line": 127, + "type": "int" + }, + { + "end_column": 6, + "end_line": 128, + "initializer": "apply(triple, 10)", + "name": "r2", + "scope": "function", + "start_column": 4, + "start_line": 128, + "type": "int" + }, + { + "end_column": 22, + "end_line": 130, + "initializer": "compose(triple, double)", + "name": "double_then_triple", + "scope": "function", + "start_column": 4, + "start_line": 130, + "type": "h" + }, + { + "end_column": 6, + "end_line": 131, + "initializer": "double_then_triple(5)", + "name": "r3", + "scope": "function", + "start_column": 4, + "start_line": 131, + "type": "int" + }, + { + "end_column": 8, + "end_line": 133, + "initializer": "make_adder(5)", + "name": "add5", + "scope": "function", + "start_column": 4, + "start_line": 133, + "type": "adder" + }, + { + "end_column": 8, + "end_line": 134, + "initializer": "make_multiplier(3)", + "name": "mul3", + "scope": "function", + "start_column": 4, + "start_line": 134, + "type": "multiplier" + }, + { + "end_column": 6, + "end_line": 135, + "initializer": "add5(10)", + "name": "r4", + "scope": "function", + "start_column": 4, + "start_line": 135, + "type": "int" + }, + { + "end_column": 6, + "end_line": 136, + "initializer": "mul3(10)", + "name": "r5", + "scope": "function", + "start_column": 4, + "start_line": 136, + "type": "int" + }, + { + "end_column": 6, + "end_line": 138, + "initializer": "greet('world')", + "name": "r6", + "scope": "function", + "start_column": 4, + "start_line": 138, + "type": "str" + }, + { + "end_column": 6, + "end_line": 140, + "initializer": "compute(2, 3)", + "name": "r7", + "scope": "function", + "start_column": 4, + "start_line": 140, + "type": "int" + }, + { + "end_column": 6, + "end_line": 141, + "initializer": "stacked(7)", + "name": "r8", + "scope": "function", + "start_column": 4, + "start_line": 141, + "type": "int" + } + ], + "name": "main", + "parameters": [], + "signature": "main.main", + "span": { + "bytes": [ + 3001, + 3366 + ], + "end": [ + 143, + 41 + ], + "start": [ + 126, + 0 + ] + }, + "start_line": 126, + "summary": [], + "types": {} + }, + "make_adder": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 88, + "name": "adder", + "scope": "local", + "type": "adder" + }, + { + "col_offset": 18, + "is_builtin": false, + "kind": "class", + "lineno": 85, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "86:4": { + "kind": "statement", + "span": { + "bytes": [ + 2271, + 2305 + ], + "end": [ + 87, + 20 + ], + "start": [ + 86, + 4 + ] + } + }, + "88:4": { + "kind": "return", + "span": { + "bytes": [ + 2310, + 2322 + ], + "end": [ + 88, + 16 + ], + "start": [ + 88, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": { + "adder": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 87, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "87:8": { + "kind": "return", + "span": { + "bytes": [ + 2293, + 2305 + ], + "end": [ + 87, + 20 + ], + "start": [ + 87, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "87:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "87:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "87:8" + } + ], + "code_start_line": 87, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "87:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "n" + }, + { + "dst": "87:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "x" + } + ], + "decorators": [], + "end_line": 87, + "id": "can://python/decorators_and_hof/main.py/make_adder(n)/adder(x)", + "kind": "function", + "local_variables": [], + "name": "adder", + "parameters": [ + { + "end_column": 15, + "end_line": 86, + "name": "x", + "start_column": 14, + "start_line": 86 + } + ], + "signature": "main.make_adder.adder", + "span": { + "bytes": [ + 2271, + 2305 + ], + "end": [ + 87, + 20 + ], + "start": [ + 86, + 4 + ] + }, + "start_line": 86, + "summary": [], + "types": {} + } + }, + "cdg": [ + { + "dst": "86:4", + "src": "@entry" + }, + { + "dst": "88:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "86:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "88:4", + "kind": "fallthrough", + "src": "86:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "88:4" + } + ], + "code_start_line": 86, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [ + { + "dst": "86:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "n" + }, + { + "dst": "88:4", + "prov": [ + "ssa" + ], + "src": "86:4", + "var": "adder" + } + ], + "decorators": [], + "end_line": 88, + "id": "can://python/decorators_and_hof/main.py/make_adder(n)", + "kind": "function", + "local_variables": [], + "name": "make_adder", + "parameters": [ + { + "end_column": 21, + "end_line": 85, + "name": "n", + "start_column": 15, + "start_line": 85, + "type": "int" + } + ], + "signature": "main.make_adder", + "span": { + "bytes": [ + 2243, + 2322 + ], + "end": [ + 88, + 16 + ], + "start": [ + 85, + 0 + ] + }, + "start_line": 85, + "summary": [], + "types": {} + }, + "make_multiplier": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 94, + "name": "multiplier", + "scope": "local", + "type": "multiplier" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "class", + "lineno": 91, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "92:4": { + "kind": "statement", + "span": { + "bytes": [ + 2358, + 2397 + ], + "end": [ + 93, + 20 + ], + "start": [ + 92, + 4 + ] + } + }, + "94:4": { + "kind": "return", + "span": { + "bytes": [ + 2402, + 2419 + ], + "end": [ + 94, + 21 + ], + "start": [ + 94, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": { + "multiplier": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "x", + "scope": "local" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 93, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + } + ], + "body": { + "93:8": { + "kind": "return", + "span": { + "bytes": [ + 2385, + 2397 + ], + "end": [ + 93, + 20 + ], + "start": [ + 93, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "93:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "93:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "93:8" + } + ], + "code_start_line": 93, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "93:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "n" + }, + { + "dst": "93:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "x" + } + ], + "decorators": [], + "end_line": 93, + "id": "can://python/decorators_and_hof/main.py/make_multiplier(n)/multiplier(x)", + "kind": "function", + "local_variables": [], + "name": "multiplier", + "parameters": [ + { + "end_column": 20, + "end_line": 92, + "name": "x", + "start_column": 19, + "start_line": 92 + } + ], + "signature": "main.make_multiplier.multiplier", + "span": { + "bytes": [ + 2358, + 2397 + ], + "end": [ + 93, + 20 + ], + "start": [ + 92, + 4 + ] + }, + "start_line": 92, + "summary": [], + "types": {} + } + }, + "cdg": [ + { + "dst": "92:4", + "src": "@entry" + }, + { + "dst": "94:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "92:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "94:4", + "kind": "fallthrough", + "src": "92:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "94:4" + } + ], + "code_start_line": 92, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [ + { + "dst": "92:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "n" + }, + { + "dst": "94:4", + "prov": [ + "ssa" + ], + "src": "92:4", + "var": "multiplier" + } + ], + "decorators": [], + "end_line": 94, + "id": "can://python/decorators_and_hof/main.py/make_multiplier(n)", + "kind": "function", + "local_variables": [], + "name": "make_multiplier", + "parameters": [ + { + "end_column": 26, + "end_line": 91, + "name": "n", + "start_column": 20, + "start_line": 91, + "type": "int" + } + ], + "signature": "main.make_multiplier", + "span": { + "bytes": [ + 2325, + 2419 + ], + "end": [ + 94, + 21 + ], + "start": [ + 91, + 0 + ] + }, + "start_line": 91, + "summary": [], + "types": {} + }, + "repeat": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "function", + "lineno": 39, + "name": "decorator", + "scope": "local", + "type": "decorator" + }, + { + "col_offset": 14, + "is_builtin": false, + "kind": "class", + "lineno": 30, + "name": "int", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 38, + "name": "wrapper", + "scope": "local", + "type": "wrapper" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 37, + "name": "result", + "scope": "local" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 32, + "name": "func", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "module", + "lineno": 32, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "func", + "scope": "local" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "31:4": { + "kind": "statement", + "span": { + "bytes": [ + 869, + 1111 + ], + "end": [ + 38, + 22 + ], + "start": [ + 31, + 4 + ] + } + }, + "39:4": { + "kind": "return", + "span": { + "bytes": [ + 1116, + 1132 + ], + "end": [ + 39, + 20 + ], + "start": [ + 39, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": { + "decorator": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "function", + "lineno": 38, + "name": "wrapper", + "scope": "local", + "type": "wrapper" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 37, + "name": "result", + "scope": "local" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 32, + "name": "func", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "module", + "lineno": 32, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "func", + "scope": "local" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "32:9": { + "callee": "can://python/decorators_and_hof/@external/functools/wraps", + "kind": "call", + "span": { + "bytes": [ + 899, + 920 + ], + "end": [ + 32, + 30 + ], + "start": [ + 32, + 9 + ] + } + }, + "33:8": { + "kind": "statement", + "span": { + "bytes": [ + 929, + 1088 + ], + "end": [ + 37, + 25 + ], + "start": [ + 33, + 8 + ] + } + }, + "38:8": { + "kind": "return", + "span": { + "bytes": [ + 1097, + 1111 + ], + "end": [ + 38, + 22 + ], + "start": [ + 38, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "Name" + ], + "arguments": [ + { + "ast_kind": "Name" + } + ], + "callee_signature": "functools.wraps", + "end_column": 30, + "end_line": 32, + "is_constructor_call": false, + "method_name": "wraps", + "receiver_expr": "functools", + "receiver_type": "functools", + "start_column": 9, + "start_line": 32 + } + ], + "callables": { + "wrapper": { + "accessed_symbols": [ + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 37, + "name": "result", + "scope": "local" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 32, + "name": "func", + "scope": "local" + }, + { + "col_offset": 21, + "is_builtin": false, + "kind": "class", + "lineno": 35, + "name": "range", + "qualified_name": "builtins.range", + "scope": "local", + "type": "range" + }, + { + "col_offset": 27, + "is_builtin": false, + "kind": "variable", + "lineno": 35, + "name": "n", + "qualified_name": "builtins.int", + "scope": "local", + "type": "int" + }, + { + "col_offset": 9, + "is_builtin": false, + "kind": "module", + "lineno": 32, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + }, + { + "col_offset": 25, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "func", + "scope": "local" + }, + { + "col_offset": 31, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 36, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "34:12": { + "kind": "statement", + "span": { + "bytes": [ + 971, + 984 + ], + "end": [ + 34, + 25 + ], + "start": [ + 34, + 12 + ] + } + }, + "35:21": { + "callee": "can://python/decorators_and_hof/@external/builtins.range/__init__", + "kind": "call", + "span": { + "bytes": [ + 1006, + 1014 + ], + "end": [ + 35, + 29 + ], + "start": [ + 35, + 21 + ] + } + }, + "36:16": { + "kind": "statement", + "span": { + "bytes": [ + 1032, + 1062 + ], + "end": [ + 36, + 46 + ], + "start": [ + 36, + 16 + ] + } + }, + "36:25": { + "kind": "call", + "span": { + "bytes": [ + 1041, + 1062 + ], + "end": [ + 36, + 46 + ], + "start": [ + 36, + 25 + ] + } + }, + "37:12": { + "kind": "return", + "span": { + "bytes": [ + 1075, + 1088 + ], + "end": [ + 37, + 25 + ], + "start": [ + 37, + 12 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "int" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "int" + } + ], + "callee_signature": "builtins.range.__init__", + "end_column": 29, + "end_line": 35, + "is_constructor_call": true, + "method_name": "range", + "return_type": "range", + "start_column": 21, + "start_line": 35 + }, + { + "argument_types": [ + "Starred" + ], + "arguments": [ + { + "ast_kind": "Starred" + } + ], + "end_column": 46, + "end_line": 36, + "is_constructor_call": false, + "method_name": "func", + "start_column": 25, + "start_line": 36 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "34:12", + "src": "@entry" + }, + { + "dst": "36:16", + "src": "35:21" + }, + { + "dst": "37:12", + "src": "35:21" + }, + { + "dst": "35:21", + "src": "36:16" + } + ], + "cfg": [ + { + "dst": "34:12", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "35:21", + "kind": "fallthrough", + "src": "34:12" + }, + { + "dst": "36:16", + "kind": "true", + "src": "35:21" + }, + { + "dst": "37:12", + "kind": "false", + "src": "35:21" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "35:21" + }, + { + "dst": "35:21", + "kind": "loop_back", + "src": "36:16" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "36:16" + }, + { + "dst": "@exit", + "kind": "return", + "src": "37:12" + } + ], + "code_start_line": 34, + "comments": [], + "cyclomatic_complexity": 3, + "ddg": [ + { + "dst": "35:21", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "n" + }, + { + "dst": "35:21", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "range" + }, + { + "dst": "36:16", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "args" + }, + { + "dst": "36:16", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "func" + }, + { + "dst": "36:16", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "kwargs" + }, + { + "dst": "37:12", + "prov": [ + "ssa" + ], + "src": "34:12", + "var": "result" + }, + { + "dst": "35:21", + "prov": [ + "ssa" + ], + "src": "35:21", + "var": "n" + }, + { + "dst": "36:16", + "prov": [ + "ssa" + ], + "src": "36:16", + "var": "kwargs" + }, + { + "dst": "37:12", + "prov": [ + "ssa" + ], + "src": "36:16", + "var": "result" + } + ], + "decorators": [ + "functools.wraps(func)" + ], + "end_line": 37, + "id": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)/wrapper(args,kwargs)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 34, + "initializer": "None", + "name": "result", + "scope": "function", + "start_column": 12, + "start_line": 34, + "type": "NoneType" + }, + { + "end_column": 22, + "end_line": 36, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 16, + "start_line": 36 + } + ], + "name": "wrapper", + "parameters": [ + { + "end_column": 25, + "end_line": 33, + "name": "args", + "start_column": 21, + "start_line": 33, + "type": "tuple" + }, + { + "end_column": 35, + "end_line": 33, + "name": "kwargs", + "start_column": 29, + "start_line": 33, + "type": "dict" + } + ], + "signature": "main.repeat.decorator.wrapper", + "span": { + "bytes": [ + 929, + 1088 + ], + "end": [ + 37, + 25 + ], + "start": [ + 33, + 8 + ] + }, + "start_line": 33, + "summary": [], + "types": {} + } + }, + "cdg": [ + { + "dst": "33:8", + "src": "@entry" + }, + { + "dst": "38:8", + "src": "33:8" + } + ], + "cfg": [ + { + "dst": "33:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "38:8", + "kind": "fallthrough", + "src": "33:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "33:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "38:8" + } + ], + "code_start_line": 33, + "comments": [], + "cyclomatic_complexity": 4, + "ddg": [ + { + "dst": "33:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "func" + }, + { + "dst": "33:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::functools.wraps" + }, + { + "dst": "33:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "n" + }, + { + "dst": "38:8", + "prov": [ + "ssa" + ], + "src": "33:8", + "var": "wrapper" + } + ], + "decorators": [], + "end_line": 38, + "id": "can://python/decorators_and_hof/main.py/repeat(n)/decorator(func)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 34, + "initializer": "None", + "name": "result", + "scope": "function", + "start_column": 12, + "start_line": 34, + "type": "NoneType" + }, + { + "end_column": 22, + "end_line": 36, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 16, + "start_line": 36 + } + ], + "name": "decorator", + "parameters": [ + { + "end_column": 22, + "end_line": 31, + "name": "func", + "start_column": 18, + "start_line": 31 + } + ], + "signature": "main.repeat.decorator", + "span": { + "bytes": [ + 869, + 1111 + ], + "end": [ + 38, + 22 + ], + "start": [ + 31, + 4 + ] + }, + "start_line": 31, + "summary": [], + "types": {} + } + }, + "cdg": [ + { + "dst": "31:4", + "src": "@entry" + }, + { + "dst": "39:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "31:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "39:4", + "kind": "fallthrough", + "src": "31:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "39:4" + } + ], + "code_start_line": 31, + "comments": [], + "cyclomatic_complexity": 5, + "ddg": [ + { + "dst": "31:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "n" + }, + { + "dst": "39:4", + "prov": [ + "ssa" + ], + "src": "31:4", + "var": "decorator" + } + ], + "decorators": [], + "end_line": 39, + "id": "can://python/decorators_and_hof/main.py/repeat(n)", + "kind": "function", + "local_variables": [ + { + "end_column": 18, + "end_line": 34, + "initializer": "None", + "name": "result", + "scope": "function", + "start_column": 12, + "start_line": 34, + "type": "NoneType" + }, + { + "end_column": 22, + "end_line": 36, + "initializer": "func(*args, **kwargs)", + "name": "result", + "scope": "function", + "start_column": 16, + "start_line": 36 + } + ], + "name": "repeat", + "parameters": [ + { + "end_column": 17, + "end_line": 30, + "name": "n", + "start_column": 11, + "start_line": 30, + "type": "int" + } + ], + "signature": "main.repeat", + "span": { + "bytes": [ + 845, + 1132 + ], + "end": [ + 39, + 20 + ], + "start": [ + 30, + 0 + ] + }, + "start_line": 30, + "summary": [], + "types": {} + }, + "say_hello": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 106, + "name": "repeat", + "qualified_name": "main.repeat", + "scope": "local", + "type": "repeat" + }, + { + "col_offset": 4, + "is_builtin": false, + "kind": "function", + "lineno": 108, + "name": "print", + "qualified_name": "builtins.print", + "scope": "local", + "type": "print" + } + ], + "body": { + "108:4": { + "callee": "can://python/decorators_and_hof/@external/builtins/print", + "kind": "call", + "span": { + "bytes": [ + 2705, + 2719 + ], + "end": [ + 108, + 18 + ], + "start": [ + 108, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "str" + ], + "arguments": [ + { + "ast_kind": "Constant", + "inferred_type": "str" + } + ], + "callee_signature": "builtins.print", + "end_column": 18, + "end_line": 108, + "is_constructor_call": false, + "method_name": "print", + "start_column": 4, + "start_line": 108 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "108:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "108:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "108:4" + }, + { + "dst": "@exit", + "kind": "return", + "src": "108:4" + } + ], + "code_start_line": 108, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "108:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "print" + } + ], + "decorators": [ + "repeat(3)" + ], + "end_line": 108, + "id": "can://python/decorators_and_hof/main.py/say_hello()", + "kind": "function", + "local_variables": [], + "name": "say_hello", + "parameters": [], + "signature": "main.say_hello", + "span": { + "bytes": [ + 2684, + 2719 + ], + "end": [ + 108, + 18 + ], + "start": [ + 107, + 0 + ] + }, + "start_line": 107, + "summary": [], + "types": {} + }, + "stacked": { + "accessed_symbols": [ + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 116, + "name": "log_call", + "qualified_name": "main.log_call", + "scope": "local", + "type": "log_call" + }, + { + "col_offset": 1, + "is_builtin": false, + "kind": "function", + "lineno": 117, + "name": "repeat", + "qualified_name": "main.repeat", + "scope": "local", + "type": "repeat" + }, + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 119, + "name": "value", + "scope": "local" + } + ], + "body": { + "119:4": { + "kind": "return", + "span": { + "bytes": [ + 2812, + 2829 + ], + "end": [ + 119, + 21 + ], + "start": [ + 119, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "119:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "119:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "119:4" + } + ], + "code_start_line": 119, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "119:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "value" + } + ], + "decorators": [ + "log_call", + "repeat(2)" + ], + "end_line": 119, + "id": "can://python/decorators_and_hof/main.py/stacked(value)", + "kind": "function", + "local_variables": [], + "name": "stacked", + "parameters": [ + { + "end_column": 17, + "end_line": 118, + "name": "value", + "start_column": 12, + "start_line": 118 + } + ], + "signature": "main.stacked", + "span": { + "bytes": [ + 2788, + 2829 + ], + "end": [ + 119, + 21 + ], + "start": [ + 118, + 0 + ] + }, + "start_line": 118, + "summary": [], + "types": {} + }, + "triple": { + "accessed_symbols": [ + { + "col_offset": 11, + "is_builtin": false, + "kind": "variable", + "lineno": 71, + "name": "x", + "scope": "local" + } + ], + "body": { + "71:4": { + "kind": "return", + "span": { + "bytes": [ + 1920, + 1932 + ], + "end": [ + 71, + 16 + ], + "start": [ + 71, + 4 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "71:4", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "71:4", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "71:4" + } + ], + "code_start_line": 71, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "71:4", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "x" + } + ], + "decorators": [], + "end_line": 71, + "id": "can://python/decorators_and_hof/main.py/triple(x)", + "kind": "function", + "local_variables": [], + "name": "triple", + "parameters": [ + { + "end_column": 12, + "end_line": 70, + "name": "x", + "start_column": 11, + "start_line": 70 + } + ], + "signature": "main.triple", + "span": { + "bytes": [ + 1901, + 1932 + ], + "end": [ + 71, + 16 + ], + "start": [ + 70, + 0 + ] + }, + "start_line": 70, + "summary": [], + "types": {} + } + }, + "id": "can://python/decorators_and_hof/main.py", + "imports": [ + { + "end_column": 16, + "end_line": 11, + "module": "functools", + "name": "functools", + "start_column": 0, + "start_line": 11 + } + ], + "kind": "module", + "module_name": "main", + "source": "\"\"\"Decorator and higher-order function patterns.\n\nExercises:\n- Simple function wrapper (functools.wraps)\n- Parameterised decorator factory\n- Class-based decorator (__call__)\n- Higher-order function (function passed as argument)\n- Closure / function factory\n- Decorator stacking\n\"\"\"\nimport functools\n\n\n# ---------------------------------------------------------------------------\n# 1. Simple wrapper decorator\n# ---------------------------------------------------------------------------\n\ndef log_call(func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n result = func(*args, **kwargs)\n return result\n return wrapper\n\n\n# ---------------------------------------------------------------------------\n# 2. Parameterised decorator factory\n# ---------------------------------------------------------------------------\n\ndef repeat(n: int):\n def decorator(func):\n @functools.wraps(func)\n def wrapper(*args, **kwargs):\n result = None\n for _ in range(n):\n result = func(*args, **kwargs)\n return result\n return wrapper\n return decorator\n\n\n# ---------------------------------------------------------------------------\n# 3. Class-based decorator\n# ---------------------------------------------------------------------------\n\nclass Timer:\n def __init__(self, func):\n functools.update_wrapper(self, func)\n self.func = func\n self.call_count = 0\n\n def __call__(self, *args, **kwargs):\n self.call_count += 1\n return self.func(*args, **kwargs)\n\n\n# ---------------------------------------------------------------------------\n# 4. Higher-order functions\n# ---------------------------------------------------------------------------\n\ndef apply(func, value):\n \"\"\"Call *func* with *value* and return the result.\"\"\"\n return func(value)\n\n\ndef double(x):\n return x * 2\n\n\ndef triple(x):\n return x * 3\n\n\ndef compose(f, g):\n \"\"\"Return a new function h(x) = f(g(x)).\"\"\"\n def h(x):\n return f(g(x))\n return h\n\n\n# ---------------------------------------------------------------------------\n# 5. Closure / function factory\n# ---------------------------------------------------------------------------\n\ndef make_adder(n: int):\n def adder(x):\n return x + n\n return adder\n\n\ndef make_multiplier(n: int):\n def multiplier(x):\n return x * n\n return multiplier\n\n\n# ---------------------------------------------------------------------------\n# 6. Decorated callables\n# ---------------------------------------------------------------------------\n\n@log_call\ndef greet(name: str) -> str:\n return f\"Hello, {name}\"\n\n\n@repeat(3)\ndef say_hello():\n print(\"hello\")\n\n\n@Timer\ndef compute(x, y):\n return x + y\n\n\n@log_call\n@repeat(2)\ndef stacked(value):\n return value * 10\n\n\n# ---------------------------------------------------------------------------\n# 7. Driver\n# ---------------------------------------------------------------------------\n\ndef main():\n r1 = apply(double, 10)\n r2 = apply(triple, 10)\n\n double_then_triple = compose(triple, double)\n r3 = double_then_triple(5)\n\n add5 = make_adder(5)\n mul3 = make_multiplier(3)\n r4 = add5(10)\n r5 = mul3(10)\n\n r6 = greet(\"world\")\n say_hello()\n r7 = compute(2, 3)\n r8 = stacked(7)\n\n return r1, r2, r3, r4, r5, r6, r7, r8\n\n\nif __name__ == \"__main__\":\n main()\n", + "types": { + "main.Timer": { + "attributes": {}, + "base_classes": [], + "callables": { + "__call__": { + "accessed_symbols": [ + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 53, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 15, + "is_builtin": false, + "kind": "variable", + "lineno": 54, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 26, + "is_builtin": false, + "kind": "variable", + "lineno": 54, + "name": "args", + "qualified_name": "builtins.tuple", + "scope": "local", + "type": "tuple" + }, + { + "col_offset": 34, + "is_builtin": false, + "kind": "variable", + "lineno": 54, + "name": "kwargs", + "qualified_name": "builtins.dict", + "scope": "local", + "type": "dict" + } + ], + "body": { + "53:8": { + "kind": "statement", + "span": { + "bytes": [ + 1510, + 1530 + ], + "end": [ + 53, + 28 + ], + "start": [ + 53, + 8 + ] + } + }, + "54:15": { + "kind": "call", + "span": { + "bytes": [ + 1546, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 54, + 15 + ] + } + }, + "54:8": { + "kind": "return", + "span": { + "bytes": [ + 1539, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 54, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "Starred" + ], + "arguments": [ + { + "ast_kind": "Starred" + } + ], + "end_column": 41, + "end_line": 54, + "is_constructor_call": false, + "method_name": "func", + "receiver_expr": "self", + "receiver_type": "Timer", + "start_column": 15, + "start_line": 54 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "53:8", + "src": "@entry" + }, + { + "dst": "54:8", + "src": "53:8" + } + ], + "cfg": [ + { + "dst": "53:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "54:8", + "kind": "fallthrough", + "src": "53:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "53:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "54:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "54:8" + } + ], + "code_start_line": 53, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "53:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "53:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.call_count" + }, + { + "dst": "54:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "args" + }, + { + "dst": "54:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "kwargs" + }, + { + "dst": "54:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "54:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.func" + }, + { + "dst": "54:8", + "prov": [ + "ssa" + ], + "src": "53:8", + "var": "self" + } + ], + "decorators": [], + "end_line": 54, + "id": "can://python/decorators_and_hof/main.py/Timer/__call__(self,args,kwargs)", + "kind": "function", + "local_variables": [], + "name": "__call__", + "parameters": [ + { + "end_column": 21, + "end_line": 52, + "name": "self", + "start_column": 17, + "start_line": 52, + "type": "Timer" + }, + { + "end_column": 28, + "end_line": 52, + "name": "args", + "start_column": 24, + "start_line": 52, + "type": "tuple" + }, + { + "end_column": 38, + "end_line": 52, + "name": "kwargs", + "start_column": 32, + "start_line": 52, + "type": "dict" + } + ], + "signature": "main.Timer.__call__", + "span": { + "bytes": [ + 1465, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 52, + 4 + ] + }, + "start_line": 52, + "summary": [], + "types": {} + }, + "__init__": { + "accessed_symbols": [ + { + "col_offset": 20, + "is_builtin": false, + "kind": "variable", + "lineno": 49, + "name": "func", + "scope": "local" + }, + { + "col_offset": 33, + "is_builtin": false, + "kind": "variable", + "lineno": 48, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 39, + "is_builtin": false, + "kind": "variable", + "lineno": 48, + "name": "func", + "scope": "local" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 49, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 50, + "name": "self", + "qualified_name": "main.Timer", + "scope": "local", + "type": "Timer" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "module", + "lineno": 48, + "name": "functools", + "qualified_name": "functools", + "scope": "global", + "type": "functools" + } + ], + "body": { + "48:8": { + "callee": "can://python/decorators_and_hof/@external/functools/update_wrapper", + "kind": "call", + "span": { + "bytes": [ + 1370, + 1406 + ], + "end": [ + 48, + 44 + ], + "start": [ + 48, + 8 + ] + } + }, + "49:8": { + "kind": "statement", + "span": { + "bytes": [ + 1415, + 1431 + ], + "end": [ + 49, + 24 + ], + "start": [ + 49, + 8 + ] + } + }, + "50:8": { + "kind": "statement", + "span": { + "bytes": [ + 1440, + 1459 + ], + "end": [ + 50, + 27 + ], + "start": [ + 50, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "Timer", + "Name" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "Timer" + }, + { + "ast_kind": "Name" + } + ], + "callee_signature": "functools.update_wrapper", + "end_column": 44, + "end_line": 48, + "is_constructor_call": false, + "method_name": "update_wrapper", + "receiver_expr": "functools", + "receiver_type": "functools", + "start_column": 8, + "start_line": 48 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "48:8", + "src": "@entry" + }, + { + "dst": "49:8", + "src": "48:8" + }, + { + "dst": "50:8", + "src": "49:8" + } + ], + "cfg": [ + { + "dst": "48:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "49:8", + "kind": "fallthrough", + "src": "48:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "48:8" + }, + { + "dst": "50:8", + "kind": "fallthrough", + "src": "49:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "49:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "50:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "50:8" + } + ], + "code_start_line": 48, + "comments": [], + "cyclomatic_complexity": 1, + "ddg": [ + { + "dst": "48:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "func" + }, + { + "dst": "48:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::functools" + }, + { + "dst": "48:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::functools.update_wrapper" + }, + { + "dst": "48:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "49:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "func" + }, + { + "dst": "49:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "50:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "49:8", + "prov": [ + "ssa" + ], + "src": "48:8", + "var": "func" + }, + { + "dst": "49:8", + "prov": [ + "ssa" + ], + "src": "48:8", + "var": "self" + }, + { + "dst": "50:8", + "prov": [ + "ssa" + ], + "src": "48:8", + "var": "self" + }, + { + "dst": "50:8", + "prov": [ + "ssa" + ], + "src": "49:8", + "var": "self" + } + ], + "decorators": [], + "end_line": 50, + "id": "can://python/decorators_and_hof/main.py/Timer/__init__(self,func)", + "kind": "function", + "local_variables": [ + { + "end_column": 17, + "end_line": 49, + "initializer": "func", + "name": "func", + "scope": "class", + "start_column": 8, + "start_line": 49, + "type": "Timer" + }, + { + "end_column": 23, + "end_line": 50, + "initializer": "0", + "name": "call_count", + "scope": "class", + "start_column": 8, + "start_line": 50, + "type": "Timer" + } + ], + "name": "__init__", + "parameters": [ + { + "end_column": 21, + "end_line": 47, + "name": "self", + "start_column": 17, + "start_line": 47, + "type": "Timer" + }, + { + "end_column": 27, + "end_line": 47, + "name": "func", + "start_column": 23, + "start_line": 47 + } + ], + "signature": "main.Timer.__init__", + "span": { + "bytes": [ + 1336, + 1459 + ], + "end": [ + 50, + 27 + ], + "start": [ + 47, + 4 + ] + }, + "start_line": 47, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 54, + "id": "can://python/decorators_and_hof/main.py/Timer", + "kind": "class", + "name": "Timer", + "signature": "main.Timer", + "span": { + "bytes": [ + 1319, + 1572 + ], + "end": [ + 54, + 41 + ], + "start": [ + 46, + 0 + ] + }, + "start_line": 46, + "types": {} + } + }, + "variables": [] + } + } + }, + "k_limit": 3, + "language": "python", + "max_level": 3, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/method_call_resolution.a1.json b/test/golden/pipeline_equivalence/method_call_resolution.a1.json new file mode 100644 index 0000000..ad1818a --- /dev/null +++ b/test/golden/pipeline_equivalence/method_call_resolution.a1.json @@ -0,0 +1,653 @@ +{ + "analyzer": { + "config": { + "analysis_level": 1 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/method_call_resolution/@external/builtins/len", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/@external/builtins.str/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/main.py/Model/helper(self)", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/main.py/Model/search(self,domain)", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/@external/main.Model/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/Environment/__getitem__(self,name)", + "weight": 1 + } + ], + "external_symbols": { + "can://python/method_call_resolution/@external/builtins.str/__init__": { + "id": "can://python/method_call_resolution/@external/builtins.str/__init__", + "kind": "external", + "module": "builtins.str", + "name": "__init__" + }, + "can://python/method_call_resolution/@external/builtins/len": { + "id": "can://python/method_call_resolution/@external/builtins/len", + "kind": "external", + "module": "builtins", + "name": "len" + }, + "can://python/method_call_resolution/@external/main.Model/__init__": { + "id": "can://python/method_call_resolution/@external/main.Model/__init__", + "kind": "external", + "module": "main.Model", + "name": "__init__" + } + }, + "id": "can://python/method_call_resolution", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [], + "content_hash": "286d25d0f8854ad5fc4d879dd9e9f7b3747e38081c3b971c7362ea0ea11c1901", + "file_size": 432, + "functions": {}, + "id": "can://python/method_call_resolution/main.py", + "imports": [], + "kind": "module", + "module_name": "main", + "source": "class Environment:\n def __getitem__(self, name):\n return Model()\n\n\nclass Model:\n env = Environment()\n\n def search(self, domain):\n return []\n\n def helper(self):\n return 42\n\n\nclass AccountMove(Model):\n _name = 'account.move'\n _inherit = ['mail.thread']\n\n def action_post(self):\n accounts = self.env['account.account'].search([])\n self.helper()\n return str(len(accounts))\n", + "types": { + "main.AccountMove": { + "attributes": { + "_inherit": { + "comments": [], + "end_line": 18, + "initializer": "['mail.thread']", + "name": "_inherit", + "start_line": 18, + "type": "list" + }, + "_name": { + "comments": [], + "end_line": 17, + "initializer": "'account.move'", + "name": "_name", + "start_line": 17, + "type": "str" + } + }, + "base_classes": [ + "Model" + ], + "callables": { + "action_post": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 23, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "self", + "qualified_name": "main.AccountMove", + "scope": "local", + "type": "AccountMove" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "function", + "lineno": 23, + "name": "len", + "qualified_name": "builtins.len", + "scope": "local", + "type": "len" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 23, + "name": "accounts", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "self", + "qualified_name": "main.AccountMove", + "scope": "local", + "type": "AccountMove" + } + ], + "body": { + "21:19": { + "kind": "call", + "span": { + "bytes": [ + 337, + 375 + ], + "end": [ + 21, + 57 + ], + "start": [ + 21, + 19 + ] + } + }, + "22:8": { + "kind": "call", + "span": { + "bytes": [ + 384, + 397 + ], + "end": [ + 22, + 21 + ], + "start": [ + 22, + 8 + ] + } + }, + "23:15": { + "kind": "call", + "span": { + "bytes": [ + 413, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 23, + 15 + ] + } + }, + "23:19": { + "kind": "call", + "span": { + "bytes": [ + 417, + 430 + ], + "end": [ + 23, + 32 + ], + "start": [ + 23, + 19 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "List", + "inferred_type": "list" + } + ], + "callee_signature": "main.Model.search", + "end_column": 57, + "end_line": 21, + "is_constructor_call": false, + "method_name": "search", + "receiver_expr": "self.env['account.account']", + "receiver_type": "AccountMove", + "return_type": "list", + "start_column": 19, + "start_line": 21 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Model.helper", + "end_column": 21, + "end_line": 22, + "is_constructor_call": false, + "method_name": "helper", + "receiver_expr": "self", + "receiver_type": "AccountMove", + "return_type": "int", + "start_column": 8, + "start_line": 22 + }, + { + "argument_types": [ + "len" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "len" + } + ], + "callee_signature": "builtins.str.__init__", + "end_column": 33, + "end_line": 23, + "is_constructor_call": true, + "method_name": "str", + "return_type": "str", + "start_column": 15, + "start_line": 23 + }, + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "list" + } + ], + "callee_signature": "builtins.len", + "end_column": 32, + "end_line": 23, + "is_constructor_call": false, + "method_name": "len", + "return_type": "int", + "start_column": 19, + "start_line": 23 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 21, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 23, + "id": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 16, + "end_line": 21, + "initializer": "self.env['account.account'].search([])", + "name": "accounts", + "scope": "function", + "start_column": 8, + "start_line": 21, + "type": "list" + } + ], + "name": "action_post", + "parameters": [ + { + "end_column": 24, + "end_line": 20, + "name": "self", + "start_column": 20, + "start_line": 20, + "type": "AccountMove" + } + ], + "signature": "main.AccountMove.action_post", + "span": { + "bytes": [ + 295, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 20, + 4 + ] + }, + "start_line": 20, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 23, + "id": "can://python/method_call_resolution/main.py/AccountMove", + "kind": "class", + "name": "AccountMove", + "signature": "main.AccountMove", + "span": { + "bytes": [ + 206, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 16, + 0 + ] + }, + "start_line": 16, + "types": {} + }, + "main.Environment": { + "attributes": {}, + "base_classes": [], + "callables": { + "__getitem__": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 3, + "name": "Model", + "qualified_name": "main.Model", + "scope": "local", + "type": "Model" + } + ], + "body": { + "3:15": { + "kind": "call", + "span": { + "bytes": [ + 67, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 3, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Model.__init__", + "end_column": 22, + "end_line": 3, + "is_constructor_call": true, + "method_name": "Model", + "return_type": "Model", + "start_column": 15, + "start_line": 3 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 3, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 3, + "id": "can://python/method_call_resolution/main.py/Environment/__getitem__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__getitem__", + "parameters": [ + { + "end_column": 24, + "end_line": 2, + "name": "self", + "start_column": 20, + "start_line": 2, + "type": "Environment" + }, + { + "end_column": 30, + "end_line": 2, + "name": "name", + "start_column": 26, + "start_line": 2 + } + ], + "signature": "main.Environment.__getitem__", + "span": { + "bytes": [ + 23, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 2, + 4 + ] + }, + "start_line": 2, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 3, + "id": "can://python/method_call_resolution/main.py/Environment", + "kind": "class", + "name": "Environment", + "signature": "main.Environment", + "span": { + "bytes": [ + 0, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 1, + 0 + ] + }, + "start_line": 1, + "types": {} + }, + "main.Model": { + "attributes": { + "env": { + "comments": [], + "end_line": 7, + "initializer": "Environment()", + "name": "env", + "start_line": 7, + "type": "Environment" + } + }, + "base_classes": [], + "callables": { + "helper": { + "accessed_symbols": [], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 13, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 13, + "id": "can://python/method_call_resolution/main.py/Model/helper(self)", + "kind": "function", + "local_variables": [], + "name": "helper", + "parameters": [ + { + "end_column": 19, + "end_line": 12, + "name": "self", + "start_column": 15, + "start_line": 12, + "type": "Model" + } + ], + "signature": "main.Model.helper", + "span": { + "bytes": [ + 168, + 203 + ], + "end": [ + 13, + 17 + ], + "start": [ + 12, + 4 + ] + }, + "start_line": 12, + "summary": [], + "types": {} + }, + "search": { + "accessed_symbols": [], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 10, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 10, + "id": "can://python/method_call_resolution/main.py/Model/search(self,domain)", + "kind": "function", + "local_variables": [], + "name": "search", + "parameters": [ + { + "end_column": 19, + "end_line": 9, + "name": "self", + "start_column": 15, + "start_line": 9, + "type": "Model" + }, + { + "end_column": 27, + "end_line": 9, + "name": "domain", + "start_column": 21, + "start_line": 9, + "type": "list" + } + ], + "signature": "main.Model.search", + "span": { + "bytes": [ + 119, + 162 + ], + "end": [ + 10, + 17 + ], + "start": [ + 9, + 4 + ] + }, + "start_line": 9, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 13, + "id": "can://python/method_call_resolution/main.py/Model", + "kind": "class", + "name": "Model", + "signature": "main.Model", + "span": { + "bytes": [ + 77, + 203 + ], + "end": [ + 13, + 17 + ], + "start": [ + 6, + 0 + ] + }, + "start_line": 6, + "types": {} + } + }, + "variables": [] + } + } + }, + "language": "python", + "max_level": 1, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/method_call_resolution.a2.json b/test/golden/pipeline_equivalence/method_call_resolution.a2.json new file mode 100644 index 0000000..5592b75 --- /dev/null +++ b/test/golden/pipeline_equivalence/method_call_resolution.a2.json @@ -0,0 +1,658 @@ +{ + "analyzer": { + "config": { + "analysis_level": 2 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/method_call_resolution/@external/builtins/len", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/@external/builtins.str/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/main.py/Model/helper(self)", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/main.py/Model/search(self,domain)", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/@external/main.Model/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/Environment/__getitem__(self,name)", + "weight": 1 + } + ], + "external_symbols": { + "can://python/method_call_resolution/@external/builtins.str/__init__": { + "id": "can://python/method_call_resolution/@external/builtins.str/__init__", + "kind": "external", + "module": "builtins.str", + "name": "__init__" + }, + "can://python/method_call_resolution/@external/builtins/len": { + "id": "can://python/method_call_resolution/@external/builtins/len", + "kind": "external", + "module": "builtins", + "name": "len" + }, + "can://python/method_call_resolution/@external/main.Model/__init__": { + "id": "can://python/method_call_resolution/@external/main.Model/__init__", + "kind": "external", + "module": "main.Model", + "name": "__init__" + } + }, + "id": "can://python/method_call_resolution", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [], + "content_hash": "286d25d0f8854ad5fc4d879dd9e9f7b3747e38081c3b971c7362ea0ea11c1901", + "file_size": 432, + "functions": {}, + "id": "can://python/method_call_resolution/main.py", + "imports": [], + "kind": "module", + "module_name": "main", + "source": "class Environment:\n def __getitem__(self, name):\n return Model()\n\n\nclass Model:\n env = Environment()\n\n def search(self, domain):\n return []\n\n def helper(self):\n return 42\n\n\nclass AccountMove(Model):\n _name = 'account.move'\n _inherit = ['mail.thread']\n\n def action_post(self):\n accounts = self.env['account.account'].search([])\n self.helper()\n return str(len(accounts))\n", + "types": { + "main.AccountMove": { + "attributes": { + "_inherit": { + "comments": [], + "end_line": 18, + "initializer": "['mail.thread']", + "name": "_inherit", + "start_line": 18, + "type": "list" + }, + "_name": { + "comments": [], + "end_line": 17, + "initializer": "'account.move'", + "name": "_name", + "start_line": 17, + "type": "str" + } + }, + "base_classes": [ + "Model" + ], + "callables": { + "action_post": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 23, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "self", + "qualified_name": "main.AccountMove", + "scope": "local", + "type": "AccountMove" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "function", + "lineno": 23, + "name": "len", + "qualified_name": "builtins.len", + "scope": "local", + "type": "len" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 23, + "name": "accounts", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "self", + "qualified_name": "main.AccountMove", + "scope": "local", + "type": "AccountMove" + } + ], + "body": { + "21:19": { + "callee": "can://python/method_call_resolution/main.py/Model/search(self,domain)", + "kind": "call", + "span": { + "bytes": [ + 337, + 375 + ], + "end": [ + 21, + 57 + ], + "start": [ + 21, + 19 + ] + } + }, + "22:8": { + "callee": "can://python/method_call_resolution/main.py/Model/helper(self)", + "kind": "call", + "span": { + "bytes": [ + 384, + 397 + ], + "end": [ + 22, + 21 + ], + "start": [ + 22, + 8 + ] + } + }, + "23:15": { + "callee": "can://python/method_call_resolution/@external/builtins.str/__init__", + "kind": "call", + "span": { + "bytes": [ + 413, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 23, + 15 + ] + } + }, + "23:19": { + "callee": "can://python/method_call_resolution/@external/builtins/len", + "kind": "call", + "span": { + "bytes": [ + 417, + 430 + ], + "end": [ + 23, + 32 + ], + "start": [ + 23, + 19 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "List", + "inferred_type": "list" + } + ], + "callee_signature": "main.Model.search", + "end_column": 57, + "end_line": 21, + "is_constructor_call": false, + "method_name": "search", + "receiver_expr": "self.env['account.account']", + "receiver_type": "AccountMove", + "return_type": "list", + "start_column": 19, + "start_line": 21 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Model.helper", + "end_column": 21, + "end_line": 22, + "is_constructor_call": false, + "method_name": "helper", + "receiver_expr": "self", + "receiver_type": "AccountMove", + "return_type": "int", + "start_column": 8, + "start_line": 22 + }, + { + "argument_types": [ + "len" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "len" + } + ], + "callee_signature": "builtins.str.__init__", + "end_column": 33, + "end_line": 23, + "is_constructor_call": true, + "method_name": "str", + "return_type": "str", + "start_column": 15, + "start_line": 23 + }, + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "list" + } + ], + "callee_signature": "builtins.len", + "end_column": 32, + "end_line": 23, + "is_constructor_call": false, + "method_name": "len", + "return_type": "int", + "start_column": 19, + "start_line": 23 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 21, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 23, + "id": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 16, + "end_line": 21, + "initializer": "self.env['account.account'].search([])", + "name": "accounts", + "scope": "function", + "start_column": 8, + "start_line": 21, + "type": "list" + } + ], + "name": "action_post", + "parameters": [ + { + "end_column": 24, + "end_line": 20, + "name": "self", + "start_column": 20, + "start_line": 20, + "type": "AccountMove" + } + ], + "signature": "main.AccountMove.action_post", + "span": { + "bytes": [ + 295, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 20, + 4 + ] + }, + "start_line": 20, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 23, + "id": "can://python/method_call_resolution/main.py/AccountMove", + "kind": "class", + "name": "AccountMove", + "signature": "main.AccountMove", + "span": { + "bytes": [ + 206, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 16, + 0 + ] + }, + "start_line": 16, + "types": {} + }, + "main.Environment": { + "attributes": {}, + "base_classes": [], + "callables": { + "__getitem__": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 3, + "name": "Model", + "qualified_name": "main.Model", + "scope": "local", + "type": "Model" + } + ], + "body": { + "3:15": { + "callee": "can://python/method_call_resolution/@external/main.Model/__init__", + "kind": "call", + "span": { + "bytes": [ + 67, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 3, + 15 + ] + } + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Model.__init__", + "end_column": 22, + "end_line": 3, + "is_constructor_call": true, + "method_name": "Model", + "return_type": "Model", + "start_column": 15, + "start_line": 3 + } + ], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 3, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 3, + "id": "can://python/method_call_resolution/main.py/Environment/__getitem__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__getitem__", + "parameters": [ + { + "end_column": 24, + "end_line": 2, + "name": "self", + "start_column": 20, + "start_line": 2, + "type": "Environment" + }, + { + "end_column": 30, + "end_line": 2, + "name": "name", + "start_column": 26, + "start_line": 2 + } + ], + "signature": "main.Environment.__getitem__", + "span": { + "bytes": [ + 23, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 2, + 4 + ] + }, + "start_line": 2, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 3, + "id": "can://python/method_call_resolution/main.py/Environment", + "kind": "class", + "name": "Environment", + "signature": "main.Environment", + "span": { + "bytes": [ + 0, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 1, + 0 + ] + }, + "start_line": 1, + "types": {} + }, + "main.Model": { + "attributes": { + "env": { + "comments": [], + "end_line": 7, + "initializer": "Environment()", + "name": "env", + "start_line": 7, + "type": "Environment" + } + }, + "base_classes": [], + "callables": { + "helper": { + "accessed_symbols": [], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 13, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 13, + "id": "can://python/method_call_resolution/main.py/Model/helper(self)", + "kind": "function", + "local_variables": [], + "name": "helper", + "parameters": [ + { + "end_column": 19, + "end_line": 12, + "name": "self", + "start_column": 15, + "start_line": 12, + "type": "Model" + } + ], + "signature": "main.Model.helper", + "span": { + "bytes": [ + 168, + 203 + ], + "end": [ + 13, + 17 + ], + "start": [ + 12, + 4 + ] + }, + "start_line": 12, + "summary": [], + "types": {} + }, + "search": { + "accessed_symbols": [], + "body": {}, + "call_sites": [], + "callables": {}, + "cdg": [], + "cfg": [], + "code_start_line": 10, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 10, + "id": "can://python/method_call_resolution/main.py/Model/search(self,domain)", + "kind": "function", + "local_variables": [], + "name": "search", + "parameters": [ + { + "end_column": 19, + "end_line": 9, + "name": "self", + "start_column": 15, + "start_line": 9, + "type": "Model" + }, + { + "end_column": 27, + "end_line": 9, + "name": "domain", + "start_column": 21, + "start_line": 9, + "type": "list" + } + ], + "signature": "main.Model.search", + "span": { + "bytes": [ + 119, + 162 + ], + "end": [ + 10, + 17 + ], + "start": [ + 9, + 4 + ] + }, + "start_line": 9, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 13, + "id": "can://python/method_call_resolution/main.py/Model", + "kind": "class", + "name": "Model", + "signature": "main.Model", + "span": { + "bytes": [ + 77, + 203 + ], + "end": [ + 13, + 17 + ], + "start": [ + 6, + 0 + ] + }, + "start_line": 6, + "types": {} + } + }, + "variables": [] + } + } + }, + "language": "python", + "max_level": 2, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/golden/pipeline_equivalence/method_call_resolution.a3.json b/test/golden/pipeline_equivalence/method_call_resolution.a3.json new file mode 100644 index 0000000..e482418 --- /dev/null +++ b/test/golden/pipeline_equivalence/method_call_resolution.a3.json @@ -0,0 +1,946 @@ +{ + "analyzer": { + "config": { + "analysis_level": 3 + }, + "name": "codeanalyzer-python" + }, + "application": { + "call_graph": [ + { + "dst": "can://python/method_call_resolution/@external/builtins/len", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/@external/builtins.str/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/main.py/Model/helper(self)", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/main.py/Model/search(self,domain)", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "weight": 1 + }, + { + "dst": "can://python/method_call_resolution/@external/main.Model/__init__", + "prov": [ + "jedi" + ], + "src": "can://python/method_call_resolution/main.py/Environment/__getitem__(self,name)", + "weight": 1 + } + ], + "external_symbols": { + "can://python/method_call_resolution/@external/builtins.str/__init__": { + "id": "can://python/method_call_resolution/@external/builtins.str/__init__", + "kind": "external", + "module": "builtins.str", + "name": "__init__" + }, + "can://python/method_call_resolution/@external/builtins/len": { + "id": "can://python/method_call_resolution/@external/builtins/len", + "kind": "external", + "module": "builtins", + "name": "len" + }, + "can://python/method_call_resolution/@external/main.Model/__init__": { + "id": "can://python/method_call_resolution/@external/main.Model/__init__", + "kind": "external", + "module": "main.Model", + "name": "__init__" + } + }, + "id": "can://python/method_call_resolution", + "kind": "application", + "param_in": [], + "param_out": [], + "symbol_table": { + "main.py": { + "comments": [], + "content_hash": "286d25d0f8854ad5fc4d879dd9e9f7b3747e38081c3b971c7362ea0ea11c1901", + "file_size": 432, + "functions": {}, + "id": "can://python/method_call_resolution/main.py", + "imports": [], + "kind": "module", + "module_name": "main", + "source": "class Environment:\n def __getitem__(self, name):\n return Model()\n\n\nclass Model:\n env = Environment()\n\n def search(self, domain):\n return []\n\n def helper(self):\n return 42\n\n\nclass AccountMove(Model):\n _name = 'account.move'\n _inherit = ['mail.thread']\n\n def action_post(self):\n accounts = self.env['account.account'].search([])\n self.helper()\n return str(len(accounts))\n", + "types": { + "main.AccountMove": { + "attributes": { + "_inherit": { + "comments": [], + "end_line": 18, + "initializer": "['mail.thread']", + "name": "_inherit", + "start_line": 18, + "type": "list" + }, + "_name": { + "comments": [], + "end_line": 17, + "initializer": "'account.move'", + "name": "_name", + "start_line": 17, + "type": "str" + } + }, + "base_classes": [ + "Model" + ], + "callables": { + "action_post": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 23, + "name": "str", + "qualified_name": "builtins.str", + "scope": "local", + "type": "str" + }, + { + "col_offset": 8, + "is_builtin": false, + "kind": "variable", + "lineno": 22, + "name": "self", + "qualified_name": "main.AccountMove", + "scope": "local", + "type": "AccountMove" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "function", + "lineno": 23, + "name": "len", + "qualified_name": "builtins.len", + "scope": "local", + "type": "len" + }, + { + "col_offset": 23, + "is_builtin": false, + "kind": "variable", + "lineno": 23, + "name": "accounts", + "qualified_name": "builtins.list", + "scope": "local", + "type": "list" + }, + { + "col_offset": 19, + "is_builtin": false, + "kind": "variable", + "lineno": 21, + "name": "self", + "qualified_name": "main.AccountMove", + "scope": "local", + "type": "AccountMove" + } + ], + "body": { + "21:19": { + "callee": "can://python/method_call_resolution/main.py/Model/search(self,domain)", + "kind": "call", + "span": { + "bytes": [ + 337, + 375 + ], + "end": [ + 21, + 57 + ], + "start": [ + 21, + 19 + ] + } + }, + "21:8": { + "kind": "statement", + "span": { + "bytes": [ + 326, + 375 + ], + "end": [ + 21, + 57 + ], + "start": [ + 21, + 8 + ] + } + }, + "22:8": { + "callee": "can://python/method_call_resolution/main.py/Model/helper(self)", + "kind": "call", + "span": { + "bytes": [ + 384, + 397 + ], + "end": [ + 22, + 21 + ], + "start": [ + 22, + 8 + ] + } + }, + "23:15": { + "callee": "can://python/method_call_resolution/@external/builtins.str/__init__", + "kind": "call", + "span": { + "bytes": [ + 413, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 23, + 15 + ] + } + }, + "23:19": { + "callee": "can://python/method_call_resolution/@external/builtins/len", + "kind": "call", + "span": { + "bytes": [ + 417, + 430 + ], + "end": [ + 23, + 32 + ], + "start": [ + 23, + 19 + ] + } + }, + "23:8": { + "kind": "return", + "span": { + "bytes": [ + 406, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 23, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "List", + "inferred_type": "list" + } + ], + "callee_signature": "main.Model.search", + "end_column": 57, + "end_line": 21, + "is_constructor_call": false, + "method_name": "search", + "receiver_expr": "self.env['account.account']", + "receiver_type": "AccountMove", + "return_type": "list", + "start_column": 19, + "start_line": 21 + }, + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Model.helper", + "end_column": 21, + "end_line": 22, + "is_constructor_call": false, + "method_name": "helper", + "receiver_expr": "self", + "receiver_type": "AccountMove", + "return_type": "int", + "start_column": 8, + "start_line": 22 + }, + { + "argument_types": [ + "len" + ], + "arguments": [ + { + "ast_kind": "Call", + "inferred_type": "len" + } + ], + "callee_signature": "builtins.str.__init__", + "end_column": 33, + "end_line": 23, + "is_constructor_call": true, + "method_name": "str", + "return_type": "str", + "start_column": 15, + "start_line": 23 + }, + { + "argument_types": [ + "list" + ], + "arguments": [ + { + "ast_kind": "Name", + "inferred_type": "list" + } + ], + "callee_signature": "builtins.len", + "end_column": 32, + "end_line": 23, + "is_constructor_call": false, + "method_name": "len", + "return_type": "int", + "start_column": 19, + "start_line": 23 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "21:8", + "src": "@entry" + }, + { + "dst": "22:8", + "src": "21:8" + }, + { + "dst": "23:8", + "src": "22:8" + } + ], + "cfg": [ + { + "dst": "21:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "22:8", + "kind": "fallthrough", + "src": "21:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "21:8" + }, + { + "dst": "23:8", + "kind": "fallthrough", + "src": "22:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "22:8" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "23:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "23:8" + } + ], + "code_start_line": 21, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "21:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.env[*]" + }, + { + "dst": "21:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.env[*].search" + }, + { + "dst": "22:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self" + }, + { + "dst": "22:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "self.helper" + }, + { + "dst": "23:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "len" + }, + { + "dst": "23:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "str" + }, + { + "dst": "22:8", + "prov": [ + "ssa" + ], + "src": "21:8", + "var": "self" + }, + { + "dst": "23:8", + "prov": [ + "ssa" + ], + "src": "21:8", + "var": "accounts" + } + ], + "decorators": [], + "end_line": 23, + "id": "can://python/method_call_resolution/main.py/AccountMove/action_post(self)", + "kind": "function", + "local_variables": [ + { + "end_column": 16, + "end_line": 21, + "initializer": "self.env['account.account'].search([])", + "name": "accounts", + "scope": "function", + "start_column": 8, + "start_line": 21, + "type": "list" + } + ], + "name": "action_post", + "parameters": [ + { + "end_column": 24, + "end_line": 20, + "name": "self", + "start_column": 20, + "start_line": 20, + "type": "AccountMove" + } + ], + "signature": "main.AccountMove.action_post", + "span": { + "bytes": [ + 295, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 20, + 4 + ] + }, + "start_line": 20, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 23, + "id": "can://python/method_call_resolution/main.py/AccountMove", + "kind": "class", + "name": "AccountMove", + "signature": "main.AccountMove", + "span": { + "bytes": [ + 206, + 431 + ], + "end": [ + 23, + 33 + ], + "start": [ + 16, + 0 + ] + }, + "start_line": 16, + "types": {} + }, + "main.Environment": { + "attributes": {}, + "base_classes": [], + "callables": { + "__getitem__": { + "accessed_symbols": [ + { + "col_offset": 15, + "is_builtin": false, + "kind": "class", + "lineno": 3, + "name": "Model", + "qualified_name": "main.Model", + "scope": "local", + "type": "Model" + } + ], + "body": { + "3:15": { + "callee": "can://python/method_call_resolution/@external/main.Model/__init__", + "kind": "call", + "span": { + "bytes": [ + 67, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 3, + 15 + ] + } + }, + "3:8": { + "kind": "return", + "span": { + "bytes": [ + 60, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 3, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [ + { + "argument_types": [], + "arguments": [], + "callee_signature": "main.Model.__init__", + "end_column": 22, + "end_line": 3, + "is_constructor_call": true, + "method_name": "Model", + "return_type": "Model", + "start_column": 15, + "start_line": 3 + } + ], + "callables": {}, + "cdg": [ + { + "dst": "3:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "3:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "exception", + "src": "3:8" + }, + { + "dst": "@exit", + "kind": "return", + "src": "3:8" + } + ], + "code_start_line": 3, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [ + { + "dst": "3:8", + "prov": [ + "ssa" + ], + "src": "@entry", + "var": "main::Model" + } + ], + "decorators": [], + "end_line": 3, + "id": "can://python/method_call_resolution/main.py/Environment/__getitem__(self,name)", + "kind": "function", + "local_variables": [], + "name": "__getitem__", + "parameters": [ + { + "end_column": 24, + "end_line": 2, + "name": "self", + "start_column": 20, + "start_line": 2, + "type": "Environment" + }, + { + "end_column": 30, + "end_line": 2, + "name": "name", + "start_column": 26, + "start_line": 2 + } + ], + "signature": "main.Environment.__getitem__", + "span": { + "bytes": [ + 23, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 2, + 4 + ] + }, + "start_line": 2, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 3, + "id": "can://python/method_call_resolution/main.py/Environment", + "kind": "class", + "name": "Environment", + "signature": "main.Environment", + "span": { + "bytes": [ + 0, + 74 + ], + "end": [ + 3, + 22 + ], + "start": [ + 1, + 0 + ] + }, + "start_line": 1, + "types": {} + }, + "main.Model": { + "attributes": { + "env": { + "comments": [], + "end_line": 7, + "initializer": "Environment()", + "name": "env", + "start_line": 7, + "type": "Environment" + } + }, + "base_classes": [], + "callables": { + "helper": { + "accessed_symbols": [], + "body": { + "13:8": { + "kind": "return", + "span": { + "bytes": [ + 194, + 203 + ], + "end": [ + 13, + 17 + ], + "start": [ + 13, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "13:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "13:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "13:8" + } + ], + "code_start_line": 13, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 13, + "id": "can://python/method_call_resolution/main.py/Model/helper(self)", + "kind": "function", + "local_variables": [], + "name": "helper", + "parameters": [ + { + "end_column": 19, + "end_line": 12, + "name": "self", + "start_column": 15, + "start_line": 12, + "type": "Model" + } + ], + "signature": "main.Model.helper", + "span": { + "bytes": [ + 168, + 203 + ], + "end": [ + 13, + 17 + ], + "start": [ + 12, + 4 + ] + }, + "start_line": 12, + "summary": [], + "types": {} + }, + "search": { + "accessed_symbols": [], + "body": { + "10:8": { + "kind": "return", + "span": { + "bytes": [ + 153, + 162 + ], + "end": [ + 10, + 17 + ], + "start": [ + 10, + 8 + ] + } + }, + "@entry": { + "kind": "entry" + }, + "@exit": { + "kind": "exit" + } + }, + "call_sites": [], + "callables": {}, + "cdg": [ + { + "dst": "10:8", + "src": "@entry" + } + ], + "cfg": [ + { + "dst": "10:8", + "kind": "fallthrough", + "src": "@entry" + }, + { + "dst": "@exit", + "kind": "return", + "src": "10:8" + } + ], + "code_start_line": 10, + "comments": [], + "cyclomatic_complexity": 2, + "ddg": [], + "decorators": [], + "end_line": 10, + "id": "can://python/method_call_resolution/main.py/Model/search(self,domain)", + "kind": "function", + "local_variables": [], + "name": "search", + "parameters": [ + { + "end_column": 19, + "end_line": 9, + "name": "self", + "start_column": 15, + "start_line": 9, + "type": "Model" + }, + { + "end_column": 27, + "end_line": 9, + "name": "domain", + "start_column": 21, + "start_line": 9, + "type": "list" + } + ], + "signature": "main.Model.search", + "span": { + "bytes": [ + 119, + 162 + ], + "end": [ + 10, + 17 + ], + "start": [ + 9, + 4 + ] + }, + "start_line": 9, + "summary": [], + "types": {} + } + }, + "comments": [], + "end_line": 13, + "id": "can://python/method_call_resolution/main.py/Model", + "kind": "class", + "name": "Model", + "signature": "main.Model", + "span": { + "bytes": [ + 77, + 203 + ], + "end": [ + 13, + 17 + ], + "start": [ + 6, + 0 + ] + }, + "start_line": 6, + "types": {} + } + }, + "variables": [] + } + } + }, + "k_limit": 3, + "language": "python", + "max_level": 3, + "schema_version": "2.0.0" +} \ No newline at end of file diff --git a/test/test_pipeline_equivalence.py b/test/test_pipeline_equivalence.py new file mode 100644 index 0000000..563297c --- /dev/null +++ b/test/test_pipeline_equivalence.py @@ -0,0 +1,87 @@ +"""Byte-for-byte characterization gate for the analysis pipeline refactor. + +Runs the CLI on copies of fixtures placed OUTSIDE any git tree (so +`repository_info` returns None and the output is deterministic), normalizes the +one volatile field (`analyzer.version`), and compares against committed goldens. +Regenerate with `REGEN=1 pytest test/test_pipeline_equivalence.py`. +""" +import json +import os +import shutil +import subprocess +import sys +from pathlib import Path + +import pytest + +FIXTURES = [ + "class_hierarchy", + "decorators_and_hof", + "async_patterns", + "method_call_resolution", +] +GOLDEN_DIR = Path(__file__).parent / "golden" / "pipeline_equivalence" +FIXTURE_ROOT = Path(__file__).parent / "fixtures" / "single_functionalities" + + +def _scalpel_available() -> bool: + try: + import scalpel # noqa: F401 + return True + except Exception: + return False + + +def _strip_volatile_paths(node) -> None: + """Recursively drop filesystem bookkeeping fields (`PyModule.file_path`, + `PyCallable.path`, `PyModule.last_modified`) that record where a fixture + copy happened to live and when it was last touched on disk, not anything + the analyzer computed. `tmp_path` mints a fresh, uniquely-numbered + directory every pytest invocation, so these fields differ run to run even + when the analysis content is identical — they must be normalized away for + the golden comparison to be meaningful.""" + if isinstance(node, dict): + for key in ("file_path", "path", "last_modified"): + node.pop(key, None) + for value in node.values(): + _strip_volatile_paths(value) + elif isinstance(node, list): + for item in node: + _strip_volatile_paths(item) + + +def _normalize(payload: dict) -> dict: + """Drop the environment-volatile fields so the gate is stable across + version bumps and non-git run locations.""" + payload.get("application", {}).pop("repository", None) + analyzer = payload.get("analyzer") + if isinstance(analyzer, dict): + analyzer.pop("version", None) + _strip_volatile_paths(payload.get("application", {})) + return payload + + +def _run(proj: Path, level: int) -> dict: + out = subprocess.run( + [sys.executable, "-m", "codeanalyzer", "-i", str(proj), "-a", str(level), "--no-venv"], + capture_output=True, text=True, check=True, + ).stdout + return _normalize(json.loads(out)) + + +@pytest.mark.parametrize("fixture", FIXTURES) +@pytest.mark.parametrize("level", [1, 2, 3, 4]) +def test_pipeline_output_matches_golden(tmp_path, fixture, level): + if level == 4 and not _scalpel_available(): + pytest.skip("L4 golden requires python-scalpel (optional soft dependency)") + proj = tmp_path / fixture + shutil.copytree(FIXTURE_ROOT / fixture, proj) + got = _run(proj, level) + golden_path = GOLDEN_DIR / f"{fixture}.a{level}.json" + if os.environ.get("REGEN"): + GOLDEN_DIR.mkdir(parents=True, exist_ok=True) + golden_path.write_text(json.dumps(got, indent=2, sort_keys=True), encoding="utf-8") + return + assert golden_path.exists(), f"missing golden {golden_path}; regenerate with REGEN=1" + want = _normalize(json.loads(golden_path.read_text(encoding="utf-8"))) + assert got == want, f"{fixture} @ -a {level} diverged from golden" From 8f29ef4bae9576779217f8cb6f3e49e649a643e1 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 11:31:40 -0400 Subject: [PATCH 04/14] feat(pipeline): add AnalysisContext carrier for the pass chain --- codeanalyzer/pipeline/__init__.py | 3 +++ codeanalyzer/pipeline/context.py | 32 +++++++++++++++++++++++++++++++ test/test_pipeline.py | 25 ++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 codeanalyzer/pipeline/__init__.py create mode 100644 codeanalyzer/pipeline/context.py create mode 100644 test/test_pipeline.py diff --git a/codeanalyzer/pipeline/__init__.py b/codeanalyzer/pipeline/__init__.py new file mode 100644 index 0000000..5fc0b9e --- /dev/null +++ b/codeanalyzer/pipeline/__init__.py @@ -0,0 +1,3 @@ +from codeanalyzer.pipeline.context import AnalysisContext + +__all__ = ["AnalysisContext"] diff --git a/codeanalyzer/pipeline/context.py b/codeanalyzer/pipeline/context.py new file mode 100644 index 0000000..e4431e8 --- /dev/null +++ b/codeanalyzer/pipeline/context.py @@ -0,0 +1,32 @@ +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Dict, Optional + +from codeanalyzer.options import AnalysisOptions +from codeanalyzer.schema import PyApplication, PyModule + + +@dataclass +class AnalysisContext: + """Mutable carrier threaded through the AnalysisPipeline passes. + + Inputs are set at construction; produced artifacts start as ``None`` and are + filled in chain order: ``symbol_table`` -> ``app``/``sig_to_id`` -> + ``infos`` -> ``ir``. ``infos`` (L3 PDGs) is deliberately reused by the L4 + pass, so its ordering in the chain matters. + """ + + # inputs + options: AnalysisOptions + project_dir: Path + virtualenv: Optional[Path] + analysis_level: int + app_name: str + cached_symbol_table: Dict[str, PyModule] = field(default_factory=dict) + + # produced by passes (loosely typed to avoid importing dataflow types here) + symbol_table: Optional[Dict[str, PyModule]] = None + app: Optional[PyApplication] = None + sig_to_id: Optional[Dict[str, str]] = None + infos: Optional[Dict[str, Any]] = None # Dict[str, FunctionInfo] + ir: Optional[Any] = None # ProgramGraphsIR diff --git a/test/test_pipeline.py b/test/test_pipeline.py new file mode 100644 index 0000000..15a7b97 --- /dev/null +++ b/test/test_pipeline.py @@ -0,0 +1,25 @@ +from pathlib import Path + +from codeanalyzer.options import AnalysisOptions +from codeanalyzer.config import OutputFormat +from codeanalyzer.pipeline import AnalysisContext + + +def _opts(tmp_path): + return AnalysisOptions( + input=tmp_path, output=None, format=OutputFormat.JSON, + analysis_level=1, skip_tests=True, no_venv=True, + ) + + +def test_context_defaults(tmp_path): + ctx = AnalysisContext( + options=_opts(tmp_path), project_dir=Path(tmp_path), + virtualenv=None, analysis_level=1, app_name="proj", + ) + assert ctx.cached_symbol_table == {} + assert ctx.symbol_table is None + assert ctx.app is None + assert ctx.sig_to_id is None + assert ctx.infos is None + assert ctx.ir is None From fd7c26b4883a0eb7f5a4525a03ca469e4fbd1f0d Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 11:40:52 -0400 Subject: [PATCH 05/14] refactor(pipeline): extract build_symbol_table as a free function --- codeanalyzer/core.py | 210 +---------------------- codeanalyzer/pipeline/symbol_table.py | 233 ++++++++++++++++++++++++++ test/test_pipeline.py | 15 ++ 3 files changed, 252 insertions(+), 206 deletions(-) create mode 100644 codeanalyzer/pipeline/symbol_table.py diff --git a/codeanalyzer/core.py b/codeanalyzer/core.py index 05b464d..67adb80 100644 --- a/codeanalyzer/core.py +++ b/codeanalyzer/core.py @@ -8,7 +8,6 @@ import time -import ray from codeanalyzer.utils import logger from codeanalyzer.schema import ( Analysis, @@ -37,46 +36,6 @@ from codeanalyzer.options import AnalysisOptions from codeanalyzer.provenance import analyzer_info, repository_info -def _ensure_ray() -> None: - """Initialize Ray with the driver's pinned hash seed in the workers. - - An implicit auto-init would not carry PYTHONHASHSEED into worker - interpreters, so PyCG shards (and Jedi inference) run there with random - set-iteration order and the emitted edges vary run to run (issue #99).""" - if not ray.is_initialized(): - ray.init( - runtime_env={ - "env_vars": { - "PYTHONHASHSEED": os.environ.get("PYTHONHASHSEED", "0") - } - }, - ) - - -@ray.remote -def _process_file_with_ray(py_file: Union[Path, str], project_dir: Union[Path, str], virtualenv: Union[Path, str, None]) -> Dict[str, PyModule]: - """Processes files in the project directory using Ray for distributed processing. - - Args: - py_file (Union[Path, str]): Path to the Python file to process. - project_dir (Union[Path, str]): Path to the project directory. - virtualenv (Union[Path, str, None]): Path to the virtual environment directory. - Returns: - Dict[str, PyModule]: A dictionary mapping file paths to PyModule objects. - """ - from rich.console import Console - console = Console() - module_map: Dict[str, PyModule] = {} - try: - py_file = Path(py_file) - symbol_table_builder = SymbolTableBuilder(project_dir, virtualenv) - module_map[str(py_file.relative_to(Path(project_dir)))] = symbol_table_builder.build_pymodule_from_file(py_file) - except Exception as e: - console.log(f"❌ Failed to process {py_file}: {e}") - raise SymbolTableBuilderRayError(f"Ray processing error for {py_file}: {e}") - return module_map - - class Codeanalyzer: """Core static analysis engine for Python projects. @@ -755,35 +714,6 @@ def _save_analysis_cache(self, analysis: Analysis, cache_file: Path) -> None: logger.info(f"Analysis cached to {cache_file}") - def _file_unchanged(self, file_path: Path, cached_module: PyModule) -> bool: - """Check if a file has changed since it was cached. - - Args: - file_path: Path to the file to check - cached_module: The cached PyModule for this file - - Returns: - bool: True if file is unchanged, False otherwise - """ - try: - # Check last modified time and file size - if (cached_module.last_modified is not None and - cached_module.file_size is not None and - cached_module.last_modified == file_path.stat().st_mtime and - cached_module.file_size == file_path.stat().st_size): - return True - # Also check content hash for extra safety - if cached_module.content_hash is not None: - content_hash = hashlib.sha256(file_path.read_bytes()).hexdigest() - return content_hash == cached_module.content_hash - - # No cached metadata mismatch, assume file changed - return False - - except Exception as e: - logger.debug(f"Error checking file {file_path}: {e}") - return False - def _compute_checksum(self, root: Path) -> str: """Compute SHA256 checksum of all Python source files in a project directory. If somethings changes, the checksum will change and thus the analysis will be redone. @@ -799,143 +729,11 @@ def _compute_checksum(self, root: Path) -> str: sha256.update(py_file.read_bytes()) return sha256.hexdigest() - def _build_symbol_table(self, cached_symbol_table: Optional[Dict[str, PyModule]] = None) -> Dict[str, PyModule]: - """Builds the symbol table for the project. - - This method scans the project directory, identifies Python files, - and constructs a symbol table containing information about classes, - functions, and variables defined in those files. - - Args: - cached_app: Previously cached PyApplication to reuse unchanged files - - Returns: - Dict[str, PyModule]: A dictionary mapping file paths to PyModule objects. - """ - symbol_table: Dict[str, PyModule] = {} - t0_st = time.perf_counter() - - # Handle single file analysis - if self.file_name is not None: - single_file = self.project_dir / self.file_name - logger.info(f"Analyzing single file: {single_file}") - - # Check if file is in cache and unchanged - file_key = str(single_file.relative_to(self.project_dir)) - if file_key in cached_symbol_table and not self.rebuild_analysis: - # Compute file checksum to see if it changed - if self._file_unchanged(single_file, cached_symbol_table[file_key]): - logger.info(f"Using cached analysis for {single_file}") - symbol_table[file_key] = cached_symbol_table[file_key] - return symbol_table - - # File is new or changed, analyze it - try: - symbol_table_builder = SymbolTableBuilder(self.project_dir, self.virtualenv) - py_module = symbol_table_builder.build_pymodule_from_file(single_file) - symbol_table[file_key] = py_module - logger.info("✅ Single file analysis complete.") - return symbol_table - except Exception as e: - logger.error(f"Failed to process {single_file}: {e}") - return symbol_table - - # Get all Python files first to show accurate progress - py_files = [] - for py_file in self.project_dir.rglob("*.py"): - rel_path = py_file.relative_to(self.project_dir) - path_parts = rel_path.parts - filename = py_file.name - - # Skip directories we don't care about - if ( - "site-packages" in path_parts - or ".venv" in path_parts - or ".codeanalyzer" in path_parts - ): - continue - - # Skip test files if enabled - if self.skip_tests and ( - "test" in path_parts - or "tests" in path_parts - or filename.startswith("test_") - or filename.endswith("_test.py") - ): - continue - - py_files.append(py_file) - - if self.using_ray: - logger.info("Using Ray for distributed symbol table generation.") - # Separate files into cached and new/changed - files_to_process = [] - for py_file in py_files: - file_key = str(py_file.relative_to(self.project_dir)) - if file_key in cached_symbol_table and not self.rebuild_analysis: - if self._file_unchanged(py_file, cached_symbol_table[file_key]): - # Use cached version - symbol_table[file_key] = cached_symbol_table[file_key] - continue - files_to_process.append(py_file) - - # Process only new/changed files with Ray - if files_to_process: - _ensure_ray() - futures = [_process_file_with_ray.remote(py_file, self.project_dir, str(self.virtualenv) if self.virtualenv else None) for py_file in files_to_process] - - with ProgressBar(len(futures), "Building symbol table (parallel)") as progress: - pending = futures[:] - while pending: - done, pending = ray.wait(pending, num_returns=1) - result = ray.get(done[0]) - if result: - symbol_table.update(result) - progress.advance() - else: - logger.info("Building symbol table serially.") - symbol_table_builder = SymbolTableBuilder(self.project_dir, self.virtualenv) - files_processed = 0 - files_from_cache = 0 - - with ProgressBar(len(py_files), "Building symbol table") as progress: - for py_file in py_files: - file_key = str(py_file.relative_to(self.project_dir)) - - # Check if file is cached and unchanged - if file_key in cached_symbol_table and not self.rebuild_analysis: - if self._file_unchanged(py_file, cached_symbol_table[file_key]): - symbol_table[file_key] = cached_symbol_table[file_key] - files_from_cache += 1 - progress.advance() - continue - - # File is new or changed, analyze it - try: - py_module = symbol_table_builder.build_pymodule_from_file(py_file) - symbol_table[file_key] = py_module - files_processed += 1 - except Exception as e: - logger.error(f"Failed to process {py_file}: {e}") - progress.advance() - - if files_from_cache > 0: - logger.info(f"Reused {files_from_cache} files from cache, processed {files_processed} new/changed files") - - if py_files and not symbol_table: - logger.error( - "Every one of the %d discovered Python files failed to process — " - "the symbol table is empty. This usually means the analysis " - "environment's interpreter is newer than the installed jedi/parso " - "stack supports (#107); check the per-file errors above.", - len(py_files), - ) - - logger.info( - "✅ Symbol table: %d modules in %.1fs", - len(symbol_table), time.perf_counter() - t0_st, + def _build_symbol_table(self, cached_symbol_table=None): + from codeanalyzer.pipeline.symbol_table import build_symbol_table + return build_symbol_table( + self.project_dir, self.virtualenv, self.options, cached_symbol_table or {} ) - return symbol_table def _get_pycg_call_graph( self, diff --git a/codeanalyzer/pipeline/symbol_table.py b/codeanalyzer/pipeline/symbol_table.py new file mode 100644 index 0000000..4b2ab46 --- /dev/null +++ b/codeanalyzer/pipeline/symbol_table.py @@ -0,0 +1,233 @@ +import hashlib +import os +import time +from pathlib import Path +from typing import Dict, Optional, Union + +import ray + +from codeanalyzer.options import AnalysisOptions +from codeanalyzer.schema import PyModule +from codeanalyzer.syntactic_analysis.exceptions import SymbolTableBuilderRayError +from codeanalyzer.syntactic_analysis.symbol_table_builder import SymbolTableBuilder +from codeanalyzer.utils import ProgressBar, logger + + +def _ensure_ray() -> None: + """Initialize Ray with the driver's pinned hash seed in the workers. + + An implicit auto-init would not carry PYTHONHASHSEED into worker + interpreters, so PyCG shards (and Jedi inference) run there with random + set-iteration order and the emitted edges vary run to run (issue #99).""" + if not ray.is_initialized(): + ray.init( + runtime_env={ + "env_vars": { + "PYTHONHASHSEED": os.environ.get("PYTHONHASHSEED", "0") + } + }, + ) + + +@ray.remote +def _process_file_with_ray(py_file: Union[Path, str], project_dir: Union[Path, str], virtualenv: Union[Path, str, None]) -> Dict[str, PyModule]: + """Processes files in the project directory using Ray for distributed processing. + + Args: + py_file (Union[Path, str]): Path to the Python file to process. + project_dir (Union[Path, str]): Path to the project directory. + virtualenv (Union[Path, str, None]): Path to the virtual environment directory. + Returns: + Dict[str, PyModule]: A dictionary mapping file paths to PyModule objects. + """ + from rich.console import Console + console = Console() + module_map: Dict[str, PyModule] = {} + try: + py_file = Path(py_file) + symbol_table_builder = SymbolTableBuilder(project_dir, virtualenv) + module_map[str(py_file.relative_to(Path(project_dir)))] = symbol_table_builder.build_pymodule_from_file(py_file) + except Exception as e: + console.log(f"❌ Failed to process {py_file}: {e}") + raise SymbolTableBuilderRayError(f"Ray processing error for {py_file}: {e}") + return module_map + + +def _file_unchanged(file_path: Path, cached_module: PyModule) -> bool: + """Check if a file has changed since it was cached. + + Args: + file_path: Path to the file to check + cached_module: The cached PyModule for this file + + Returns: + bool: True if file is unchanged, False otherwise + """ + try: + # Check last modified time and file size + if (cached_module.last_modified is not None and + cached_module.file_size is not None and + cached_module.last_modified == file_path.stat().st_mtime and + cached_module.file_size == file_path.stat().st_size): + return True + # Also check content hash for extra safety + if cached_module.content_hash is not None: + content_hash = hashlib.sha256(file_path.read_bytes()).hexdigest() + return content_hash == cached_module.content_hash + + # No cached metadata mismatch, assume file changed + return False + + except Exception as e: + logger.debug(f"Error checking file {file_path}: {e}") + return False + + +def build_symbol_table( + project_dir: Path, + virtualenv: Optional[Path], + options: AnalysisOptions, + cached_symbol_table: Optional[Dict[str, PyModule]] = None, +) -> Dict[str, PyModule]: + """Build the symbol table for the project (moved from Codeanalyzer). + + This scans the project directory, identifies Python files, + and constructs a symbol table containing information about classes, + functions, and variables defined in those files. + + Args: + project_dir: Root directory of the project. + virtualenv: Path to the virtual environment directory, if any. + options: Analysis configuration options. + cached_app: Previously cached PyApplication to reuse unchanged files + + Returns: + Dict[str, PyModule]: A dictionary mapping file paths to PyModule objects. + """ + if cached_symbol_table is None: + cached_symbol_table = {} + + symbol_table: Dict[str, PyModule] = {} + t0_st = time.perf_counter() + + # Handle single file analysis + if options.file_name is not None: + single_file = project_dir / options.file_name + logger.info(f"Analyzing single file: {single_file}") + + # Check if file is in cache and unchanged + file_key = str(single_file.relative_to(project_dir)) + if file_key in cached_symbol_table and not options.rebuild_analysis: + # Compute file checksum to see if it changed + if _file_unchanged(single_file, cached_symbol_table[file_key]): + logger.info(f"Using cached analysis for {single_file}") + symbol_table[file_key] = cached_symbol_table[file_key] + return symbol_table + + # File is new or changed, analyze it + try: + symbol_table_builder = SymbolTableBuilder(project_dir, virtualenv) + py_module = symbol_table_builder.build_pymodule_from_file(single_file) + symbol_table[file_key] = py_module + logger.info("✅ Single file analysis complete.") + return symbol_table + except Exception as e: + logger.error(f"Failed to process {single_file}: {e}") + return symbol_table + + # Get all Python files first to show accurate progress + py_files = [] + for py_file in project_dir.rglob("*.py"): + rel_path = py_file.relative_to(project_dir) + path_parts = rel_path.parts + filename = py_file.name + + # Skip directories we don't care about + if ( + "site-packages" in path_parts + or ".venv" in path_parts + or ".codeanalyzer" in path_parts + ): + continue + + # Skip test files if enabled + if options.skip_tests and ( + "test" in path_parts + or "tests" in path_parts + or filename.startswith("test_") + or filename.endswith("_test.py") + ): + continue + + py_files.append(py_file) + + if options.using_ray: + logger.info("Using Ray for distributed symbol table generation.") + # Separate files into cached and new/changed + files_to_process = [] + for py_file in py_files: + file_key = str(py_file.relative_to(project_dir)) + if file_key in cached_symbol_table and not options.rebuild_analysis: + if _file_unchanged(py_file, cached_symbol_table[file_key]): + # Use cached version + symbol_table[file_key] = cached_symbol_table[file_key] + continue + files_to_process.append(py_file) + + # Process only new/changed files with Ray + if files_to_process: + _ensure_ray() + futures = [_process_file_with_ray.remote(py_file, project_dir, str(virtualenv) if virtualenv else None) for py_file in files_to_process] + + with ProgressBar(len(futures), "Building symbol table (parallel)") as progress: + pending = futures[:] + while pending: + done, pending = ray.wait(pending, num_returns=1) + result = ray.get(done[0]) + if result: + symbol_table.update(result) + progress.advance() + else: + logger.info("Building symbol table serially.") + symbol_table_builder = SymbolTableBuilder(project_dir, virtualenv) + files_processed = 0 + files_from_cache = 0 + + with ProgressBar(len(py_files), "Building symbol table") as progress: + for py_file in py_files: + file_key = str(py_file.relative_to(project_dir)) + + # Check if file is cached and unchanged + if file_key in cached_symbol_table and not options.rebuild_analysis: + if _file_unchanged(py_file, cached_symbol_table[file_key]): + symbol_table[file_key] = cached_symbol_table[file_key] + files_from_cache += 1 + progress.advance() + continue + + # File is new or changed, analyze it + try: + py_module = symbol_table_builder.build_pymodule_from_file(py_file) + symbol_table[file_key] = py_module + files_processed += 1 + except Exception as e: + logger.error(f"Failed to process {py_file}: {e}") + progress.advance() + + if files_from_cache > 0: + logger.info(f"Reused {files_from_cache} files from cache, processed {files_processed} new/changed files") + + if py_files and not symbol_table: + logger.error( + "Every one of the %d discovered Python files failed to process — " + "the symbol table is empty. This usually means the analysis " + "environment's interpreter is newer than the installed jedi/parso " + "stack supports (#107); check the per-file errors above.", + len(py_files), + ) + + logger.info( + "✅ Symbol table: %d modules in %.1fs", + len(symbol_table), time.perf_counter() - t0_st, + ) + return symbol_table diff --git a/test/test_pipeline.py b/test/test_pipeline.py index 15a7b97..0620c5b 100644 --- a/test/test_pipeline.py +++ b/test/test_pipeline.py @@ -23,3 +23,18 @@ def test_context_defaults(tmp_path): assert ctx.sig_to_id is None assert ctx.infos is None assert ctx.ir is None + + +from codeanalyzer.pipeline.symbol_table import build_symbol_table + + +def test_build_symbol_table_free_function(tmp_path): + proj = tmp_path / "proj" + proj.mkdir() + (proj / "m.py").write_text("def f():\n return 1\n", encoding="utf-8") + opts = AnalysisOptions( + input=proj, output=None, format=OutputFormat.JSON, + analysis_level=1, skip_tests=True, no_venv=True, + ) + table = build_symbol_table(proj, None, opts, cached_symbol_table={}) + assert "m.py" in table From 16e0a8114840e9c4a5a4ab7b4e355eba21fadfaa Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 11:47:54 -0400 Subject: [PATCH 06/14] refactor(pipeline): drop imports made dead by the symbol-table move --- codeanalyzer/core.py | 5 +---- codeanalyzer/pipeline/symbol_table.py | 3 ++- test/test_pipeline.py | 4 +--- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/codeanalyzer/core.py b/codeanalyzer/core.py index 67adb80..fc77967 100644 --- a/codeanalyzer/core.py +++ b/codeanalyzer/core.py @@ -4,7 +4,7 @@ import subprocess import sys from pathlib import Path -from typing import Any, Dict, Optional, Union, List +from typing import Any, Dict, Optional, List import time @@ -29,10 +29,7 @@ resolve_unresolved_constructors, ) from codeanalyzer.semantic_analysis.pycg import PyCG, PyCGExceptions -from codeanalyzer.syntactic_analysis.exceptions import SymbolTableBuilderRayError from codeanalyzer.syntactic_analysis.import_resolver import resolve_imports -from codeanalyzer.syntactic_analysis.symbol_table_builder import SymbolTableBuilder -from codeanalyzer.utils import ProgressBar from codeanalyzer.options import AnalysisOptions from codeanalyzer.provenance import analyzer_info, repository_info diff --git a/codeanalyzer/pipeline/symbol_table.py b/codeanalyzer/pipeline/symbol_table.py index 4b2ab46..0074f79 100644 --- a/codeanalyzer/pipeline/symbol_table.py +++ b/codeanalyzer/pipeline/symbol_table.py @@ -99,7 +99,8 @@ def build_symbol_table( project_dir: Root directory of the project. virtualenv: Path to the virtual environment directory, if any. options: Analysis configuration options. - cached_app: Previously cached PyApplication to reuse unchanged files + cached_symbol_table: Previously cached ``Dict[str, PyModule]`` symbol + table whose unchanged files are reused. Returns: Dict[str, PyModule]: A dictionary mapping file paths to PyModule objects. diff --git a/test/test_pipeline.py b/test/test_pipeline.py index 0620c5b..b89e035 100644 --- a/test/test_pipeline.py +++ b/test/test_pipeline.py @@ -3,6 +3,7 @@ from codeanalyzer.options import AnalysisOptions from codeanalyzer.config import OutputFormat from codeanalyzer.pipeline import AnalysisContext +from codeanalyzer.pipeline.symbol_table import build_symbol_table def _opts(tmp_path): @@ -25,9 +26,6 @@ def test_context_defaults(tmp_path): assert ctx.ir is None -from codeanalyzer.pipeline.symbol_table import build_symbol_table - - def test_build_symbol_table_free_function(tmp_path): proj = tmp_path / "proj" proj.mkdir() From 2af770079af08c43519dac1004bbc2f767a9a78c Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 11:51:36 -0400 Subject: [PATCH 07/14] refactor(pipeline): extract pycg + external-symbol helpers as free functions --- codeanalyzer/core.py | 53 +++------------------------------ codeanalyzer/pipeline/passes.py | 50 +++++++++++++++++++++++++++++++ test/test_pipeline.py | 16 ++++++++++ 3 files changed, 70 insertions(+), 49 deletions(-) create mode 100644 codeanalyzer/pipeline/passes.py diff --git a/codeanalyzer/core.py b/codeanalyzer/core.py index fc77967..dd33ec2 100644 --- a/codeanalyzer/core.py +++ b/codeanalyzer/core.py @@ -493,25 +493,8 @@ def __exit__(self, *args, **kwargs) -> None: @staticmethod def _home_external_symbols(app, app_id, sig_to_id): - """Home every call-graph endpoint that is not a declared class/callable - onto a ``can://…/@external//`` id (the keystone edge-endpoint - id home). Registers each homed id in ``sig_to_id`` so callee backfill and - call-graph re-identity map the dotted signature to it, and returns the - id-keyed external-symbol map. ``name``/``module`` are derived from the - signature (best effort: split on the last dot).""" - externals: Dict[str, PyExternalSymbol] = {} - for edge in app.call_graph: - for sig in (edge.src, edge.dst): - if sig in sig_to_id: - continue - module, name = sig.rsplit(".", 1) if "." in sig else (None, sig) - ext_id = f"{app_id}/@external/{module}/{name}" if module else \ - f"{app_id}/@external/{name}" - sig_to_id[sig] = ext_id - externals[ext_id] = PyExternalSymbol( - id=ext_id, name=name, module=module - ) - return externals + from codeanalyzer.pipeline.passes import home_external_symbols + return home_external_symbols(app, app_id, sig_to_id) def analyze(self) -> Analysis: """Analyze the project and return the v2 ``Analysis`` envelope. @@ -737,33 +720,5 @@ def _get_pycg_call_graph( symbol_table: Dict[str, PyModule], jedi_edges: List[PyCallEdge], ) -> List[PyCallEdge]: - """Build PyCG-resolved call edges. - - Runs PyCG's iterative name-pointer analysis over the whole project - and returns edges with ``prov=["pycg"]``. Falls back to an - empty list and logs a warning on any failure so the caller can - continue with Jedi-only edges. - - *jedi_edges* are the level-1 call edges; under the ``jedi`` shard - strategy they drive coupling-aware partitioning (see - :func:`shard_planner.plan_shards`). - """ - try: - pycg = PyCG( - self.project_dir, - skip_tests=self.skip_tests, - shard=self.options.pycg_shard, - shard_ceiling=self.options.pycg_shard_ceiling, - shard_timeout=self.options.pycg_shard_timeout, - shard_strategy=self.options.pycg_shard_strategy, - max_iter=self.options.pycg_max_iter, - using_ray=self.using_ray, - ) - return pycg.build_call_graph_edges(symbol_table, jedi_edges=jedi_edges) - except PyCGExceptions.PyCGImportError as exc: - logger.warning(f"PyCG not installed — level 2 edges will be Jedi-only: {exc}") - return [] - except PyCGExceptions.PyCGAnalysisError as exc: - logger.warning(f"PyCG analysis failed — level 2 edges will be Jedi-only: {exc}") - logger.debug("PyCG full traceback:", exc_info=True) - return [] \ No newline at end of file + from codeanalyzer.pipeline.passes import pycg_call_graph_edges + return pycg_call_graph_edges(self.project_dir, symbol_table, jedi_edges, self.options) \ No newline at end of file diff --git a/codeanalyzer/pipeline/passes.py b/codeanalyzer/pipeline/passes.py new file mode 100644 index 0000000..32de1e9 --- /dev/null +++ b/codeanalyzer/pipeline/passes.py @@ -0,0 +1,50 @@ +from pathlib import Path +from typing import Dict, List + +from codeanalyzer.options import AnalysisOptions +from codeanalyzer.schema import PyApplication, PyExternalSymbol +from codeanalyzer.schema.py_schema import PyCallEdge +from codeanalyzer.semantic_analysis.pycg import PyCG, PyCGExceptions +from codeanalyzer.utils import logger + + +def home_external_symbols(app, app_id, sig_to_id) -> Dict[str, PyExternalSymbol]: + """Home every call-graph endpoint that is not a declared callable onto a + ``can://…/@external//`` id. Moved verbatim from + ``Codeanalyzer._home_external_symbols`` (static method, no ``self``).""" + externals: Dict[str, PyExternalSymbol] = {} + for edge in app.call_graph: + for sig in (edge.src, edge.dst): + if sig in sig_to_id: + continue + module, name = sig.rsplit(".", 1) if "." in sig else (None, sig) + ext_id = f"{app_id}/@external/{module}/{name}" if module else \ + f"{app_id}/@external/{name}" + sig_to_id[sig] = ext_id + externals[ext_id] = PyExternalSymbol(id=ext_id, name=name, module=module) + return externals + + +def pycg_call_graph_edges(project_dir, symbol_table, jedi_edges, options) -> List[PyCallEdge]: + """Build PyCG-resolved call edges, degrading to Jedi-only on failure. Moved + from ``Codeanalyzer._get_pycg_call_graph`` (``self.X`` -> ``options.X`` / + ``project_dir``).""" + try: + pycg = PyCG( + project_dir, + skip_tests=options.skip_tests, + shard=options.pycg_shard, + shard_ceiling=options.pycg_shard_ceiling, + shard_timeout=options.pycg_shard_timeout, + shard_strategy=options.pycg_shard_strategy, + max_iter=options.pycg_max_iter, + using_ray=options.using_ray, + ) + return pycg.build_call_graph_edges(symbol_table, jedi_edges=jedi_edges) + except PyCGExceptions.PyCGImportError as exc: + logger.warning(f"PyCG not installed — level 2 edges will be Jedi-only: {exc}") + return [] + except PyCGExceptions.PyCGAnalysisError as exc: + logger.warning(f"PyCG analysis failed — level 2 edges will be Jedi-only: {exc}") + logger.debug("PyCG full traceback:", exc_info=True) + return [] diff --git a/test/test_pipeline.py b/test/test_pipeline.py index b89e035..7142759 100644 --- a/test/test_pipeline.py +++ b/test/test_pipeline.py @@ -36,3 +36,19 @@ def test_build_symbol_table_free_function(tmp_path): ) table = build_symbol_table(proj, None, opts, cached_symbol_table={}) assert "m.py" in table + + +from codeanalyzer.pipeline.passes import home_external_symbols +from codeanalyzer.schema import PyApplication + + +def test_home_external_symbols_homes_undeclared_endpoints(): + app = PyApplication.builder().symbol_table({}).call_graph([]).build() + app.id = "can://python/proj" + # a call edge whose endpoints are not declared callables + from codeanalyzer.schema.py_schema import PyCallEdge + app.call_graph = [PyCallEdge(src="a.b", dst="os.getcwd", prov=["jedi"], weight=1)] + sig_to_id = {} + externals = home_external_symbols(app, app.id, sig_to_id) + assert "can://python/proj/@external/os/getcwd" in externals + assert sig_to_id["os.getcwd"] == "can://python/proj/@external/os/getcwd" From 166a4eb1953456f3228ba8384eb7c56fc3bf3066 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 11:53:35 -0400 Subject: [PATCH 08/14] refactor(pipeline): drop imports made dead by the call-graph helpers move --- codeanalyzer/core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/codeanalyzer/core.py b/codeanalyzer/core.py index dd33ec2..7c192d8 100644 --- a/codeanalyzer/core.py +++ b/codeanalyzer/core.py @@ -12,7 +12,6 @@ from codeanalyzer.schema import ( Analysis, PyApplication, - PyExternalSymbol, PyModule, model_dump_json, model_validate_json, @@ -28,7 +27,6 @@ merge_edges, resolve_unresolved_constructors, ) -from codeanalyzer.semantic_analysis.pycg import PyCG, PyCGExceptions from codeanalyzer.syntactic_analysis.import_resolver import resolve_imports from codeanalyzer.options import AnalysisOptions from codeanalyzer.provenance import analyzer_info, repository_info From b08f44b073c1d546afcc657546047396974bb049 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 11:59:46 -0400 Subject: [PATCH 09/14] refactor(pipeline): type the call-graph helper signatures --- codeanalyzer/pipeline/passes.py | 8 ++++++-- test/test_pipeline.py | 8 +++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/codeanalyzer/pipeline/passes.py b/codeanalyzer/pipeline/passes.py index 32de1e9..d63017a 100644 --- a/codeanalyzer/pipeline/passes.py +++ b/codeanalyzer/pipeline/passes.py @@ -8,7 +8,9 @@ from codeanalyzer.utils import logger -def home_external_symbols(app, app_id, sig_to_id) -> Dict[str, PyExternalSymbol]: +def home_external_symbols( + app: PyApplication, app_id: str, sig_to_id: Dict[str, str] +) -> Dict[str, PyExternalSymbol]: """Home every call-graph endpoint that is not a declared callable onto a ``can://…/@external//`` id. Moved verbatim from ``Codeanalyzer._home_external_symbols`` (static method, no ``self``).""" @@ -25,7 +27,9 @@ def home_external_symbols(app, app_id, sig_to_id) -> Dict[str, PyExternalSymbol] return externals -def pycg_call_graph_edges(project_dir, symbol_table, jedi_edges, options) -> List[PyCallEdge]: +def pycg_call_graph_edges( + project_dir: Path, symbol_table, jedi_edges, options: AnalysisOptions +) -> List[PyCallEdge]: """Build PyCG-resolved call edges, degrading to Jedi-only on failure. Moved from ``Codeanalyzer._get_pycg_call_graph`` (``self.X`` -> ``options.X`` / ``project_dir``).""" diff --git a/test/test_pipeline.py b/test/test_pipeline.py index 7142759..317c8eb 100644 --- a/test/test_pipeline.py +++ b/test/test_pipeline.py @@ -3,7 +3,10 @@ from codeanalyzer.options import AnalysisOptions from codeanalyzer.config import OutputFormat from codeanalyzer.pipeline import AnalysisContext +from codeanalyzer.pipeline.passes import home_external_symbols from codeanalyzer.pipeline.symbol_table import build_symbol_table +from codeanalyzer.schema import PyApplication +from codeanalyzer.schema.py_schema import PyCallEdge def _opts(tmp_path): @@ -38,15 +41,10 @@ def test_build_symbol_table_free_function(tmp_path): assert "m.py" in table -from codeanalyzer.pipeline.passes import home_external_symbols -from codeanalyzer.schema import PyApplication - - def test_home_external_symbols_homes_undeclared_endpoints(): app = PyApplication.builder().symbol_table({}).call_graph([]).build() app.id = "can://python/proj" # a call edge whose endpoints are not declared callables - from codeanalyzer.schema.py_schema import PyCallEdge app.call_graph = [PyCallEdge(src="a.b", dst="os.getcwd", prov=["jedi"], weight=1)] sig_to_id = {} externals = home_external_symbols(app, app.id, sig_to_id) From 78cd504328ebf9eb9fb707e76333c730deb6d0c5 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 12:06:05 -0400 Subject: [PATCH 10/14] feat(pipeline): add the four analysis pass functions --- codeanalyzer/pipeline/passes.py | 83 +++++++++++++++++++++++++++++++++ test/test_pipeline.py | 59 +++++++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/codeanalyzer/pipeline/passes.py b/codeanalyzer/pipeline/passes.py index d63017a..c68867c 100644 --- a/codeanalyzer/pipeline/passes.py +++ b/codeanalyzer/pipeline/passes.py @@ -2,9 +2,21 @@ from typing import Dict, List from codeanalyzer.options import AnalysisOptions +from codeanalyzer.pipeline.context import AnalysisContext +from codeanalyzer.pipeline.symbol_table import build_symbol_table +from codeanalyzer.provenance import repository_info from codeanalyzer.schema import PyApplication, PyExternalSymbol +from codeanalyzer.schema.assign_ids import assign_ids +from codeanalyzer.schema.call_graph_ids import reidentify_call_graph +from codeanalyzer.schema.l1_body import populate_l1_body +from codeanalyzer.schema.l2_callees import backfill_callees from codeanalyzer.schema.py_schema import PyCallEdge +from codeanalyzer.semantic_analysis.call_graph import ( + filter_external_edges, jedi_call_graph_edges, merge_edges, + resolve_unresolved_constructors, +) from codeanalyzer.semantic_analysis.pycg import PyCG, PyCGExceptions +from codeanalyzer.syntactic_analysis.import_resolver import resolve_imports from codeanalyzer.utils import logger @@ -52,3 +64,74 @@ def pycg_call_graph_edges( logger.warning(f"PyCG analysis failed — level 2 edges will be Jedi-only: {exc}") logger.debug("PyCG full traceback:", exc_info=True) return [] + + +def _pass_symbol_table(ctx: AnalysisContext) -> None: + symbol_table = build_symbol_table( + ctx.project_dir, ctx.virtualenv, ctx.options, ctx.cached_symbol_table + ) + resolve_unresolved_constructors(symbol_table) + ctx.symbol_table = symbol_table + + +def _pass_call_graph(ctx: AnalysisContext) -> None: + assert ctx.symbol_table is not None, "call_graph pass requires the symbol-table pass" + st = ctx.symbol_table + + call_graph = list(jedi_call_graph_edges(st)) + if ctx.analysis_level >= 2: + pycg_edges = pycg_call_graph_edges( + ctx.project_dir, st, call_graph, ctx.options + ) + call_graph = merge_edges(call_graph, pycg_edges) + call_graph = filter_external_edges(call_graph, st) + call_graph.sort(key=lambda e: (e.src, e.dst)) + + app = PyApplication.builder().symbol_table(st).call_graph(call_graph).build() + resolve_imports(app, ctx.project_dir) + app.repository = repository_info(ctx.project_dir) + + sig_to_id = assign_ids(app, ctx.app_name) + app.external_symbols = home_external_symbols(app, app.id, sig_to_id) + populate_l1_body(app) + if ctx.analysis_level >= 2: + backfill_callees(app, sig_to_id) + reidentify_call_graph(app, sig_to_id) + + ctx.app = app + ctx.sig_to_id = sig_to_id + + +def _pass_intraproc_dataflow(ctx: AnalysisContext) -> None: + assert ctx.app is not None and ctx.sig_to_id is not None, \ + "intraproc dataflow pass requires the call-graph pass" + from codeanalyzer.dataflow.builder import build_function_pdgs, emit_l3_body + from codeanalyzer.dataflow.syntactic import SyntacticOracle + + infos, _func_asts = build_function_pdgs( + ctx.app, + k=ctx.options.graph_field_depth, + oracle_factory=lambda c, fast: SyntacticOracle(), + ) + emit_l3_body(ctx.app, infos, ctx.sig_to_id, set(ctx.options.graphs.split(","))) + ctx.infos = infos + + +def _pass_interproc_dataflow(ctx: AnalysisContext) -> None: + assert ctx.app is not None and ctx.sig_to_id is not None, \ + "interproc dataflow pass requires the call-graph pass" + assert ctx.infos is not None, \ + "interproc dataflow pass requires the intraproc pass (it reuses its PDGs)" + from codeanalyzer.dataflow.builder import ( + _base_types, build_program_graphs, emit_ddg_pointsto_delta, emit_l4, + ) + from codeanalyzer.dataflow.scalpel_oracle import make_alias_oracle + + ir = build_program_graphs( + ctx.app, + k=ctx.options.graph_field_depth, + oracle_factory=lambda c, fast: make_alias_oracle(c, fast, _base_types(c)), + ) + emit_l4(ctx.app, ir, ctx.sig_to_id) + emit_ddg_pointsto_delta(ctx.app, ctx.infos, ir, ctx.sig_to_id) + ctx.ir = ir diff --git a/test/test_pipeline.py b/test/test_pipeline.py index 317c8eb..923bf8a 100644 --- a/test/test_pipeline.py +++ b/test/test_pipeline.py @@ -1,5 +1,7 @@ from pathlib import Path +import pytest + from codeanalyzer.options import AnalysisOptions from codeanalyzer.config import OutputFormat from codeanalyzer.pipeline import AnalysisContext @@ -50,3 +52,60 @@ def test_home_external_symbols_homes_undeclared_endpoints(): externals = home_external_symbols(app, app.id, sig_to_id) assert "can://python/proj/@external/os/getcwd" in externals assert sig_to_id["os.getcwd"] == "can://python/proj/@external/os/getcwd" + + +from codeanalyzer.pipeline.passes import ( + _pass_symbol_table, _pass_call_graph, + _pass_intraproc_dataflow, _pass_interproc_dataflow, +) + + +def _ctx(tmp_path, level, src="def g():\n return 1\ndef f():\n return g()\n"): + proj = tmp_path / "proj" + proj.mkdir() + (proj / "m.py").write_text(src, encoding="utf-8") + opts = AnalysisOptions( + input=proj, output=None, format=OutputFormat.JSON, + analysis_level=level, skip_tests=True, no_venv=True, + ) + return AnalysisContext( + options=opts, project_dir=proj, virtualenv=None, + analysis_level=level, app_name="proj", + ) + + +def test_pass_symbol_table_populates_symbol_table(tmp_path): + ctx = _ctx(tmp_path, 1) + _pass_symbol_table(ctx) + assert ctx.symbol_table is not None and "m.py" in ctx.symbol_table + + +def test_pass_call_graph_populates_app_and_ids(tmp_path): + ctx = _ctx(tmp_path, 1) + _pass_symbol_table(ctx) + _pass_call_graph(ctx) + assert ctx.app is not None and ctx.sig_to_id is not None + assert ctx.app.id.startswith("can://python/proj") + + +def test_pass_call_graph_requires_symbol_table(tmp_path): + ctx = _ctx(tmp_path, 1) + with pytest.raises(AssertionError): + _pass_call_graph(ctx) + + +def test_pass_interproc_requires_infos(tmp_path): + ctx = _ctx(tmp_path, 4) + _pass_symbol_table(ctx) + _pass_call_graph(ctx) + # intraproc pass deliberately skipped -> infos is still None + with pytest.raises(AssertionError): + _pass_interproc_dataflow(ctx) + + +def test_pass_intraproc_populates_infos(tmp_path): + ctx = _ctx(tmp_path, 3, src="def g(x):\n return x\ndef f(a):\n b = a\n g(b)\n return b\n") + _pass_symbol_table(ctx) + _pass_call_graph(ctx) + _pass_intraproc_dataflow(ctx) + assert ctx.infos is not None From 9c1d41cce31e0560afdcb5bf5be60b665f65115d Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 12:13:37 -0400 Subject: [PATCH 11/14] feat(pipeline): add the fluent AnalysisPipeline with self-gating passes --- codeanalyzer/pipeline/__init__.py | 3 +- codeanalyzer/pipeline/pipeline.py | 53 +++++++++++++++++++++++++++++++ test/test_pipeline.py | 47 +++++++++++++++++++++------ 3 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 codeanalyzer/pipeline/pipeline.py diff --git a/codeanalyzer/pipeline/__init__.py b/codeanalyzer/pipeline/__init__.py index 5fc0b9e..2f60387 100644 --- a/codeanalyzer/pipeline/__init__.py +++ b/codeanalyzer/pipeline/__init__.py @@ -1,3 +1,4 @@ from codeanalyzer.pipeline.context import AnalysisContext +from codeanalyzer.pipeline.pipeline import AnalysisPipeline -__all__ = ["AnalysisContext"] +__all__ = ["AnalysisContext", "AnalysisPipeline"] diff --git a/codeanalyzer/pipeline/pipeline.py b/codeanalyzer/pipeline/pipeline.py new file mode 100644 index 0000000..7c5d109 --- /dev/null +++ b/codeanalyzer/pipeline/pipeline.py @@ -0,0 +1,53 @@ +import time + +from codeanalyzer.pipeline.context import AnalysisContext +from codeanalyzer.pipeline.passes import ( + _pass_call_graph, _pass_interproc_dataflow, + _pass_intraproc_dataflow, _pass_symbol_table, +) +from codeanalyzer.provenance import analyzer_info +from codeanalyzer.schema import Analysis +from codeanalyzer.utils import logger + + +class AnalysisPipeline: + """Fluent chain of analysis passes over a shared AnalysisContext. + + Each ``.with_*`` runs one pass through the shared ``_run`` gate: a pass + below its intrinsic ``min_level`` is a logged no-op. ``.build()`` assembles + the ``Analysis`` envelope from the produced context. + """ + + def __init__(self, ctx: AnalysisContext): + self.ctx = ctx + + def with_symbol_table(self): + return self._run("symbol_table", 1, _pass_symbol_table) + + def with_call_graph(self): + return self._run("call_graph", 1, _pass_call_graph) + + def with_intraproc_dataflow(self): + return self._run("intraproc_dataflow", 3, _pass_intraproc_dataflow) + + def with_interproc_dataflow(self): + return self._run("interproc_dataflow", 4, _pass_interproc_dataflow) + + def _run(self, name, min_level, fn): + if self.ctx.analysis_level < min_level: + logger.info("⏭️ %s: skipped (level %d < %d)", name, + self.ctx.analysis_level, min_level) + return self + t0 = time.perf_counter() + fn(self.ctx) + logger.info("✅ %s: %.1fs", name, time.perf_counter() - t0) + return self + + def build(self) -> Analysis: + return Analysis( + max_level=self.ctx.analysis_level, + k_limit=self.ctx.options.graph_field_depth + if self.ctx.analysis_level >= 3 else None, + analyzer=analyzer_info(self.ctx.analysis_level), + application=self.ctx.app, + ) diff --git a/test/test_pipeline.py b/test/test_pipeline.py index 923bf8a..f2d3e4c 100644 --- a/test/test_pipeline.py +++ b/test/test_pipeline.py @@ -4,10 +4,14 @@ from codeanalyzer.options import AnalysisOptions from codeanalyzer.config import OutputFormat -from codeanalyzer.pipeline import AnalysisContext -from codeanalyzer.pipeline.passes import home_external_symbols +from codeanalyzer.pipeline import AnalysisContext, AnalysisPipeline +from codeanalyzer.pipeline.passes import ( + _pass_symbol_table, _pass_call_graph, + _pass_intraproc_dataflow, _pass_interproc_dataflow, + home_external_symbols, +) from codeanalyzer.pipeline.symbol_table import build_symbol_table -from codeanalyzer.schema import PyApplication +from codeanalyzer.schema import Analysis, PyApplication from codeanalyzer.schema.py_schema import PyCallEdge @@ -54,12 +58,6 @@ def test_home_external_symbols_homes_undeclared_endpoints(): assert sig_to_id["os.getcwd"] == "can://python/proj/@external/os/getcwd" -from codeanalyzer.pipeline.passes import ( - _pass_symbol_table, _pass_call_graph, - _pass_intraproc_dataflow, _pass_interproc_dataflow, -) - - def _ctx(tmp_path, level, src="def g():\n return 1\ndef f():\n return g()\n"): proj = tmp_path / "proj" proj.mkdir() @@ -109,3 +107,34 @@ def test_pass_intraproc_populates_infos(tmp_path): _pass_call_graph(ctx) _pass_intraproc_dataflow(ctx) assert ctx.infos is not None + + +def test_pipeline_gating_skips_dataflow_below_level(tmp_path): + ctx = _ctx(tmp_path, 2) + analysis = ( + AnalysisPipeline(ctx) + .with_symbol_table() + .with_call_graph() + .with_intraproc_dataflow() # min_level 3 -> skipped at level 2 + .with_interproc_dataflow() # min_level 4 -> skipped at level 2 + .build() + ) + assert isinstance(analysis, Analysis) + assert ctx.infos is None and ctx.ir is None # gates fired + assert analysis.max_level == 2 + assert analysis.k_limit is None # L3+ only + + +def test_pipeline_with_methods_return_self(tmp_path): + ctx = _ctx(tmp_path, 1) + pipe = AnalysisPipeline(ctx) + assert pipe.with_symbol_table() is pipe + + +def test_pipeline_level4_runs_intraproc_before_interproc(tmp_path): + ctx = _ctx(tmp_path, 4, src="def g(x):\n return x\ndef f(a):\n b = g(a)\n return b\n") + (AnalysisPipeline(ctx) + .with_symbol_table().with_call_graph() + .with_intraproc_dataflow().with_interproc_dataflow().build()) + assert ctx.infos is not None # L3 ran before L4 (reuse precondition held) + assert ctx.ir is not None From 38bbbe7bbc02743966a52904f4f479c8dd99fd33 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 12:19:00 -0400 Subject: [PATCH 12/14] test(pipeline): assert call-graph ran and all with_* return self --- test/test_pipeline.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/test_pipeline.py b/test/test_pipeline.py index f2d3e4c..0624b25 100644 --- a/test/test_pipeline.py +++ b/test/test_pipeline.py @@ -120,6 +120,7 @@ def test_pipeline_gating_skips_dataflow_below_level(tmp_path): .build() ) assert isinstance(analysis, Analysis) + assert ctx.app is not None and ctx.sig_to_id is not None # call-graph pass RAN assert ctx.infos is None and ctx.ir is None # gates fired assert analysis.max_level == 2 assert analysis.k_limit is None # L3+ only @@ -128,7 +129,14 @@ def test_pipeline_gating_skips_dataflow_below_level(tmp_path): def test_pipeline_with_methods_return_self(tmp_path): ctx = _ctx(tmp_path, 1) pipe = AnalysisPipeline(ctx) - assert pipe.with_symbol_table() is pipe + # both the run path (symbol_table/call_graph at L1) and the gated skip path + # (intraproc/interproc gated off at L1) return self + assert ( + pipe.with_symbol_table() + .with_call_graph() + .with_intraproc_dataflow() + .with_interproc_dataflow() + ) is pipe def test_pipeline_level4_runs_intraproc_before_interproc(tmp_path): From 7ac29dd7c27fb24cd6e92fb968717d7f14183a16 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 12:36:26 -0400 Subject: [PATCH 13/14] refactor(core): drive analyze() through the fluent AnalysisPipeline --- codeanalyzer/core.py | 165 +++--------------- codeanalyzer/dataflow/scalpel_oracle.py | 3 +- .../semantic_analysis/pycg/pycg_analysis.py | 2 +- test/test_env_interpreter.py | 6 +- 4 files changed, 29 insertions(+), 147 deletions(-) diff --git a/codeanalyzer/core.py b/codeanalyzer/core.py index 7c192d8..dcdf2ac 100644 --- a/codeanalyzer/core.py +++ b/codeanalyzer/core.py @@ -4,32 +4,17 @@ import subprocess import sys from pathlib import Path -from typing import Any, Dict, Optional, List - -import time +from typing import Optional, List from codeanalyzer.utils import logger from codeanalyzer.schema import ( Analysis, - PyApplication, - PyModule, model_dump_json, model_validate_json, ) -from codeanalyzer.schema.assign_ids import assign_ids -from codeanalyzer.schema.l1_body import populate_l1_body -from codeanalyzer.schema.l2_callees import backfill_callees -from codeanalyzer.schema.call_graph_ids import reidentify_call_graph -from codeanalyzer.schema.py_schema import PyCallEdge -from codeanalyzer.semantic_analysis.call_graph import ( - filter_external_edges, - jedi_call_graph_edges, - merge_edges, - resolve_unresolved_constructors, -) -from codeanalyzer.syntactic_analysis.import_resolver import resolve_imports from codeanalyzer.options import AnalysisOptions -from codeanalyzer.provenance import analyzer_info, repository_info +from codeanalyzer.pipeline import AnalysisContext, AnalysisPipeline +from codeanalyzer.provenance import analyzer_info class Codeanalyzer: """Core static analysis engine for Python projects. @@ -489,19 +474,14 @@ def __exit__(self, *args, **kwargs) -> None: logger.info(f"Clearing cache directory: {self.cache_dir}") shutil.rmtree(self.cache_dir) - @staticmethod - def _home_external_symbols(app, app_id, sig_to_id): - from codeanalyzer.pipeline.passes import home_external_symbols - return home_external_symbols(app, app_id, sig_to_id) - def analyze(self) -> Analysis: """Analyze the project and return the v2 ``Analysis`` envelope. - Uses caching to avoid re-analyzing unchanged files. + Loads any cache seed, runs the fluent AnalysisPipeline, and persists the + result. The per-level work lives in the pipeline passes. """ cache_file = self.cache_dir / "analysis_cache.json" - # Try to load existing cached analysis cached = None if not self.rebuild_analysis and cache_file.exists(): try: @@ -517,114 +497,25 @@ def analyze(self) -> Analysis: logger.info("Analysis cache written by a different analyzer version; rebuilding.") cached = None - # Build symbol table from cached application if available (if no available, the build a new one) - symbol_table = self._build_symbol_table(cached.application.symbol_table if cached else {}) - - resolve_unresolved_constructors(symbol_table) - - # Level 1: Jedi call graph. - t0_jedi = time.perf_counter() - jedi_edges = jedi_call_graph_edges(symbol_table) - call_graph = list(jedi_edges) - logger.info("✅ Jedi: %d edges in %.1fs", len(call_graph), time.perf_counter() - t0_jedi) - - if self.analysis_level >= 2: - # Level 2: also add PyCG edges. The Jedi edges double as the - # coupling graph that drives coupling-aware PyCG sharding. - pycg_edges = self._get_pycg_call_graph(symbol_table, jedi_edges) - call_graph = merge_edges(call_graph, pycg_edges) - - call_graph = filter_external_edges(call_graph, symbol_table) - # Canonical edge order: backend iteration order (PyCG dicts, Counter - # insertion) is not a contract — sort so identical edge SETS always - # serialize identically (issue #99 determinism gate), and so the - # external-symbol homing below assigns ids in a stable order. - call_graph.sort(key=lambda e: (e.src, e.dst)) - - # Recreate pyapplication - app = ( - PyApplication.builder() - .symbol_table(symbol_table) - .call_graph(call_graph) - .build() + ctx = AnalysisContext( + options=self.options, + project_dir=self.project_dir, + virtualenv=self.virtualenv, + analysis_level=self.analysis_level, + app_name=self.options.app_name or self.project_dir.name, + cached_symbol_table=cached.application.symbol_table if cached else {}, ) - # Every run re-resolves import spellings against the analyzed module - # set -- pure and cheap; cached modules from older caches default to - # resolved_module=None and get stamped here (issue #82). - resolve_imports(app, self.project_dir) - - # Single choke point for provenance: every produced app (fresh symbol - # table or reused-from-cache) passes through here before being cached - # or returned, so repository provenance always reflects *this* checkout - # even when the symbol table itself came from the on-disk cache. The - # analyzer identity rides the envelope below (keystone home). - app.repository = repository_info(self.project_dir) - - app_name = self.options.app_name or self.project_dir.name - sig_to_id = assign_ids(app, app_name) - # Home call-graph endpoints that are not declared in the symbol table - # (imported library / builtin members) onto @external ids once, so the - # JSON and Neo4j backends share one authoritative external-symbol set - # and every edge endpoint joins the id space (no dangling endpoints). - app.external_symbols = self._home_external_symbols(app, app.id, sig_to_id) - populate_l1_body(app) - if self.analysis_level >= 2: - backfill_callees(app, sig_to_id) - reidentify_call_graph(app, sig_to_id) - - # L3: intraprocedural dataflow (CFG/CDG/DDG) emitted onto the v2 tree. - if self.analysis_level >= 3: - from codeanalyzer.dataflow.builder import ( - build_function_pdgs, - emit_l3_body, - ) - from codeanalyzer.dataflow.syntactic import SyntacticOracle - - infos, _func_asts = build_function_pdgs( - app, - k=self.options.graph_field_depth, - oracle_factory=lambda c, fast: SyntacticOracle(), - ) - emit_l3_body(app, infos, sig_to_id, set(self.options.graphs.split(","))) - - # L4: interprocedural dataflow (param vertices + summary + param_in/out) - # layered on top of the L3 syntactic overlay (L3 ⊆ L4). Scalpel is the - # primary may-alias oracle, with the type-based total fallback. - if self.analysis_level >= 4: - from codeanalyzer.dataflow.builder import ( - _base_types, - build_program_graphs, - emit_ddg_pointsto_delta, - emit_l4, - ) - from codeanalyzer.dataflow.scalpel_oracle import make_alias_oracle - - ir = build_program_graphs( - app, - k=self.options.graph_field_depth, - oracle_factory=lambda c, fast: make_alias_oracle( - c, fast, _base_types(c) - ), - ) - emit_l4(app, ir, sig_to_id) - # Semantic ddg delta: the alias-derived def-use edges the real - # oracle adds beyond the L3 syntactic set, tagged prov=["points-to"]. - # ``infos`` are the syntactic (L3) PDGs from the >=3 block above. - emit_ddg_pointsto_delta(app, infos, ir, sig_to_id) - - # Build the v2 envelope, then persist it (the cache stores the full - # ``Analysis`` envelope so a reused cache round-trips schema_version). - # k_limit is an L3+ envelope key: below the dataflow levels it stays - # None and exclude_none drops it from the payload. - analysis = Analysis( - max_level=self.analysis_level, - k_limit=self.options.graph_field_depth if self.analysis_level >= 3 else None, - analyzer=analyzer_info(self.analysis_level), - application=app, + analysis = ( + AnalysisPipeline(ctx) + .with_symbol_table() + .with_call_graph() + .with_intraproc_dataflow() + .with_interproc_dataflow() + .build() ) - self._save_analysis_cache(analysis, cache_file) + self._save_analysis_cache(analysis, cache_file) return analysis @staticmethod @@ -705,18 +596,4 @@ def _compute_checksum(self, root: Path) -> str: sha256 = hashlib.sha256() for py_file in sorted(root.rglob("*.py")): sha256.update(py_file.read_bytes()) - return sha256.hexdigest() - - def _build_symbol_table(self, cached_symbol_table=None): - from codeanalyzer.pipeline.symbol_table import build_symbol_table - return build_symbol_table( - self.project_dir, self.virtualenv, self.options, cached_symbol_table or {} - ) - - def _get_pycg_call_graph( - self, - symbol_table: Dict[str, PyModule], - jedi_edges: List[PyCallEdge], - ) -> List[PyCallEdge]: - from codeanalyzer.pipeline.passes import pycg_call_graph_edges - return pycg_call_graph_edges(self.project_dir, symbol_table, jedi_edges, self.options) \ No newline at end of file + return sha256.hexdigest() \ No newline at end of file diff --git a/codeanalyzer/dataflow/scalpel_oracle.py b/codeanalyzer/dataflow/scalpel_oracle.py index 1e25325..f300939 100644 --- a/codeanalyzer/dataflow/scalpel_oracle.py +++ b/codeanalyzer/dataflow/scalpel_oracle.py @@ -253,7 +253,8 @@ def make_alias_oracle(pycallable, func_ast, base_types) -> object: Returns a :class:`ScalpelAliasOracle` when ``python-scalpel`` is importable *and* builds successfully on ``func_ast``; otherwise logs once (INFO) and returns a :class:`TypeBasedAliasOracle` over ``base_types``. Never raises — - mirrors how ``core._get_pycg_call_graph`` degrades on a missing/failed PyCG. + mirrors how ``pipeline.passes.pycg_call_graph_edges`` degrades on a + missing/failed PyCG. """ fallback = TypeBasedAliasOracle(base_types) try: diff --git a/codeanalyzer/semantic_analysis/pycg/pycg_analysis.py b/codeanalyzer/semantic_analysis/pycg/pycg_analysis.py index 3a19915..dbe5772 100644 --- a/codeanalyzer/semantic_analysis/pycg/pycg_analysis.py +++ b/codeanalyzer/semantic_analysis/pycg/pycg_analysis.py @@ -494,7 +494,7 @@ def _collect_entry_points(self) -> List[str]: if any(part in self._SKIP_DIRS for part in p.parts): continue # Skip test files using exact path-component matching, consistent - # with core.py's _build_symbol_table filter. Substring matching + # with pipeline.symbol_table.build_symbol_table filter. Substring matching # (e.g. "/test" in full_path_str) incorrectly excludes files in # paths like "test/fixtures/..." that are source files, not tests. rel_parts = p.relative_to(self.project_dir).parts diff --git a/test/test_env_interpreter.py b/test/test_env_interpreter.py index 68b1989..1aaa384 100644 --- a/test/test_env_interpreter.py +++ b/test/test_env_interpreter.py @@ -148,7 +148,11 @@ def boom(self, py_file): analyzer = Codeanalyzer(opts) monkeypatch.setattr(logging.getLogger("codeanalyzer"), "propagate", True) with caplog.at_level(logging.ERROR, logger="codeanalyzer"): - table = analyzer._build_symbol_table(cached_symbol_table={}) + from codeanalyzer.pipeline.symbol_table import build_symbol_table + table = build_symbol_table( + analyzer.project_dir, analyzer.virtualenv, analyzer.options, + cached_symbol_table={}, + ) assert table == {} assert any( "every" in r.getMessage().lower() or "all " in r.getMessage().lower() From 0a1072704ea1ab37dac03095bb0d35768e190487 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Wed, 22 Jul 2026 12:49:16 -0400 Subject: [PATCH 14/14] refactor(core): drop orphaned instance attrs; document L4 equivalence coverage --- codeanalyzer/core.py | 3 --- test/test_pipeline_equivalence.py | 12 ++++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/codeanalyzer/core.py b/codeanalyzer/core.py index dcdf2ac..8f87012 100644 --- a/codeanalyzer/core.py +++ b/codeanalyzer/core.py @@ -26,7 +26,6 @@ class Codeanalyzer: def __init__(self, options: AnalysisOptions) -> None: self.options = options self.project_dir = Path(options.input).resolve() - self.skip_tests = options.skip_tests self.analysis_level = options.analysis_level self.rebuild_analysis = options.rebuild_analysis self.no_venv = options.no_venv @@ -35,8 +34,6 @@ def __init__(self, options: AnalysisOptions) -> None: ) / ".codeanalyzer" self.clear_cache = options.clear_cache self.virtualenv: Optional[Path] = None - self.using_ray: bool = options.using_ray - self.file_name: Optional[Path] = options.file_name @staticmethod def _cmd_exec_helper( diff --git a/test/test_pipeline_equivalence.py b/test/test_pipeline_equivalence.py index 563297c..7ef81db 100644 --- a/test/test_pipeline_equivalence.py +++ b/test/test_pipeline_equivalence.py @@ -4,6 +4,15 @@ `repository_info` returns None and the output is deterministic), normalizes the one volatile field (`analyzer.version`), and compares against committed goldens. Regenerate with `REGEN=1 pytest test/test_pipeline_equivalence.py`. + +Coverage caveat: the `-a 4` goldens require the optional `python-scalpel` +dependency. When it is absent those cases `pytest.skip`, so in a scalpel-free +environment this gate enforces byte-identical output for **L1–L3 only**. L4 +behavior-preservation is instead guaranteed by (a) the interproc pass being a +verbatim relocation of the old `analyze()` L4 block, and (b) the structural L4 +suites `test_v2_l4.py` / `test_v2_l4_summary.py` / `test_dataflow_sdg.py` plus +the L4 ordering happy-path `test_pipeline_level4_runs_intraproc_before_interproc` +in `test_pipeline.py`. """ import json import os @@ -73,6 +82,9 @@ def _run(proj: Path, level: int) -> dict: @pytest.mark.parametrize("level", [1, 2, 3, 4]) def test_pipeline_output_matches_golden(tmp_path, fixture, level): if level == 4 and not _scalpel_available(): + # Optional dep absent -> skip. This means byte-identity here is enforced + # for L1-L3 only; L4 is covered by the structural suites noted in the + # module docstring. pytest.skip("L4 golden requires python-scalpel (optional soft dependency)") proj = tmp_path / fixture shutil.copytree(FIXTURE_ROOT / fixture, proj)