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
{{ message }}
Repository navigation
Add BRANCH_TAKEN and BRANCH_NOT_TAKEN events to sys.monitoring #122548
The interactions between BRANCH events and the BRANCH_TAKEN and BRANCH_NOT_TAKEN pair of events is going to be awkward to implement and unnecessarily inefficient, given there is no use case for combining the old and new approach.
So, here's the updated proposal:
Add BRANCH_TAKEN and BRANCH_NOT_TAKEN events to sys.monitoring
Deprecate the old BRANCH event (as it will be redundant)
Tools should use either the old BRANCH event, or the new BRANCH_TAKEN and BRANCH_NOT_TAKEN pair. Not both.
The behavior of BRANCH, BRANCH_TAKEN and BRANCH_NOT_TAKEN events is not defined if both are used.
This is still fully backwards compatible, and will allow us to implement BRANCH events on top of the new events without negatively impacting their performance.
If I try to just use the old BRANCH event, I get an error:
File "/Users/juan/project/slipcover/pep669.py", line 92, in enable_events
sys.monitoring.set_local_events(sys.monitoring.COVERAGE_ID, co,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
sys.monitoring.events.LINE|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
sys.monitoring.events.BRANCH)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: invalid local event set 0x40020
Since monitoring shouldn't impact performance when not used, we don't want to restrict the bytecode compiler in its optimizations, or how it lays out code.
With that in mind, I think we need to drop the "taken"/"not taken" labels and use something like "left" and "right".
That way there is less of an implication that a particular branch direction corresponds to a particular source construct.
In the code:
ifcond:
xy
It could be surprising if the branch from cond to x is marked "taken" rather than "not taken", but less surprising if it is marked "right" instead of "left".
I therefore proposed renaming BRANCH_NOT_TAKEN to BRANCH_LEFT and BRANCH_TAKEN to BRANCH_RIGHT.
The triples in co_branches will then be origin, left, right.
I therefore proposed renaming BRANCH_NOT_TAKEN to BRANCH_LEFT and BRANCH_TAKEN to BRANCH_RIGHT. The triples in co_branches will then be origin, left, right.
Ok! I've modified my code, so as far as I'm concerned, you can remove the taken/not_taken names.
There is no branch from line 1 to 2, the "left" branch is from line 1 to line 1.
Maybe first we should decide: from the user's point of view, does line 1 have two possible destinations (line 2 and line 3) or does it not? Should line 1 be considered a branch for the purposes of branch coverage?
If we can't map the collected data back to a user-understandable interpretation, then we haven't finished the feature. Can you help me puzzle through this? I'd like to get efficient branch coverage measurement. So far, we seem to be talking about different things (byte codes vs lines) and we aren't meeting in the middle.
Sorry, I am getting confused. I know I said "#3->4" was wrong, but now I'm thinking I need to see that branch reported. Shouldn't PEP626 require that it be reported?
PEP 626 says nothing about branches, just line events.
Since __debug__ is a constant, like True or False, there is no branch as far as the interpreter is concerned.
Without -O, if __debug__: is treated the same as if True:
Thanks, I see what you mean about the __debug__ constant being optimized away. I'm trying to figure out how to accommodate that. Coverage.py has a rudimentary understanding of this kind of optimization. For example, it knows that while True: isn't a branch, but thinks that while not False: is a branch. I'm not sure how to mirror Python's optimizations effectively...
Activity
markshannon commentedon Aug 1, 2024
The interactions between
BRANCHevents and theBRANCH_TAKENandBRANCH_NOT_TAKENpair of events is going to be awkward to implement and unnecessarily inefficient, given there is no use case for combining the old and new approach.So, here's the updated proposal:
BRANCH_TAKENandBRANCH_NOT_TAKENevents tosys.monitoringBRANCHevent (as it will be redundant)BRANCHevent, or the newBRANCH_TAKENandBRANCH_NOT_TAKENpair. Not both.BRANCH,BRANCH_TAKENandBRANCH_NOT_TAKENevents is not defined if both are used.This is still fully backwards compatible, and will allow us to implement
BRANCHevents on top of the new events without negatively impacting their performance.jaltmayerpizzorno commentedon Aug 14, 2024
If I try to just use the old
BRANCHevent, I get an error:markshannon commentedon Aug 15, 2024
Fixed.
In future, could you report issues with PRs on the PR? It gets hard to track them otherwise.
Thanks.
jaltmayerpizzorno commentedon Aug 15, 2024
Ah, sorry about that! Ironically, that's what I intended by reporting it here -- but it was the code in #122564 that I saw that on.
markshannon commentedon Aug 16, 2024
Since monitoring shouldn't impact performance when not used, we don't want to restrict the bytecode compiler in its optimizations, or how it lays out code.
With that in mind, I think we need to drop the "taken"/"not taken" labels and use something like "left" and "right".
That way there is less of an implication that a particular branch direction corresponds to a particular source construct.
In the code:
It could be surprising if the branch from
condtoxis marked "taken" rather than "not taken", but less surprising if it is marked "right" instead of "left".I therefore proposed renaming
BRANCH_NOT_TAKENtoBRANCH_LEFTandBRANCH_TAKENtoBRANCH_RIGHT.The triples in
co_brancheswill then beorigin, left, right.jaltmayerpizzorno commentedon Aug 16, 2024
Ok! I've modified my code, so as far as I'm concerned, you can remove the taken/not_taken names.
GH-122548: Implement branch taken and not taken events for sys.monito…
GH-122548: Correct magic number comment (GH-128115)
nedbat commentedon Dec 22, 2024
Continuing from #122564 (comment) and #122564 (comment)
Maybe first we should decide: from the user's point of view, does line 1 have two possible destinations (line 2 and line 3) or does it not? Should line 1 be considered a branch for the purposes of branch coverage?
If we can't map the collected data back to a user-understandable interpretation, then we haven't finished the feature. Can you help me puzzle through this? I'd like to get efficient branch coverage measurement. So far, we seem to be talking about different things (byte codes vs lines) and we aren't meeting in the middle.
pythonGH-122548: Implement branch taken and not taken events for sys.…
pythonGH-122548: Correct magic number comment (pythonGH-128115)
markshannon commentedon Dec 31, 2024
See #123050 (comment)
nedbat commentedon Jan 6, 2025
(Moved here from #128445 (comment))
Two more examples of branch event surprises. run_sysmon.py is used to see the sys.monitoring events. These are using commit cbe6c7c from PR #128445.
ifdebug.py
Disassembled:
Events:
The starred line is a BRANCH event from a NOT_TAKEN instruction.
1176_async.py
Disassembled:
Events:
Here there are no BRANCH events at all. I would expect some for the
async forline, analogous to how synchronousforis evented.pythonGH-122548: Implement branch taken and not taken events for sys.…
pythonGH-122548: Correct magic number comment (pythonGH-128115)
nedbat commentedon Feb 1, 2025
@markshannon Any word on the
if __debug__and async problems I mentioned above? Should they be new issues?markshannon commentedon Feb 3, 2025
if __debug__was fixed by #128533, I believe. Please let us know if you are still seeing issues.The
async forissue is tracked in #128534.Both issues are listed in the "tasks" section above.
nedbat commentedon Feb 4, 2025
Sorry, I am getting confused. I know I said "#3->4" was wrong, but now I'm thinking I need to see that branch reported. Shouldn't PEP626 require that it be reported?
markshannon commentedon Feb 6, 2025
PEP 626 says nothing about branches, just line events.
Since
__debug__is a constant, likeTrueorFalse, there is no branch as far as the interpreter is concerned.Without
-O,if __debug__:is treated the same asif True:nedbat commentedon Feb 7, 2025
Thanks, I see what you mean about the
__debug__constant being optimized away. I'm trying to figure out how to accommodate that. Coverage.py has a rudimentary understanding of this kind of optimization. For example, it knows thatwhile True:isn't a branch, but thinks thatwhile not False:is a branch. I'm not sure how to mirror Python's optimizations effectively...nedbat commentedon Feb 20, 2025
I think the only thing remaining before coverage.py can do efficient branch coverage is the
asyncissue. When should I expect an update?markshannon commentedon Feb 26, 2025
#130569 adds branch monitoring for
async forloops.markshannon commentedon Feb 27, 2025
@nedbat I think that's everything done.
Is there anything else you think is needed, either code or documentation?
nedbat commentedon Mar 3, 2025
I think we are set on the code. Not sure about the docs, TBH.
markshannon commentedon Jan 5, 2026
3.14 has been released with this feature