forked from fabioz/PyDev.Debugger
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pydevd.py
2225 lines (1816 loc) · 90.9 KB
/
pydevd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
'''
Entry point module (keep at root):
This module starts the debugger.
'''
import sys # @NoMove
if sys.version_info[:2] < (2, 6):
raise RuntimeError('The PyDev.Debugger requires Python 2.6 onwards to be run. If you need to use an older Python version, use an older version of the debugger.')
import atexit
from collections import defaultdict
from contextlib import contextmanager
from functools import partial
import itertools
import os
import traceback
import weakref
from _pydev_bundle import fix_getpass
from _pydev_bundle import pydev_imports, pydev_log
from _pydev_bundle._pydev_filesystem_encoding import getfilesystemencoding
from _pydev_bundle.pydev_is_thread_alive import is_thread_alive
from _pydev_bundle.pydev_override import overrides
from _pydev_imps._pydev_saved_modules import thread
from _pydev_imps._pydev_saved_modules import threading
from _pydev_imps._pydev_saved_modules import time
from _pydevd_bundle import pydevd_extension_utils
from _pydevd_bundle.pydevd_filtering import FilesFiltering
from _pydevd_bundle import pydevd_io, pydevd_vm_type
from _pydevd_bundle import pydevd_utils
from _pydevd_bundle.pydevd_additional_thread_info import set_additional_thread_info
from _pydevd_bundle.pydevd_breakpoints import ExceptionBreakpoint, get_exception_breakpoint
from _pydevd_bundle.pydevd_comm_constants import (CMD_THREAD_SUSPEND, CMD_STEP_INTO, CMD_SET_BREAK,
CMD_STEP_INTO_MY_CODE, CMD_STEP_OVER, CMD_SMART_STEP_INTO, CMD_RUN_TO_LINE,
CMD_SET_NEXT_STATEMENT, CMD_STEP_RETURN, CMD_ADD_EXCEPTION_BREAK, CMD_STEP_RETURN_MY_CODE,
CMD_STEP_OVER_MY_CODE)
from _pydevd_bundle.pydevd_constants import (IS_JYTH_LESS25, IS_PYCHARM, get_thread_id, get_current_thread_id,
dict_keys, dict_iter_items, DebugInfoHolder, PYTHON_SUSPEND, STATE_SUSPEND, STATE_RUN, get_frame,
clear_cached_thread_id, INTERACTIVE_MODE_AVAILABLE, SHOW_DEBUG_INFO_ENV, IS_PY34_OR_GREATER, IS_PY2, NULL,
NO_FTRACE)
from _pydevd_bundle.pydevd_custom_frames import CustomFramesContainer, custom_frames_container_init
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE, PYDEV_FILE
from _pydevd_bundle.pydevd_extension_api import DebuggerEventHandler
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, remove_exception_from_frame
from _pydevd_bundle.pydevd_kill_all_pydevd_threads import kill_all_pydev_threads
from _pydevd_bundle.pydevd_net_command_factory_xml import NetCommandFactory
from _pydevd_bundle.pydevd_trace_dispatch import (
trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips, fix_top_level_trace_and_get_trace_func)
from _pydevd_bundle.pydevd_utils import save_main_module, is_current_thread_main_thread
from _pydevd_frame_eval.pydevd_frame_eval_main import (
frame_eval_func, dummy_trace_dispatch)
import pydev_ipython # @UnusedImport
from pydevd_concurrency_analyser.pydevd_concurrency_logger import ThreadingLogger, AsyncioLogger, send_message, cur_time
from pydevd_concurrency_analyser.pydevd_thread_wrappers import wrap_threads
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame, NORM_PATHS_AND_BASE_CONTAINER, get_abs_path_real_path_and_base_from_file
from pydevd_file_utils import get_fullname, rPath, get_package_dir
import pydevd_tracing
from _pydevd_bundle.pydevd_comm import InternalThreadCommand, InternalThreadCommandForAnyThread
from _pydevd_bundle.pydevd_comm import(InternalConsoleExec,
PyDBDaemonThread, _queue, ReaderThread, GetGlobalDebugger, get_global_debugger,
set_global_debugger, WriterThread, pydevd_log,
start_client, start_server, InternalGetBreakpointException, InternalSendCurrExceptionTrace,
InternalSendCurrExceptionTraceProceeded, run_as_pydevd_daemon_thread)
from _pydevd_bundle.pydevd_breakpoints import stop_on_unhandled_exception
from _pydevd_bundle.pydevd_collect_try_except_info import collect_try_except_info
from _pydevd_bundle.pydevd_suspended_frames import SuspendedFramesManager
__version_info__ = (1, 4, 0)
__version_info_str__ = []
for v in __version_info__:
__version_info_str__.append(str(v))
__version__ = '.'.join(__version_info_str__)
# IMPORTANT: pydevd_constants must be the 1st thing defined because it'll keep a reference to the original sys._getframe
def install_breakpointhook(pydevd_breakpointhook=None):
if pydevd_breakpointhook is None:
def pydevd_breakpointhook(*args, **kwargs):
hookname = os.getenv('PYTHONBREAKPOINT')
if (
hookname is not None
and len(hookname) > 0
and hasattr(sys, '__breakpointhook__')
and sys.__breakpointhook__ != pydevd_breakpointhook
):
sys.__breakpointhook__(*args, **kwargs)
else:
settrace(*args, **kwargs)
if sys.version_info[0:2] >= (3, 7):
# There are some choices on how to provide the breakpoint hook. Namely, we can provide a
# PYTHONBREAKPOINT which provides the import path for a method to be executed or we
# can override sys.breakpointhook.
# pydevd overrides sys.breakpointhook instead of providing an environment variable because
# it's possible that the debugger starts the user program but is not available in the
# PYTHONPATH (and would thus fail to be imported if PYTHONBREAKPOINT was set to pydevd.settrace).
# Note that the implementation still takes PYTHONBREAKPOINT in account (so, if it was provided
# by someone else, it'd still work).
sys.breakpointhook = pydevd_breakpointhook
else:
if sys.version_info[0] >= 3:
import builtins as __builtin__ # Py3 noqa
else:
import __builtin__ # noqa
# In older versions, breakpoint() isn't really available, so, install the hook directly
# in the builtins.
__builtin__.breakpoint = pydevd_breakpointhook
sys.__breakpointhook__ = pydevd_breakpointhook
# Install the breakpoint hook at import time.
install_breakpointhook()
SUPPORT_PLUGINS = not IS_JYTH_LESS25
PluginManager = None
if SUPPORT_PLUGINS:
from _pydevd_bundle.pydevd_plugin_utils import PluginManager
threadingEnumerate = threading.enumerate
threadingCurrentThread = threading.currentThread
try:
'dummy'.encode('utf-8') # Added because otherwise Jython 2.2.1 wasn't finding the encoding (if it wasn't loaded in the main thread).
except:
pass
connected = False
bufferStdOutToServer = False
bufferStdErrToServer = False
remote = False
forked = False
file_system_encoding = getfilesystemencoding()
#=======================================================================================================================
# PyDBCommandThread
#=======================================================================================================================
class PyDBCommandThread(PyDBDaemonThread):
def __init__(self, py_db):
PyDBDaemonThread.__init__(self)
self._py_db_command_thread_event = py_db._py_db_command_thread_event
self.py_db = py_db
self.setName('pydevd.CommandThread')
@overrides(PyDBDaemonThread._on_run)
def _on_run(self):
# Delay a bit this initialization to wait for the main program to start.
time.sleep(0.3)
if self.killReceived:
return
try:
while not self.killReceived:
try:
self.py_db.process_internal_commands()
except:
pydevd_log(0, 'Finishing debug communication...(2)')
self._py_db_command_thread_event.clear()
self._py_db_command_thread_event.wait(0.3)
except:
pydev_log.debug(sys.exc_info()[0])
# only got this error in interpreter shutdown
# pydevd_log(0, 'Finishing debug communication...(3)')
#=======================================================================================================================
# CheckOutputThread
# Non-daemon thread: guarantees that all data is written even if program is finished
#=======================================================================================================================
class CheckOutputThread(PyDBDaemonThread):
def __init__(self, py_db):
PyDBDaemonThread.__init__(self)
self.py_db = py_db
self.setName('pydevd.CheckAliveThread')
self.daemon = False
@overrides(PyDBDaemonThread._on_run)
def _on_run(self):
while not self.killReceived:
time.sleep(0.3)
if not self.py_db.has_threads_alive() and self.py_db.writer.empty():
try:
pydev_log.debug("No threads alive, finishing debug session")
self.py_db.finish_debugging_session()
kill_all_pydev_threads()
except:
traceback.print_exc()
self.killReceived = True
self.py_db.check_output_redirect()
def do_kill_pydev_thread(self):
self.killReceived = True
class AbstractSingleNotificationBehavior(object):
'''
The basic usage should be:
# Increment the request time for the suspend.
single_notification_behavior.increment_suspend_time()
# Notify that this is a pause request (when a pause, not a breakpoint).
single_notification_behavior.on_pause()
# Mark threads to be suspended.
set_suspend(...)
# On do_wait_suspend, use notify_thread_suspended:
def do_wait_suspend(...):
with single_notification_behavior.notify_thread_suspended(thread_id):
...
'''
__slots__ = [
'_last_resume_notification_time',
'_last_suspend_notification_time',
'_lock',
'_next_request_time',
'_suspend_time_request',
'_suspended_thread_ids',
'_pause_requested',
]
NOTIFY_OF_PAUSE_TIMEOUT = .5
def __init__(self):
self._next_request_time = partial(next, itertools.count())
self._last_suspend_notification_time = -1
self._last_resume_notification_time = -1
self._suspend_time_request = self._next_request_time()
self._lock = thread.allocate_lock()
self._suspended_thread_ids = set()
self._pause_requested = False
def send_suspend_notification(self, thread_id, stop_reason):
raise AssertionError('abstract: subclasses must override.')
def send_resume_notification(self, thread_id):
raise AssertionError('abstract: subclasses must override.')
def increment_suspend_time(self):
with self._lock:
self._suspend_time_request = self._next_request_time()
def on_pause(self):
# Upon a pause, we should force sending new suspend notifications
# if no notification is sent after some time and there's some thread already stopped.
with self._lock:
self._pause_requested = True
global_suspend_time = self._suspend_time_request
run_as_pydevd_daemon_thread(self._notify_after_timeout, global_suspend_time)
def _notify_after_timeout(self, global_suspend_time):
time.sleep(self.NOTIFY_OF_PAUSE_TIMEOUT)
with self._lock:
if self._suspended_thread_ids:
if global_suspend_time > self._last_suspend_notification_time:
self._last_suspend_notification_time = global_suspend_time
# Notify about any thread which is currently suspended.
self.send_suspend_notification(next(iter(self._suspended_thread_ids)), CMD_THREAD_SUSPEND)
@contextmanager
def notify_thread_suspended(self, thread_id, stop_reason):
with self._lock:
pause_requested = self._pause_requested
if pause_requested:
# When a suspend notification is sent, reset the pause flag.
self._pause_requested = False
self._suspended_thread_ids.add(thread_id)
# CMD_THREAD_SUSPEND should always be a side-effect of a break, so, only
# issue for a CMD_THREAD_SUSPEND if a pause is pending.
if stop_reason != CMD_THREAD_SUSPEND or pause_requested:
if self._suspend_time_request > self._last_suspend_notification_time:
self._last_suspend_notification_time = self._suspend_time_request
self.send_suspend_notification(thread_id, stop_reason)
try:
yield # At this point the thread must be actually suspended.
finally:
# on resume (step, continue all):
with self._lock:
self._suspended_thread_ids.remove(thread_id)
if self._last_resume_notification_time < self._last_suspend_notification_time:
self._last_resume_notification_time = self._last_suspend_notification_time
self.send_resume_notification(thread_id)
class ThreadsSuspendedSingleNotification(AbstractSingleNotificationBehavior):
__slots__ = AbstractSingleNotificationBehavior.__slots__ + [
'multi_threads_single_notification', '_py_db']
def __init__(self, py_db):
AbstractSingleNotificationBehavior.__init__(self)
# If True, pydevd will send a single notification when all threads are suspended/resumed.
self.multi_threads_single_notification = False
self._py_db = weakref.ref(py_db)
@overrides(AbstractSingleNotificationBehavior.send_resume_notification)
def send_resume_notification(self, thread_id):
py_db = self._py_db()
if py_db is not None:
py_db.writer.add_command(py_db.cmd_factory.make_thread_resume_single_notification(thread_id))
@overrides(AbstractSingleNotificationBehavior.send_suspend_notification)
def send_suspend_notification(self, thread_id, stop_reason):
py_db = self._py_db()
if py_db is not None:
py_db.writer.add_command(py_db.cmd_factory.make_thread_suspend_single_notification(thread_id, stop_reason))
@overrides(AbstractSingleNotificationBehavior.notify_thread_suspended)
@contextmanager
def notify_thread_suspended(self, thread_id, stop_reason):
if self.multi_threads_single_notification:
with AbstractSingleNotificationBehavior.notify_thread_suspended(self, thread_id, stop_reason):
yield
else:
yield
#=======================================================================================================================
# PyDB
#=======================================================================================================================
class PyDB(object):
""" Main debugging class
Lots of stuff going on here:
PyDB starts two threads on startup that connect to remote debugger (RDB)
The threads continuously read & write commands to RDB.
PyDB communicates with these threads through command queues.
Every RDB command is processed by calling process_net_command.
Every PyDB net command is sent to the net by posting NetCommand to WriterThread queue
Some commands need to be executed on the right thread (suspend/resume & friends)
These are placed on the internal command queue.
"""
def __init__(self, set_as_global=True):
if set_as_global:
set_global_debugger(self)
pydevd_tracing.replace_sys_set_trace_func()
self.reader = None
self.writer = None
self.output_checker_thread = None
self.py_db_command_thread = None
self.quitting = None
self.cmd_factory = NetCommandFactory()
self._cmd_queue = defaultdict(_queue.Queue) # Key is thread id or '*', value is Queue
self.suspended_frames_manager = SuspendedFramesManager()
self._files_filtering = FilesFiltering()
self.breakpoints = {}
# mtime to be raised when breakpoints change
self.mtime = 0
self.file_to_id_to_line_breakpoint = {}
self.file_to_id_to_plugin_breakpoint = {}
# Note: breakpoints dict should not be mutated: a copy should be created
# and later it should be assigned back (to prevent concurrency issues).
self.break_on_uncaught_exceptions = {}
self.break_on_caught_exceptions = {}
self.ready_to_run = False
self._main_lock = thread.allocate_lock()
self._lock_running_thread_ids = thread.allocate_lock()
self._py_db_command_thread_event = threading.Event()
if set_as_global:
CustomFramesContainer._py_db_command_thread_event = self._py_db_command_thread_event
self._finish_debugging_session = False
self._termination_event_set = False
self.signature_factory = None
self.SetTrace = pydevd_tracing.SetTrace
self.skip_on_exceptions_thrown_in_same_context = False
self.ignore_exceptions_thrown_in_lines_with_ignore_exception = True
# Suspend debugger even if breakpoint condition raises an exception.
# May be changed with CMD_PYDEVD_JSON_CONFIG.
self.skip_suspend_on_breakpoint_exception = () # By default suspend on any Exception.
self.skip_print_breakpoint_exception = () # By default print on any Exception.
# By default user can step into properties getter/setter/deleter methods
self.disable_property_trace = False
self.disable_property_getter_trace = False
self.disable_property_setter_trace = False
self.disable_property_deleter_trace = False
# this is a dict of thread ids pointing to thread ids. Whenever a command is passed to the java end that
# acknowledges that a thread was created, the thread id should be passed here -- and if at some time we do not
# find that thread alive anymore, we must remove it from this list and make the java side know that the thread
# was killed.
self._running_thread_ids = {}
self._set_breakpoints_with_id = False
# This attribute holds the file-> lines which have an @IgnoreException.
self.filename_to_lines_where_exceptions_are_ignored = {}
# working with plugins (lazily initialized)
self.plugin = None
self.has_plugin_line_breaks = False
self.has_plugin_exception_breaks = False
self.thread_analyser = None
self.asyncio_analyser = None
# matplotlib support in debugger and debug console
self.mpl_in_use = False
self.mpl_hooks_in_debug_console = False
self.mpl_modules_for_patching = {}
self._filename_to_not_in_scope = {}
self.first_breakpoint_reached = False
self._exclude_filters_enabled = self._files_filtering.use_exclude_filters()
self._is_libraries_filter_enabled = self._files_filtering.use_libraries_filter()
self.is_files_filter_enabled = self._exclude_filters_enabled or self._is_libraries_filter_enabled
self.show_return_values = False
self.remove_return_values_flag = False
self.redirect_output = False
# this flag disables frame evaluation even if it's available
self.use_frame_eval = True
self.stop_on_start = False
# If True, pydevd will send a single notification when all threads are suspended/resumed.
self._threads_suspended_single_notification = ThreadsSuspendedSingleNotification(self)
self._local_thread_trace_func = threading.local()
# Bind many locals to the debugger because upon teardown those names may become None
# in the namespace (and thus can't be relied upon unless the reference was previously
# saved).
self.trace_dispatch = partial(_trace_dispatch, self)
self.fix_top_level_trace_and_get_trace_func = fix_top_level_trace_and_get_trace_func
self.frame_eval_func = frame_eval_func
self.dummy_trace_dispatch = dummy_trace_dispatch
# Note: this is different from pydevd_constants.thread_get_ident because we want Jython
# to be None here because it also doesn't have threading._active.
try:
self.threading_get_ident = threading.get_ident # Python 3
self.threading_active = threading._active
except:
try:
self.threading_get_ident = threading._get_ident # Python 2 noqa
self.threading_active = threading._active
except:
self.threading_get_ident = None # Jython
self.threading_active = None
self.threading_current_thread = threading.currentThread
self.set_additional_thread_info = set_additional_thread_info
self.stop_on_unhandled_exception = stop_on_unhandled_exception
self.collect_try_except_info = collect_try_except_info
self.get_exception_breakpoint = get_exception_breakpoint
self._dont_trace_get_file_type = DONT_TRACE.get
self.PYDEV_FILE = PYDEV_FILE
self._in_project_scope_cache = {}
self._exclude_by_filter_cache = {}
self._apply_filter_cache = {}
def add_fake_frame(self, thread_id, frame_id, frame):
self.suspended_frames_manager.add_fake_frame(thread_id, frame_id, frame)
def handle_breakpoint_condition(self, info, pybreakpoint, new_frame):
condition = pybreakpoint.condition
try:
if pybreakpoint.handle_hit_condition(new_frame):
return True
if condition is None:
return False
return eval(condition, new_frame.f_globals, new_frame.f_locals)
except Exception as e:
if IS_PY2:
# Must be bytes on py2.
if isinstance(condition, unicode): # noqa
condition = condition.encode('utf-8')
if not isinstance(e, self.skip_print_breakpoint_exception):
sys.stderr.write('Error while evaluating expression: %s\n' % (condition,))
etype, value, tb = sys.exc_info()
traceback.print_exception(etype, value, tb.tb_next)
if not isinstance(e, self.skip_suspend_on_breakpoint_exception):
try:
# add exception_type and stacktrace into thread additional info
etype, value, tb = sys.exc_info()
error = ''.join(traceback.format_exception_only(etype, value))
stack = traceback.extract_stack(f=tb.tb_frame.f_back)
# On self.set_suspend(thread, CMD_SET_BREAK) this info will be
# sent to the client.
info.conditional_breakpoint_exception = \
('Condition:\n' + condition + '\n\nError:\n' + error, stack)
except:
traceback.print_exc()
return True
return False
finally:
etype, value, tb = None, None, None
def handle_breakpoint_expression(self, pybreakpoint, info, new_frame):
try:
try:
val = eval(pybreakpoint.expression, new_frame.f_globals, new_frame.f_locals)
except:
val = sys.exc_info()[1]
finally:
if val is not None:
info.pydev_message = str(val)
def get_file_type(self, abs_real_path_and_basename):
'''
:param abs_real_path_and_basename:
The result from get_abs_path_real_path_and_base_from_file or
get_abs_path_real_path_and_base_from_frame.
:return
_pydevd_bundle.pydevd_dont_trace_files.PYDEV_FILE:
If it's a file internal to the debugger which shouldn't be
traced nor shown to the user.
_pydevd_bundle.pydevd_dont_trace_files.LIB_FILE:
If it's a file in a library which shouldn't be traced.
None:
If it's a regular user file which should be traced.
'''
basename = abs_real_path_and_basename[-1]
if basename.startswith('<frozen '):
# In Python 3.7 "<frozen ..." appear multiple times during import and should be
# ignored for the user.
return self.PYDEV_FILE
return self._dont_trace_get_file_type(basename)
def get_thread_local_trace_func(self):
try:
thread_trace_func = self._local_thread_trace_func.thread_trace_func
except AttributeError:
thread_trace_func = self.trace_dispatch
return thread_trace_func
def enable_tracing(self, thread_trace_func=None):
'''
Enables tracing.
If in regular mode (tracing), will set the tracing function to the tracing
function for this thread -- by default it's `PyDB.trace_dispatch`, but after
`PyDB.enable_tracing` is called with a `thread_trace_func`, the given function will
be the default for the given thread.
'''
if self.frame_eval_func is not None:
self.frame_eval_func()
pydevd_tracing.SetTrace(self.dummy_trace_dispatch)
return
if thread_trace_func is None:
thread_trace_func = self.get_thread_local_trace_func()
else:
self._local_thread_trace_func.thread_trace_func = thread_trace_func
pydevd_tracing.SetTrace(thread_trace_func)
def disable_tracing(self):
pydevd_tracing.SetTrace(None)
def on_breakpoints_changed(self, removed=False):
'''
When breakpoints change, we have to re-evaluate all the assumptions we've made so far.
'''
if not self.ready_to_run:
# No need to do anything if we're still not running.
return
self.mtime += 1
if not removed:
# When removing breakpoints we can leave tracing as was, but if a breakpoint was added
# we have to reset the tracing for the existing functions to be re-evaluated.
self.set_tracing_for_untraced_contexts()
def set_tracing_for_untraced_contexts(self, ignore_current_thread=False):
# Enable the tracing for existing threads (because there may be frames being executed that
# are currently untraced).
ignore_thread = None
if ignore_current_thread:
ignore_thread = threading.current_thread()
threads = threadingEnumerate()
try:
for t in threads:
if getattr(t, 'is_pydev_daemon_thread', False) or t is ignore_thread or getattr(t, 'pydev_do_not_trace', False):
continue
additional_info = set_additional_thread_info(t)
frame = additional_info.get_topmost_frame(t)
try:
if frame is not None:
self.set_trace_for_frame_and_parents(frame)
finally:
frame = None
finally:
frame = None
t = None
threads = None
additional_info = None
@property
def multi_threads_single_notification(self):
return self._threads_suspended_single_notification.multi_threads_single_notification
@multi_threads_single_notification.setter
def multi_threads_single_notification(self, notify):
self._threads_suspended_single_notification.multi_threads_single_notification = notify
def get_plugin_lazy_init(self):
if self.plugin is None and SUPPORT_PLUGINS:
self.plugin = PluginManager(self)
return self.plugin
def in_project_scope(self, filename):
try:
return self._in_project_scope_cache[filename]
except KeyError:
cache = self._in_project_scope_cache
abs_real_path_and_basename = get_abs_path_real_path_and_base_from_file(filename)
# pydevd files are never considered to be in the project scope.
if self.get_file_type(abs_real_path_and_basename) == self.PYDEV_FILE:
cache[filename] = False
else:
cache[filename] = self._files_filtering.in_project_roots(filename)
return cache[filename]
def _clear_filters_caches(self):
self._in_project_scope_cache.clear()
self._exclude_by_filter_cache.clear()
self._apply_filter_cache.clear()
self._exclude_filters_enabled = self._files_filtering.use_exclude_filters()
self._is_libraries_filter_enabled = self._files_filtering.use_libraries_filter()
self.is_files_filter_enabled = self._exclude_filters_enabled or self._is_libraries_filter_enabled
def _exclude_by_filter(self, frame, filename):
'''
:param str filename:
The filename to filter.
:return: True if it should be excluded, False if it should be included and None
if no rule matched the given file.
'''
try:
return self._exclude_by_filter_cache[filename]
except KeyError:
cache = self._exclude_by_filter_cache
abs_real_path_and_basename = get_abs_path_real_path_and_base_from_file(filename)
# pydevd files are always filtered out
if self.get_file_type(abs_real_path_and_basename) == self.PYDEV_FILE:
cache[filename] = True
else:
module_name = None
if self._files_filtering.require_module:
module_name = frame.f_globals.get('__name__')
cache[filename] = self._files_filtering.exclude_by_filter(filename, module_name)
return cache[filename]
def apply_files_filter(self, frame, filename, force_check_project_scope):
'''
Should only be called if `self.is_files_filter_enabled == True`.
Note that it covers both the filter by specific paths includes/excludes as well
as the check which filters out libraries if not in the project scope.
:param force_check_project_scope:
Check that the file is in the project scope even if the global setting
is off.
:return bool:
True if it should be excluded when stepping and False if it should be
included.
'''
cache_key = (frame.f_code.co_firstlineno, frame.f_code.co_name, filename, force_check_project_scope)
try:
return self._apply_filter_cache[cache_key]
except KeyError:
if self.plugin is not None and self.has_plugin_line_breaks:
# If it's explicitly needed by some plugin, we can't skip it.
if self.plugin.can_not_skip(self, frame):
# print('include (include by plugins): %s' % filename)
self._apply_filter_cache[cache_key] = False
return False
if self._exclude_filters_enabled:
exclude_by_filter = self._exclude_by_filter(frame, filename)
if exclude_by_filter is not None:
if exclude_by_filter:
# ignore files matching stepping filters
# print('exclude (filtered out): %s' % filename)
self._apply_filter_cache[cache_key] = True
return True
else:
# print('include (explicitly included): %s' % filename)
self._apply_filter_cache[cache_key] = False
return False
if (self._is_libraries_filter_enabled or force_check_project_scope) and not self.in_project_scope(filename):
# print('exclude (not on project): %s' % filename)
# ignore library files while stepping
self._apply_filter_cache[cache_key] = True
return True
# print('include (on project): %s' % filename)
self._apply_filter_cache[cache_key] = False
return False
def is_exception_trace_in_project_scope(self, trace):
if trace is None or not self.in_project_scope(trace.tb_frame.f_code.co_filename):
return False
else:
trace = trace.tb_next
while trace is not None:
if not self.in_project_scope(trace.tb_frame.f_code.co_filename):
return False
trace = trace.tb_next
return True
def set_project_roots(self, project_roots):
self._files_filtering.set_project_roots(project_roots)
self._clear_skip_caches()
self._clear_filters_caches()
def set_exclude_filters(self, exclude_filters):
self._files_filtering.set_exclude_filters(exclude_filters)
self._clear_skip_caches()
self._clear_filters_caches()
def set_use_libraries_filter(self, use_libraries_filter):
self._files_filtering.set_use_libraries_filter(use_libraries_filter)
self._clear_skip_caches()
self._clear_filters_caches()
def has_threads_alive(self):
for t in pydevd_utils.get_non_pydevd_threads():
if isinstance(t, PyDBDaemonThread):
pydev_log.error_once(
'Error in debugger: Found PyDBDaemonThread not marked with is_pydev_daemon_thread=True.\n')
if is_thread_alive(t):
if not t.isDaemon() or hasattr(t, "__pydevd_main_thread"):
return True
return False
def finish_debugging_session(self):
self._finish_debugging_session = True
def initialize_network(self, sock):
try:
sock.settimeout(None) # infinite, no timeouts from now on - jython does not have it
except:
pass
self.writer = WriterThread(sock)
self.reader = ReaderThread(sock)
self.writer.start()
self.reader.start()
time.sleep(0.1) # give threads time to start
def connect(self, host, port):
if host:
s = start_client(host, port)
else:
s = start_server(port)
self.initialize_network(s)
def get_internal_queue(self, thread_id):
""" returns internal command queue for a given thread.
if new queue is created, notify the RDB about it """
if thread_id.startswith('__frame__'):
thread_id = thread_id[thread_id.rfind('|') + 1:]
return self._cmd_queue[thread_id]
def post_method_as_internal_command(self, thread_id, method, *args, **kwargs):
if thread_id == '*':
internal_cmd = InternalThreadCommandForAnyThread(thread_id, method, *args, **kwargs)
else:
internal_cmd = InternalThreadCommand(thread_id, method, *args, **kwargs)
self.post_internal_command(internal_cmd, thread_id)
def post_internal_command(self, int_cmd, thread_id):
""" if thread_id is *, post to the '*' queue"""
queue = self.get_internal_queue(thread_id)
queue.put(int_cmd)
def enable_output_redirection(self, redirect_stdout, redirect_stderr):
global bufferStdOutToServer
global bufferStdErrToServer
bufferStdOutToServer = redirect_stdout
bufferStdErrToServer = redirect_stderr
self.redirect_output = redirect_stdout or redirect_stderr
if bufferStdOutToServer:
init_stdout_redirect()
if bufferStdErrToServer:
init_stderr_redirect()
def check_output_redirect(self):
global bufferStdOutToServer
global bufferStdErrToServer
if bufferStdOutToServer:
init_stdout_redirect()
if bufferStdErrToServer:
init_stderr_redirect()
def init_matplotlib_in_debug_console(self):
# import hook and patches for matplotlib support in debug console
from _pydev_bundle.pydev_import_hook import import_hook_manager
if is_current_thread_main_thread():
for module in dict_keys(self.mpl_modules_for_patching):
import_hook_manager.add_module_name(module, self.mpl_modules_for_patching.pop(module))
def init_matplotlib_support(self):
# prepare debugger for integration with matplotlib GUI event loop
from pydev_ipython.matplotlibtools import activate_matplotlib, activate_pylab, activate_pyplot, do_enable_gui
# enable_gui_function in activate_matplotlib should be called in main thread. Unlike integrated console,
# in the debug console we have no interpreter instance with exec_queue, but we run this code in the main
# thread and can call it directly.
class _MatplotlibHelper:
_return_control_osc = False
def return_control():
# Some of the input hooks (e.g. Qt4Agg) check return control without doing
# a single operation, so we don't return True on every
# call when the debug hook is in place to allow the GUI to run
_MatplotlibHelper._return_control_osc = not _MatplotlibHelper._return_control_osc
return _MatplotlibHelper._return_control_osc
from pydev_ipython.inputhook import set_return_control_callback
set_return_control_callback(return_control)
self.mpl_modules_for_patching = {"matplotlib": lambda: activate_matplotlib(do_enable_gui),
"matplotlib.pyplot": activate_pyplot,
"pylab": activate_pylab }
def _activate_mpl_if_needed(self):
if len(self.mpl_modules_for_patching) > 0:
if is_current_thread_main_thread():
for module in dict_keys(self.mpl_modules_for_patching):
if module in sys.modules:
activate_function = self.mpl_modules_for_patching.pop(module)
activate_function()
self.mpl_in_use = True
def _call_mpl_hook(self):
try:
from pydev_ipython.inputhook import get_inputhook
inputhook = get_inputhook()
if inputhook:
inputhook()
except:
pass
def notify_thread_created(self, thread_id, thread, use_lock=True):
if self.writer is None:
# Protect about threads being created before the communication structure is in place
# (note that they will appear later on anyways as pydevd does reconcile live/dead threads
# when processing internal commands, albeit it may take longer and in general this should
# not be usual as it's expected that the debugger is live before other threads are created).
return
with self._lock_running_thread_ids if use_lock else NULL:
if thread_id in self._running_thread_ids:
return
additional_info = set_additional_thread_info(thread)
if additional_info.pydev_notify_kill:
# After we notify it should be killed, make sure we don't notify it's alive (on a racing condition
# this could happen as we may notify before the thread is stopped internally).
return
self._running_thread_ids[thread_id] = thread
self.writer.add_command(self.cmd_factory.make_thread_created_message(thread))
def notify_thread_not_alive(self, thread_id, use_lock=True):
""" if thread is not alive, cancel trace_dispatch processing """
if self.writer is None:
return
with self._lock_running_thread_ids if use_lock else NULL:
thread = self._running_thread_ids.pop(thread_id, None)
if thread is None:
return
was_notified = thread.additional_info.pydev_notify_kill
if not was_notified:
thread.additional_info.pydev_notify_kill = True
self.writer.add_command(self.cmd_factory.make_thread_killed_message(thread_id))
def process_internal_commands(self):
'''This function processes internal commands
'''
with self._main_lock:
self.check_output_redirect()
program_threads_alive = {}
all_threads = threadingEnumerate()
program_threads_dead = []
with self._lock_running_thread_ids:
reset_cache = not self._running_thread_ids
for t in all_threads:
if getattr(t, 'is_pydev_daemon_thread', False):
pass # I.e.: skip the DummyThreads created from pydev daemon threads
elif isinstance(t, PyDBDaemonThread):
pydev_log.error_once('Error in debugger: Found PyDBDaemonThread not marked with is_pydev_daemon_thread=True.\n')
elif is_thread_alive(t):
if reset_cache:
# Fix multiprocessing debug with breakpoints in both main and child processes
# (https://youtrack.jetbrains.com/issue/PY-17092) When the new process is created, the main
# thread in the new process already has the attribute 'pydevd_id', so the new thread doesn't
# get new id with its process number and the debugger loses access to both threads.
# Therefore we should update thread_id for every main thread in the new process.
clear_cached_thread_id(t)
thread_id = get_thread_id(t)
program_threads_alive[thread_id] = t
self.notify_thread_created(thread_id, t, use_lock=False)
# Compute and notify about threads which are no longer alive.
thread_ids = list(self._running_thread_ids.keys())
for thread_id in thread_ids:
if thread_id not in program_threads_alive:
program_threads_dead.append(thread_id)
for thread_id in program_threads_dead:
self.notify_thread_not_alive(thread_id, use_lock=False)
# Without self._lock_running_thread_ids
if len(program_threads_alive) == 0:
self.finish_debugging_session()
for t in all_threads:
if hasattr(t, 'do_kill_pydev_thread'):
t.do_kill_pydev_thread()
else:
# Actually process the commands now (make sure we don't have a lock for _lock_running_thread_ids
# acquired at this point as it could lead to a deadlock if some command evaluated tried to
# create a thread and wait for it -- which would try to notify about it getting that lock).
curr_thread_id = get_current_thread_id(threadingCurrentThread())
for thread_id in (curr_thread_id, '*'):
queue = self.get_internal_queue(thread_id)
# some commands must be processed by the thread itself... if that's the case,
# we will re-add the commands to the queue after executing.
cmds_to_add_back = []
try:
while True:
int_cmd = queue.get(False)
if not self.mpl_hooks_in_debug_console and isinstance(int_cmd, InternalConsoleExec):
# add import hooks for matplotlib patches if only debug console was started
try:
self.init_matplotlib_in_debug_console()
self.mpl_in_use = True
except:
pydevd_log(2, "Matplotlib support in debug console failed", traceback.format_exc())
self.mpl_hooks_in_debug_console = True
if int_cmd.can_be_executed_by(curr_thread_id):
pydevd_log(2, "processing internal command ", str(int_cmd))
int_cmd.do_it(self)
else:
pydevd_log(2, "NOT processing internal command ", str(int_cmd))