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

Commit

Permalink
Debugger should not stop on GeneratorExit and StopIteration exceptions.
Browse files Browse the repository at this point in the history
Fixes #1959
  • Loading branch information
fabioz authored and int19h committed Dec 4, 2019
1 parent 299dfa7 commit 4df532c
Show file tree
Hide file tree
Showing 8 changed files with 1,413 additions and 1,275 deletions.
2,579 changes: 1,322 additions & 1,257 deletions src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_cython.c

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_cython.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,11 @@ cdef class PyDBFrame:
if exception is SystemExit and main_debugger.ignore_system_exit_code(value):
return False, frame

if exception in (GeneratorExit, StopIteration):
# These exceptions are control-flow related (they work as a generator
# pause), so, we shouldn't stop on them.
return False, frame

if exception_breakpoint.condition is not None:
eval_result = main_debugger.handle_breakpoint_condition(info, exception_breakpoint, frame)
if not eval_result:
Expand Down Expand Up @@ -726,7 +731,6 @@ cdef class PyDBFrame:
breakpoint = breakpoints_for_file[line]
new_frame = frame
stop = True
# print('step cmd', constant_to_str(step_cmd))
if step_cmd in (108, 159) and (stop_frame is frame and is_line):
stop = False # we don't stop on breakpoint if we have to stop by step-over (it will be processed later)
elif plugin_manager is not None and main_debugger.has_plugin_line_breaks:
Expand Down Expand Up @@ -777,7 +781,6 @@ cdef class PyDBFrame:
main_debugger.remove_return_values_flag = False

