Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Debugger doesn't stop on uncaught exceptions in third party libraries #2027

Closed
andersea opened this issue Jan 7, 2020 · 11 comments
Closed

Comments

@andersea
Copy link

andersea commented Jan 7, 2020

Environment data

  • VS Code version: 1.41.1
  • Extension version (available under the Extensions sidebar): 2020.1.57204
  • OS and version: Windows 10 Version 1909
  • Python version (& distribution if applicable, e.g. Anaconda): 3.7.3 AMD 64
  • Type of virtual environment used (N/A | venv | virtualenv | conda | ...): venv with poetry
  • Relevant/affected Python packages and their versions: N/A
  • Jedi or Language Server? (i.e. what is "python.jediEnabled" set to; more info How to update the language server to the latest stable version vscode-python#3977): false (language server)

Additional settings:

In the debugger tab:
I have 'Raised Exceptions' unchecked.
I have 'Uncaught Exceptions' checked.
(As a side note, I think it is very unclear how these settings are supposed to interact. I can't see anything in any documentation anywhere.)

launch.json:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal"
        }
    ]
}

Expected behaviour

If an exception is raised in a third party library. Code should stop on the line of code that called into the third party library.

Actual behaviour

The exception is ignored by the debugger, causing the program to exit with an error and the exception to be printed to the terminal.

Steps to reproduce:

(0. I tested both sync and async versions. I use trio as async library, so this needs to be added to the venv.)

  1. I created a simple library with the following test functions. I used poetry with the command 'new' to create a new project, and in the project __init__.py file I added the following code.

__init__.py

__version__ = '0.1.2'

import trio
import time

async def aiter_thrower():
    i = 0
    while i < 5:
        yield i
        await trio.sleep(0.5)
        i += 1
    raise RuntimeError('Auch!')

async def afunc_thrower():
    await trio.sleep(0.5)
    raise RuntimeError('Auch!')

def iter_thrower():
    i = 0
    while i < 5:
        yield i
        time.sleep(0.5)
        i += 1
    raise RuntimeError('Auch!')

def func_thrower():
    time.sleep(0.5)
    raise RuntimeError('Auch!')
  1. I build the library using poetry build. This creates a wheel file in the dist subdir.

  2. I create another test project using poetry new.

  3. In the test project I add the wheel file using poetry add ..\test_library\dist\<name of wheel file>

  4. In the test project I add the following test code:

import trio
from async_test_lib import aiter_thrower, afunc_thrower, iter_thrower, func_thrower

async def test_afunc():
    await afunc_thrower()

async def test_aiter():
    async for x in aiter_thrower():
        print(f'Got {x}')

if __name__ == '__main__':
    print('Uncomment one of these sections in turn.')
    # Test aiter_thrower
#    trio.run(test_aiter)
    # Test afunc_thrower
#    trio.run(test_afunc)
    # Test func_thrower
#    func_thrower()
    # Test iter_thrower
#    for x in iter_thrower():
#        print(f'Got {x}')
  1. In turn, uncomment one of the sections to test each type of raised exception.

Logs

Output for Python in the Output panel (ViewOutput, change the drop-down the upper-right of the Output panel to Python)

(stream-trio-test-gbo-I5e8-py3.7) PS C:\Users\Anders\Documents\python\stream-trio-test> cd 'c:\Users\Anders\Documents\python\stream-trio-test'; ${env:PYTHONIOENCODING}='UTF-8'; ${env:PYTHONUNBUFFERED}='1'; & 'C:\Users\Anders\AppData\Local\pypoetry\Cache\virtualenvs\stream-trio-test-gbo-I5e8-py3.7\Scripts\python.exe' 'c:\Users\Anders\.vscode\extensions\ms-python.python-2020.1.57204\pythonFiles\ptvsd_launcher.py' '--default' '--client' 
'--host' 'localhost' '--port' '63052' 'c:\Users\Anders\Documents\python\stream-trio-test\test_asyinciter_except.py' 
Start
Got 0
Got 1
Got 2
Got 3
Got 4
Traceback (most recent call last):
  File "c:\Users\Anders\.vscode\extensions\ms-python.python-2020.1.57204\pythonFiles\ptvsd_launcher.py", line 43, in <module>
    main(ptvsdArgs)
  File "c:\Users\Anders\.vscode\extensions\ms-python.python-2020.1.57204\pythonFiles\lib\python\old_ptvsd\ptvsd\__main__.py", line 432, in main   
    run()
  File "c:\Users\Anders\.vscode\extensions\ms-python.python-2020.1.57204\pythonFiles\lib\python\old_ptvsd\ptvsd\__main__.py", line 316, in run_file
    runpy.run_path(target, run_name='__main__')
  File "C:\Users\Anders\AppData\Local\Programs\Python\Python37\lib\runpy.py", line 263, in run_path
