Skip to content

Debugger does not recognize that exception is handled in single line except statement scenario #404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
huguesv opened this issue Sep 4, 2020 · 5 comments
Assignees
Labels
bug Something isn't working

Comments

@huguesv
Copy link
Contributor

huguesv commented Sep 4, 2020

Environment data

  • debugpy version: 1.0.0rc2 (run import debugpy; print(debugpy.__version__) if uncertain)
  • OS and version: Windows
  • Python version (& distribution if applicable, e.g. Anaconda): 3.7 64-bit
  • Using VS Code or Visual Studio: Visual Studio

Actual behavior

Debugger breaks on exception when it shouldn't given the exception is handled. Break on thrown is 'off'.

Expected behavior

Debugger does not break on exception

Steps to reproduce:

  1. pip install beautifulsoup4 and lxml in Python 3.7
  2. debug this code:
from bs4 import BeautifulSoup

def has_cost(tag):
    try: class_names = tag.attrs['class']
    except: return False
    return True

shtml = "<html><head><title>The Dormouse's story</title></head>"
soup = BeautifulSoup(shtml, 'lxml')
soup_row_cost_arr = soup.find_all(has_cost)
print(soup_row_cost_arr)

result: breaks on exception thrown
image

change the implementation to have the return statement on its own line like this:

def has_cost(tag):
    try: class_names = tag.attrs['class']
    except: 
        return False
    return True

result: does not break, it correctly recognizes that it is handled

exception settings:
image

logs for both scenarios:
one-liner-breaks.txt
two-liner-no-break.txt

from developer community feedback at https://developercommunity.visualstudio.com/content/problem/1158181/python-exception-in-try-block-stops-in-debugger.html?childToView=1173634

@karthiknadig
Copy link
Member

/cc @fabioz

@int19h int19h added the bug Something isn't working label Sep 21, 2020
@fabioz
Copy link
Collaborator

fabioz commented Oct 1, 2020

I'll start to take a look at this.

@fabioz
Copy link
Collaborator

fabioz commented Oct 1, 2020

Note: from the logs it's possible to see that the filter is also for userUnhandled (besides the unhandled mode) i.e.:

{"type":"request","command":"setExceptionBreakpoints","arguments":
{"filters":[],"exceptionOptions":[
    {"breakMode":"userUnhandled","path":[{"names":["Python Exceptions"]}]},
    {"breakMode":"unhandled","path":[{"names":["Python Exceptions"]},
{"names":["GeneratorExit","IndexError","StopIteration","AttributeError","KeyError"]}]}]},"seq":12}

So, the correct thing is not to break in the user unhandled mode as it's a handled exception.

@fabioz
Copy link
Collaborator

fabioz commented Oct 2, 2020

I'm still working on this...

@int19h as this is an issue on user handled exceptions, maybe we should only enable those on a default build when this is fixed
(hopefully I'll be able to provide a fix today).

@fabioz
Copy link
Collaborator

fabioz commented Oct 2, 2020

Unfortunately I still haven't been able to fix it... let me try to give some feedback on why this happens and why this is tricky:

During Python tracing, Python does not provide a way to know whether we're exiting because of an exception or because of a return (it provides a tracing for the exception and another for the return), so, to differentiate we analyze the bytecode and obtain the information on where the try started and where's the except and possible raise statements.

With that the debugger uses some heuristics based on where the return of the function is being called. Until now, if it exited in the except line this meant that the exception was unhandled, but in this particular case, both the except and the return are at the same line, so, the debugger thinks that it's returning due to the unhandled exception in the except line instead of the return that's in the same line.

I still haven't discovered a really good way to differentiate here -- what I thought for now is checking the arg in the tracing during return (which apparently is always None on an exception, so, it'd fix this particular case) or trying to discover the list of exceptions that were handled (but the exception name could actually come from some call or an alias, in which case it's also not perfect).

So, I'm still investigating... I'll spend at most one more day checking if I can come up with a better approach, if I can't I'll probably do the pragmatic thing and just additionally check if the return value is really None -- this won't work in the case where the return on the except line actually returns None, but this is such an odd construct that I think it should be reasonable.

fabioz added a commit to fabioz/debugpy that referenced this issue Oct 8, 2020
fabioz added a commit to fabioz/debugpy that referenced this issue Oct 8, 2020
@fabioz fabioz closed this as completed in 9cda8e0 Oct 14, 2020
rchiodo pushed a commit to rchiodo/debugpy that referenced this issue Nov 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants