Repository navigation
Fix assert_trace matching other functions with the same decorator - #2552
Conversation
…orator Functions wrapped by the same decorator share one code object, so assert_trace matched a container created by any of them. Also compare closure variables of the frame with the searched function's closure. Fixes dry-python#876
Merging this PR will not alter performance1 note
Comparing |
sobolevn
left a comment
There was a problem hiding this comment.
please, add a test case where you compare objects that trigger an exception on __eq__
| if frame.f_code is not function_code: | ||
| return False | ||
|
|
||
| # Functions created by the same decorator share the same code object, |
There was a problem hiding this comment.
can we have a fast path here? what if they are not? comparing everything is slow and can cause different side-effects and other problems.
|
Thanks for the review! I added |
|
what about the fast path? :) |
|
Good point! I added a fast path: if the searched function has no closure, a matching code object is enough, so we return right away without reading the frame's locals. The closure comparison now only runs for wrappers that share a code object, which is exactly the decorator case. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2552 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 80 82 +2
Lines 2485 2692 +207
Branches 437 44 -393
==========================================
+ Hits 2485 2692 +207 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
I have made things!
Checklist
CHANGELOG.mdRelated issues
assert_tracedoes not work correctly with functions that use the same decorator #876assert_tracecompared the__code__of the function being searched for with the code object of each frame on the stack. Every wrapper produced by one decorator shares the same code object, soassert_trace(Success, func_one)passed when the container actually came fromfunc_two.Unwrapping to the inner function, the approach tried in the issue, breaks
@safe: theFailureis created in the wrapper after the inner function has already returned. Instead, when the code object matches, this change also checks that the frame's closure variables are the same objects as the searched function's closure cells. Each wrapper closes over its own wrapped function, so the wrappers can be told apart, and@safekeeps working. This works with and withoutfunctools.wraps. Closure cells that are still empty are skipped.Added tests: a function wrapped by a shared decorator is still found, and a different function with the same decorator no longer passes the check. The second test fails on
masterand passes with this change.make unitpasses, and so doruff,flake8andmypy.