RuntimeError: Auch!

Output from Console under the Developer Tools panel (toggle Developer Tools on under Help; turn on source maps to make any tracebacks be useful by running Enable source map support for extension debugging)


@karthiknadig
Copy link
Member

Try adding "justMyCode":false in your launch json. The debugger by default ignores anything that is outside of the current working directory.

@int19h
Copy link
Contributor

int19h commented Jan 8, 2020

This scenario should not be affected by that setting - it should report the exception the moment it crosses into user code.

It looks like async is what's causing the problem here.

@andersea
Copy link
Author

andersea commented Jan 8, 2020

The same thing happens with purely sync code.

Bad library:

__version__ = '0.1.0'

import time

def bad_func():
    print('Gone bad')
    time.sleep(0.5)
    raise RuntimeError('Bad!')

def bad_gen():
    print('Bad gen')
    yield 1
    time.sleep(0.5)
    yield 2
    time.sleep(0.5)
    print('This is going to hurt!')
    raise RuntimeError('That hurt!')

Build the library and install the wheel into another project, like I described in the first post.

Run a simple test function:

from bad_sync_lib import bad_func, bad_gen

print('Testing bad func')
bad_func()
print('We should never get to this place')

The debugger ignores the exception and the error is output into the terminal.

@karthiknadig thanks for the suggestion, but that is not what I am looking for. Using "justMyCode":false is designed to make the debugger halt inside the third party module code. That is not what I want. Like @int19h comments, it should stop as soon as it crosses into user code.

Essentially, what I expect can be illustrated with this example:

Screenshot of very simple code that does what I expect

You wouldn't expect it to suddenly jump into standard library code in this case? It should stop at the open() function call line. I would expect the same behaviour with third party modules.

@karthiknadig
Copy link
Member

@andersea Ah!. We addressed a bunch of async issues in the new version of the debugger. Can you try using the insiders version of the python extension (from here)? Add this to your user settings.json to enable the new debugger and reload:

    "python.experiments.optInto": [
        "DebugAdapterFactory - experiment",
        "PtvsdWheels37 - experiment"
    ]

The above steps should enable the new debugger. If everything works well, then when you start debugging in the command line it should have new_ptvsd in the path.

@karthiknadig karthiknadig self-assigned this Jan 8, 2020
@andersea
Copy link
Author

andersea commented Jan 8, 2020

Sorry, still doesn't work. As I mentioned, I don't think this is async related actually.

Console output:

(test-bad-sync-lib-dHqxARaN-py3.7) PS C:\Users\Anders\Documents\python\test_bad_sync_lib> ${env:PTVSD_LAUNCHER_PORT}='50514'; & 'C:\Users\Anders\AppData\Local\pypoetry\Cache\virtualenvs\test-bad-sync-lib-dHqxARaN-py3.7\Scripts\python.exe' 'c:\Users\Anders\.vscode\extensions\ms-python.python-2020.1.57377-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd\launcher' 'c:\Users\Anders\Documents\python\test_bad_sync_lib\test.py'
Try to open file that doesnt exist.
Gone bad
Traceback (most recent call last):
  File "C:\Users\Anders\AppData\Local\Programs\Python\Python37\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Users\Anders\AppData\Local\Programs\Python\Python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "c:\Users\Anders\.vscode\extensions\ms-python.python-2020.1.57377-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd\__main__.py", line 45, 
