Skip to content

python: impact and path read framework_edge (#1509) - #1564

Merged
swapnilpaliwal-sd merged 1 commit into
0.1.3from
fix/1509-python-framework-edge
Sep 28, 2026
Merged

swapnilpaliwal-sd merged 1 commit into
0.1.3from
fix/1509-python-framework-edge

Conversation

@swapnilpaliwal-sd

Copy link
Copy Markdown
Contributor

Fixes #1509

The Python engine writes framework_edge for a hop no call site expresses: a task and its .delay() / .apply_async() producer, a signal send and its @receiver, a route table and its view, a Depends() default and its provider, and a test and the fixture it names. Nothing under plugins/ selected it. So impact on a receiver said the change was local, and path called the two ends independent. A @app.task with two .apply_async producers answered "nothing the graph can see" on a real project.

Change

  • impact
    • ext_framework_edge is exported as framework facts (axiomcode-impact, IMPACT_VERSION 27).
    • dl/impact.dl lists the end that hands over as a direct dependent with its own certainty, [framework], and the reason framework-mediated, not a call: <mechanism> via <detail> [<confidence>].
    • It ranks below [remote] and above every name match.
    • It is one direction only: a provider does not depend on the handler asking for it.
    • It is a direct row only and does not seed the closure, the contract [remote] keeps.
    • It counts in the delete verdict and in next:, and a note explains the label.
  • path
    • When no chain of calls connects two endpoints and a framework_edge row does, the hop is printed with its mechanism and confidence, and the answer no longer says "independent".
    • next: names both ends.
  • graph_sql.py: the hooks' fast path and the opt-in SQL arm list the same row, so the hook line and impact agree.

Tests

  • tests/cases/python/framework-hop-is-a-dependent has five construct checks (signal, .delay, .apply_async on a bind=True task, both path directions) and four controls.
    • An unrelated Animation.delay gains no framework row.
    • .delay() on an unregistered def joins nothing.
    • An unconnected pair is still independent.
    • An ordinary call beside the signal keeps its [resolved] row.
    • On 0.1.3 the five construct checks fail and the four controls pass. With this change it is 9/9.
  • graph/test/python/cases/29-framework-edge-consumers pins the rows the two verbs read: three framework_edge rows, and none for the Animation.delay or plain.delay sites.
  • bin/axiomcode test python 31/31, tests/run.py --lang python 65/65, tests/fastpath.py 4/4, and tiers, surfaces and manifests ok.

Measurement

Measured on 0.1.3 at 9d18792, cloned graphs, same graph for both arms (the engine is unchanged). Five dev projects (Django URLconf, FastAPI Depends, pytest-heavy libraries) and five held-out projects (two Django + task-queue apps, FastAPI, a REST framework library, a CLI). Up to 12 targets per mechanism per project.

pairs listed as a dependent, before after path "independent", before after
dev 0 of 216 (26 only by name) 216 of 216 24 of 24 0 of 24
held-out 0 of 503 (64 only by name) 503 of 503 33 of 41 2 of 41
  • The two held-out path misses are endpoints written as file:line on a module or class body that shares its line with another declaration. They resolve to that other declaration. Asked with the file endpoint, the hop is found.
  • Signal and task rows occur only in held-out projects. The rule reads the relation as written, and nothing in it was fitted to them.
  • No regression. Over every target on both sets:
    • no direct row was lost;
    • no row other than [framework] was added;
    • the reached set and the test set are unchanged;
    • none of 49 same-name controls gained a framework row.
  • Solve cost.
    • The median impact query on the largest task-queue project went 4.0 s to 3.6 s, and 2.3 s to 2.4 s on the project with the most framework_edge rows (1,682).
    • The rest move by at most 0.3 s on a loaded machine.
    • The new impact.dl compiles once and is then cached.

Not in this change

  • A task producer still also appears as a [by name] "names it as a value" row beside its [framework] row.
  • Animation.delay keeps its existing [by name] row for the send_report.delay(k) site.

The Python engine writes framework_edge for a hop no call site expresses: a
task and its .delay()/.apply_async() producer, a signal send and its
@receiver, a route table and its view, a Depends() default and its provider,
a test and the fixture it names. Nothing under plugins/ selected it, so
`impact` on a receiver said the change was local and `path` called the two
ends independent.

impact: ext_framework_edge is exported as `framework` facts and the end that
hands over is a direct dependent with its own certainty, `[framework]`, and
the reason "framework-mediated, not a call: <mechanism> via <detail>
[<confidence>]". It ranks below [remote] and above every name match. One
direction only, and a direct row only: it does not seed the closure, which is
the contract [remote] keeps. The hooks' SQL fast path and the opt-in SQL arm
list the same row, so the hook line and `impact` agree.

path: when no chain of calls connects two endpoints and a framework_edge row
does, the hop is printed with its mechanism and confidence, the closing line
no longer says "independent", and next: points at both ends.

Controls: an unrelated method sharing the dispatch name (Animation.delay)
gains no framework row, a .delay() on an unregistered def joins nothing, an
ordinary call beside the signal keeps its resolved row.
@swapnilpaliwal-sd
swapnilpaliwal-sd merged commit 16a492e into 0.1.3 Sep 28, 2026
11 checks passed
@swapnilpaliwal-sd
swapnilpaliwal-sd deleted the fix/1509-python-framework-edge branch September 28, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant