You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix#125: attribute decorator-argument uses to the decorated function
Names appearing inside a decorator's call arguments are visited at module
scope (Python evaluates decorator expressions at definition time), so the
resulting uses edges landed on the enclosing module only. Functions like
`@app.get("/x", dependencies=[Depends(Guard())]) def fn(): ...` showed no
edge to Depends or Guard — those hung off the module instead.
Mirror the existing treatment of default values: visit decorators in the
enclosing scope (unchanged), but also record which targets are touched and
re-emit the same uses edges from the decorated function's node. This
preserves the module-level edges (a decorator expression really does run
at import time) while adding the edges the call-graph reader expects.
Implementation: a small `_decorator_use_recorders` stack. `add_uses_edge`
appends the to_node to the top recorder regardless of whether the edge
itself is new — otherwise, if two functions in the same module share a
decorator, the second one would see an empty delta because the edge is
deduplicated. `visit_FunctionDef` pushes/pops the recorder around
`analyze_functiondef` and replays the captured targets once inside the
function scope.
Reported by @doctorgu (#125).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,9 @@
2
2
3
3
## 2.4.3 (in progress)
4
4
5
-
*No user-visible changes yet.*
5
+
### Bug fixes
6
+
7
+
-**Names referenced inside a decorator's arguments are now attributed to the decorated function**, not only to the enclosing module. Previously, a function decorated with e.g. `@app.get("/x", dependencies=[Depends(Guard())])` showed no uses of `Depends` or `Guard` — those edges landed on the module instead. The function now also gets a uses edge to each target referenced in its decorator arguments, mirroring the existing treatment of default values. (#125 — thanks @doctorgu)
0 commit comments