if stop:
# print('set suspend 1')
self.set_suspend(
thread,
111,
Expand All @@ -791,7 +794,6 @@ cdef class PyDBFrame:

# if thread has a suspend flag, we suspend with a busy wait
if info.pydev_state == 2:
# print(' -------- do wait suspend 1 -----------')
self.do_wait_suspend(thread, frame, event, arg)
return self.trace_dispatch
else:
Expand Down Expand Up @@ -891,7 +893,6 @@ cdef class PyDBFrame:
elif stop:
if is_line:
self.set_suspend(thread, step_cmd, original_step_cmd=info.pydev_original_step_cmd)
# print(' -------- do wait suspend 2 -----------')
self.do_wait_suspend(thread, frame, event, arg)
elif is_return: # return event
back = frame.f_back
Expand Down Expand Up @@ -921,7 +922,6 @@ cdef class PyDBFrame:
if back is not None:
# if we're in a return, we want it to appear to the user in the previous frame!
self.set_suspend(thread, step_cmd, original_step_cmd=info.pydev_original_step_cmd)
# print(' -------- do wait suspend 3 -----------')
self.do_wait_suspend(thread, back, event, arg)
else:
# in jython we may not have a back frame
Expand Down
5 changes: 5 additions & 0 deletions src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ def should_stop_on_exception(self, frame, event, arg):
if exception is SystemExit and main_debugger.ignore_system_exit_code(value):
return False, frame

if exception in (GeneratorExit, StopIteration):
# These exceptions are control-flow related (they work as a generator
# pause), so, we shouldn't stop on them.
return False, frame

if exception_breakpoint.condition is not None:
eval_result = main_debugger.handle_breakpoint_condition(info, exception_breakpoint, frame)
if not eval_result:
Expand Down
14 changes: 7 additions & 7 deletions src/ptvsd/_vendored/pydevd/build_tools/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def consume(it):
except StopIteration:
pass


def get_environment_from_batch_command(env_cmd, initial=None):
"""
Take a command (either a single command or list of arguments)
Expand Down Expand Up @@ -95,22 +96,21 @@ def build():

os.chdir(root_dir)

env=None
env = None
if sys.platform == 'win32':
# "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat"
# set MSSdk=1
# set DISTUTILS_USE_SDK=1
# set VS100COMNTOOLS=C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Tools


env = os.environ.copy()
if sys.version_info[:2] in ((2, 6), (2, 7), (3, 5), (3, 6), (3, 7)):
import setuptools # We have to import it first for the compiler to be found
if sys.version_info[:2] in ((2, 6), (2, 7), (3, 5), (3, 6), (3, 7), (3, 8)):
import setuptools # We have to import it first for the compiler to be found
from distutils import msvc9compiler

if sys.version_info[:2] in ((2, 6), (2, 7)):
vcvarsall = msvc9compiler.find_vcvarsall(9.0)
elif sys.version_info[:2] in ((3, 5), (3, 6), (3, 7)):
elif sys.version_info[:2] in ((3, 5), (3, 6), (3, 7), (3, 8)):
vcvarsall = msvc9compiler.find_vcvarsall(14.0)
if vcvarsall is None or not os.path.exists(vcvarsall):
raise RuntimeError('Error finding vcvarsall.')
Expand All @@ -124,7 +124,7 @@ def build():
[vcvarsall, 'x86'],
initial=os.environ.copy()))

elif sys.version_info[:2] in ((3,3), (3,4)):
elif sys.version_info[:2] in ((3, 3), (3, 4)):
if is_python_64bit():
env.update(get_environment_from_batch_command(
[r"C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd", '/x64'],
Expand Down Expand Up @@ -152,7 +152,7 @@ def build():

args = [
sys.executable, os.path.join(os.path.dirname(__file__), '..', 'setup_cython.py'), 'build_ext', '--inplace',
]+additional_args
] + additional_args
print('Calling args: %s' % (args,))
subprocess.check_call(args, env=env,)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import sys


def main():
import subprocess
process = subprocess.Popen(
'git status --porcelain'.split(), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
output, _ = process.communicate()
if output:
if sys.version_info[0] > 2:
output = output.decode('utf-8')

files = set()
for line in output.splitlines():
filename = line[3:]
files.add(filename.strip())

files.discard('.travis_install_python_deps.sh')
files.discard('miniconda.sh')
if files:
# If there are modifications, show a diff of the modifications and fail the script.
# (we're mostly interested in modifications to the .c generated files by cython).
print('Found modifications in git:\n%s ' % (output,))
print('Files: %s' % (files,))
print('----------- diff -------------')
subprocess.call('git diff'.split())
print('----------- end diff -------------')
sys.exit(1)


if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@ conda deactivate

cd /D x:\pydev\plugins\org.python.pydev.core\pysrc
set PYTHONPATH=x:\pydev\plugins\org.python.pydev.core\pysrc
C:\bin\Miniconda3\envs\py37_64\python build_tools\build.py

C:\bin\Python38-32\python build_tools\build.py

${ptvsd_folder}
cd /D X:\ptvsd_workspace\ptvsd\src\ptvsd\_vendored\pydevd
set PYTHONPATH=X:\ptvsd_workspace\ptvsd\src\ptvsd\_vendored\pydevd
C:\bin\Miniconda3\envs\py36_64\python build_tools\build.py
C:\bin\Python38-32\python build_tools\build.py


cd ~/Desktop/Pydev/plugins/org.python.pydev.core/pysrc
Expand All @@ -131,8 +131,8 @@ cd /D x:\PyDev.Debugger
set PYTHONPATH=x:\PyDev.Debugger
set MINICONDA_ENVS=C:\bin\Miniconda3\envs
conda deactivate
C:\bin\Miniconda3\envs\py36_64\python build_tools\build.py
C:\bin\Miniconda3\envs\py36_64\python build_tools\build_binaries_windows.py
C:\bin\Python38-32\python build_tools\build.py
C:\bin\Python38-32\python build_tools\build_binaries_windows.py

rm dist/pydevd*

Expand Down Expand Up @@ -179,6 +179,6 @@ dir dist
# Note: uploading with twine gives an error in the end, but apparently it works (check final result in pypi).
twine upload dist/pydevd*

git tag pydev_debugger_1_4_0 -a -m "PyDev.Debugger 1.4.0"
git tag pydev_debugger_1_8_0 -a -m "PyDev.Debugger 1.8.0"
git push --tags

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import sys
if __name__ == '__main__':

a = [1, 2, 3, 4]
b = [2, 7]

assert any(x in a for x in b)

def gen():
yield 1

for i in gen():
pass

def gen2():
yield 2
if sys.version_info[:2] < (3, 7):
# On Python 3.7 onwards this will generate an unhandled exception, which
# is not what we want.
raise StopIteration()

for i in gen2():
pass

print('TEST SUCEEDED!')
11 changes: 11 additions & 0 deletions src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,17 @@ def test_case_json_change_breaks(case_setup):
writer.finished_ok = True


def test_case_handled_exception_no_break_on_generator(case_setup):
with case_setup.test_file('_debugger_case_ignore_exceptions.py') as writer:
json_facade = JsonFacade(writer)

json_facade.write_launch()
json_facade.write_set_exception_breakpoints(['raised'])
json_facade.write_make_initial_run()

writer.finished_ok = True


def test_case_handled_exception_breaks(case_setup):
with case_setup.test_file('_debugger_case_exceptions.py') as writer:
json_facade = JsonFacade(writer)
Expand Down

0 comments on commit 4df532c

Please sign in to comment.