in <module>
    cli.main()
  File "c:\Users\Anders\.vscode\extensions\ms-python.python-2020.1.57377-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd/..\ptvsd\server\cli.py", line 361, in main
    run()
  File "c:\Users\Anders\.vscode\extensions\ms-python.python-2020.1.57377-dev\pythonFiles\lib\python\new_ptvsd\wheels\ptvsd/..\ptvsd\server\cli.py", line 203, in run_file
    runpy.run_path(options.target, run_name="__main__")
  File "C:\Users\Anders\AppData\Local\Programs\Python\Python37\lib\runpy.py", line 263, in run_path
    pkg_name=pkg_name, script_name=fname)
  File "C:\Users\Anders\AppData\Local\Programs\Python\Python37\lib\runpy.py", line 96, in _run_module_code
    mod_name, mod_spec, pkg_name, script_name)
  File "C:\Users\Anders\AppData\Local\Programs\Python\Python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "c:\Users\Anders\Documents\python\test_bad_sync_lib\test.py", line 5, in <module>
    bad_func()
  File "C:\Users\Anders\AppData\Local\pypoetry\Cache\virtualenvs\test-bad-sync-lib-dHqxARaN-py3.7\lib\site-packages\bad_sync_lib\__init__.py", line 8, in bad_func
    raise RuntimeError('Bad!')
RuntimeError: Bad!

This is using my second example library with only sync code, from my previous comment.

@andersea
Copy link
Author

andersea commented Jan 8, 2020

If I enable the option Raised exceptions -

option raised exceptions screenshot

then the example behaves pretty much as I expect, however this will come with the drawback of halting on any exception, no matter if I handle it or not. A lot of code in python uses exceptions as part of normal branching:

https://devblogs.microsoft.com/python/idiomatic-python-eafp-versus-lbyl/

So I open myself up to being flooded with exceptions that are expected and already handled, like in this screenshot:

Screenshot of debugger catching a handled exception

The uncaught exceptions option exists for exactly this purpose.

(Note that, although in the screenshot I write in the comment that I expect the debugger to stop, in the actual screenshot'ed code, it should not stop, since in this case I catch the error. It only stops because I am using the Raised exceptions option.)

@karthiknadig karthiknadig transferred this issue from microsoft/vscode-python Jan 8, 2020
@karthiknadig
Copy link
Member

@fabioz Can you look at this one?

@fabioz
Copy link
Contributor

fabioz commented Jan 8, 2020

Sure... I was actually taking a look at uncaught exceptions in third party libraries as a part #1946 (see my comment: #1946 (comment))... I'll double check to see if this use case is also fixed when I finish #1946.

@karthiknadig karthiknadig removed their assignment Jan 8, 2020
fabioz added a commit to fabioz/ptvsd that referenced this issue Jan 9, 2020
…icrosoft#2027

This commit also makes sure that unhandled exceptions raised
in libraries are shown if some frame from its stack is in user
code (when justMyCode:true).
fabioz added a commit to fabioz/ptvsd that referenced this issue Jan 9, 2020
…icrosoft#2027

This commit also makes sure that unhandled exceptions raised
in libraries are shown if some frame from its stack is in user
code (when justMyCode:true).
@fabioz fabioz closed this as completed in 0149646 Jan 10, 2020
@andersea
Copy link
Author

@fabioz @karthiknadig is this fix included in current vscode-python? Because it still doesn't work.

@int19h
Copy link
Contributor

int19h commented Feb 17, 2020

@andersea It should be included in ptvsd 5.0.0a12. You can check the version that you have with:

import ptvsd
print(ptvsd.__version__)

@andersea
Copy link
Author

andersea commented Feb 17, 2020

@int19h Thanks, I had version 4, so I deleted that, made sure the above optin section was in my settings.json and now it says 5.0.0a12. The test case appears to work now. Thanks.

Edit: I am using the insiders build of the extension, also.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants