FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
pythonVSCode/pythonFiles/PythonTools/visualstudio_py_debugger.py at master · vdt/pythonVSCode · GitHub
vdt
/
pythonVSCode
Public
forked from
DonJayamanne/pythonVSCode
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Pull requests
0
Actions
Projects
Wiki
Security and quality
0
Insights
Additional navigation options
Code
Pull requests
Actions
Projects
Wiki
Security and quality
Insights
Expand file tree
Breadcrumbs
pythonVSCode
/
pythonFiles
/
PythonTools
/
visualstudio_py_debugger.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
2714 lines (2295 loc) · 101 KB
Breadcrumbs
pythonVSCode
/
pythonFiles
/
PythonTools
/
visualstudio_py_debugger.py
Copy path
File metadata and controls
2714 lines (2295 loc) · 101 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
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
# Python Tools for Visual Studio
# Copyright(c) Microsoft Corporation
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the License); you may not use
# this file except in compliance with the License. You may obtain a copy of the
# License at http://www.apache.org/licenses/LICENSE-2.0
#
# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
# OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
# IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
# MERCHANTABLITY OR NON-INFRINGEMENT.
#
# See the Apache Version 2.0 License for specific language governing
# permissions and limitations under the License.
# With number of modifications by Don Jayamanne
from
__future__
import
with_statement
__author__
=
"Microsoft Corporation <ptvshelp@microsoft.com>"
__version__
=
"3.0.0.0"
# This module MUST NOT import threading in global scope. This is because in a direct (non-ptvsd)
# attach scenario, it is loaded on the injected debugger attach thread, and if threading module
# hasn't been loaded already, it will assume that the thread on which it is being loaded is the
# main thread. This will cause issues when the thread goes away after attach completes.
_threading
=
None
import
sys
import
ctypes
try
:
import
thread
except
ImportError
:
import
_thread
as
thread
import
socket
import
struct
import
weakref
import
traceback
import
types
import
bisect
from
os
import
path
import
ntpath
import
runpy
import
datetime
from
codecs
import
BOM_UTF8
try
:
# In the local attach scenario, visualstudio_py_util is injected into globals()
# by PyDebugAttach before loading this module, and cannot be imported.
_vspu
=
visualstudio_py_util
except
:
try
:
import
visualstudio_py_util
as
_vspu
except
ImportError
:
import
ptvsd
.
visualstudio_py_util
as
_vspu
to_bytes
=
_vspu
.
to_bytes
exec_file
=
_vspu
.
exec_file
exec_module
=
_vspu
.
exec_module
exec_code
=
_vspu
.
exec_code
read_bytes
=
_vspu
.
read_bytes
read_int
=
_vspu
.
read_int
read_string
=
_vspu
.
read_string
write_bytes
=
_vspu
.
write_bytes
write_int
=
_vspu
.
write_int
write_string
=
_vspu
.
write_string
safe_repr
=
_vspu
.
SafeRepr
()
try
:
# In the local attach scenario, visualstudio_py_repl is injected into globals()
# by PyDebugAttach before loading this module, and cannot be imported.
_vspr
=
visualstudio_py_repl
except
:
try
:
import
visualstudio_py_repl
as
_vspr
except
ImportError
:
import
ptvsd
.
visualstudio_py_repl
as
_vspr
try
:
import
stackless
except
ImportError
:
stackless
=
None
try
:
xrange
except
:
xrange
=
range
if
sys
.
platform
==
'cli'
:
import
clr
from
System
.
Runtime
.
CompilerServices
import
ConditionalWeakTable
IPY_SEEN_MODULES
=
ConditionalWeakTable
[
object
,
object
]()
# Import encodings early to avoid import on the debugger thread, which may cause deadlock
from
encodings
import
utf_8
# WARNING: Avoid imports beyond this point, specifically on the debugger thread, as this may cause
# deadlock where the debugger thread performs an import while a user thread has the import lock
# save start_new_thread so we can call it later, we'll intercept others calls to it.
debugger_dll_handle
=
None
DETACHED
=
True
def
thread_creator
(
func
,
args
,
kwargs
=
{},
*
extra_args
):
if
not
isinstance
(
args
,
tuple
):
# args is not a tuple. This may be because we have become bound to a
# class, which has offset our arguments by one.
if
isinstance
(
kwargs
,
tuple
):
func
,
args
=
args
,
kwargs
kwargs
=
extra_args
[
0
]
if
len
(
extra_args
)
>
0
else
{}
return
_start_new_thread
(
new_thread_wrapper
, (
func
,
args
,
kwargs
))
_start_new_thread
=
thread
.
start_new_thread
THREADS
=
{}
THREADS_LOCK
=
thread
.
allocate_lock
()
MODULES
=
[]
BREAK_ON_SYSTEMEXIT_ZERO
=
False
DEBUG_STDLIB
=
False
DJANGO_DEBUG
=
False
IGNORE_DJANGO_TEMPLATE_WARNINGS
=
False
# Py3k compat - alias unicode to str
try
:
unicode
except
:
unicode
=
str
# A value of a synthesized child. The string is passed through to the variable list, and type is not displayed at all.
class
SynthesizedValue
(
object
):
def
__init__
(
self
,
repr_value
=
''
,
len_value
=
None
):
self
.
repr_value
=
repr_value
self
.
len_value
=
len_value
def
__repr__
(
self
):
return
self
.
repr_value
def
__len__
(
self
):
return
self
.
len_value
# Specifies list of files not to debug. Can be extended by other modules
# (the REPL does this for $attach support and not stepping into the REPL).
DONT_DEBUG
=
[
path
.
normcase
(
__file__
),
path
.
normcase
(
_vspu
.
__file__
)]
if
sys
.
version_info
>=
(
3
,
3
):
DONT_DEBUG
.
append
(
path
.
normcase
(
'<frozen importlib._bootstrap>'
))
if
sys
.
version_info
>=
(
3
,
5
):
DONT_DEBUG
.
append
(
path
.
normcase
(
'<frozen importlib._bootstrap_external>'
))
# Contains information about all breakpoints in the process. Keys are line numbers on which
# there are breakpoints in any file, and values are dicts. For every line number, the
# corresponding dict contains all the breakpoints that fall on that line. The keys in that
# dict are tuples of the form (filename, breakpoint_id), each entry representing a single
# breakpoint, and values are BreakpointInfo objects.
#
# For example, given the following breakpoints:
#
# 1. In 'main.py' at line 10.
# 2. In 'main.py' at line 20.
# 3. In 'module.py' at line 10.
#
# the contents of BREAKPOINTS would be:
# {10: {('main.py', 1): ..., ('module.py', 3): ...}, 20: {('main.py', 2): ... }}
BREAKPOINTS
=
{}
# Contains information about all pending (i.e. not yet bound) breakpoints in the process.
# Elements are BreakpointInfo objects.
PENDING_BREAKPOINTS
=
set
()
# Must be in sync with enum PythonBreakpointConditionKind in PythonBreakpoint.cs
BREAKPOINT_CONDITION_ALWAYS
=
0
BREAKPOINT_CONDITION_WHEN_TRUE
=
1
BREAKPOINT_CONDITION_WHEN_CHANGED
=
2
# Must be in sync with enum PythonBreakpointPassCountKind in PythonBreakpoint.cs
BREAKPOINT_PASS_COUNT_ALWAYS
=
0
BREAKPOINT_PASS_COUNT_EVERY
=
1
BREAKPOINT_PASS_COUNT_WHEN_EQUAL
=
2
BREAKPOINT_PASS_COUNT_WHEN_EQUAL_OR_GREATER
=
3
## Begin modification by Don Jayamanne
DJANGO_VERSIONS_IDENTIFIED
=
False
IS_DJANGO18
=
False
IS_DJANGO19
=
False
IS_DJANGO19_OR_HIGHER
=
False
try
:
dict_contains
=
dict
.
has_key
except
:
try
:
#Py3k does not have has_key anymore, and older versions don't have __contains__
dict_contains
=
dict
.
__contains__
except
:
try
:
dict_contains
=
dict
.
has_key
except
NameError
:
def
dict_contains
(
d
,
key
):
return
d
.
has_key
(
key
)
## End modification by Don Jayamanne
class
BreakpointInfo
(
object
):
__slots__
=
[
'breakpoint_id'
,
'filename'
,
'lineno'
,
'condition_kind'
,
'condition'
,
'pass_count_kind'
,
'pass_count'
,
'is_bound'
,
'last_condition_value'
,
'hit_count'
]
# For "when changed" breakpoints, this is used as the initial value of last_condition_value,
# such that it is guaranteed to not compare equal to any other value that it will get later.
_DUMMY_LAST_VALUE
=
object
()
def
__init__
(
self
,
breakpoint_id
,
filename
,
lineno
,
condition_kind
,
condition
,
pass_count_kind
,
pass_count
):
self
.
breakpoint_id
=
breakpoint_id
self
.
filename
=
filename
self
.
lineno
=
lineno
self
.
condition_kind
=
condition_kind
self
.
condition
=
condition
self
.
pass_count_kind
=
pass_count_kind
self
.
pass_count
=
pass_count
self
.
is_bound
=
False
self
.
last_condition_value
=
BreakpointInfo
.
_DUMMY_LAST_VALUE
self
.
hit_count
=
0
@
staticmethod
def
find_by_id
(
breakpoint_id
):
for
line
,
bp_dict
in
BREAKPOINTS
.
items
():
for
(
filename
,
bp_id
),
bp
in
bp_dict
.
items
():
if
bp_id
==
breakpoint_id
:
return
bp
return
None
# lock for calling .send on the socket
send_lock
=
thread
.
allocate_lock
()
class
_SendLockContextManager
(
object
):
"""context manager for send lock. Handles both acquiring/releasing the
send lock as well as detaching the debugger if the remote process
is disconnected"""
def
__enter__
(
self
):
# mark that we're about to do socket I/O so we won't deliver
# debug events when we're debugging the standard library
cur_thread
=
get_thread_from_id
(
thread
.
get_ident
())
if
cur_thread
is
not
None
:
cur_thread
.
is_sending
=
True
send_lock
.
acquire
()
def
__exit__
(
self
,
exc_type
,
exc_value
,
tb
):
send_lock
.
release
()
# start sending debug events again
cur_thread
=
get_thread_from_id
(
thread
.
get_ident
())
if
cur_thread
is
not
None
:
cur_thread
.
is_sending
=
False
if
exc_type
is
not
None
:
detach_threads
()
detach_process
()
# swallow the exception, we're no longer debugging
return
True
_SendLockCtx
=
_SendLockContextManager
()
SEND_BREAK_COMPLETE
=
False
STEPPING_OUT
=
-
1
# first value, we decrement below this
STEPPING_NONE
=
0
STEPPING_BREAK
=
1
STEPPING_LAUNCH_BREAK
=
2
STEPPING_ATTACH_BREAK
=
3
STEPPING_INTO
=
4
STEPPING_OVER
=
5
# last value, we increment past this.
USER_STEPPING
=
(
STEPPING_OUT
,
STEPPING_INTO
,
STEPPING_OVER
)
FRAME_KIND_NONE
=
0
FRAME_KIND_PYTHON
=
1
FRAME_KIND_DJANGO
=
2
DJANGO_BUILTINS
=
{
'True'
:
True
,
'False'
:
False
,
'None'
:
None
}
PYTHON_EVALUATION_RESULT_REPR_KIND_NORMAL
=
0
# regular repr and hex repr (if applicable) for the evaluation result; length is len(result)
PYTHON_EVALUATION_RESULT_REPR_KIND_RAW
=
1
# repr is raw representation of the value - see TYPES_WITH_RAW_REPR; length is len(repr)
PYTHON_EVALUATION_RESULT_REPR_KIND_RAWLEN
=
2
# same as above, but only the length is reported, not the actual value
PYTHON_EVALUATION_RESULT_EXPANDABLE
=
1
PYTHON_EVALUATION_RESULT_METHOD_CALL
=
2
PYTHON_EVALUATION_RESULT_SIDE_EFFECTS
=
4
PYTHON_EVALUATION_RESULT_RAW
=
8
PYTHON_EVALUATION_RESULT_HAS_RAW_REPR
=
16
# Don't show attributes of these types if they come from the class (assume they are methods).
METHOD_TYPES
=
(
types
.
FunctionType
,
types
.
MethodType
,
types
.
BuiltinFunctionType
,
type
(
""
.
__repr__
),
# method-wrapper
)
# repr() for these types can be used as input for eval() to get the original value.
# float is intentionally not included because it is not always round-trippable (e.g inf, nan).
TYPES_WITH_ROUND_TRIPPING_REPR
=
set
((
type
(
None
),
int
,
bool
,
str
,
unicode
))
if
sys
.
version
[
0
]
==
'3'
:
TYPES_WITH_ROUND_TRIPPING_REPR
.
add
(
bytes
)
else
:
TYPES_WITH_ROUND_TRIPPING_REPR
.
add
(
long
)
# repr() for these types can be used as input for eval() to get the original value, provided that the same is true for all their elements.
COLLECTION_TYPES_WITH_ROUND_TRIPPING_REPR
=
set
((
tuple
,
list
,
set
,
frozenset
))
# eval(repr(x)), but optimized for common types for which it is known that result == x.
def
eval_repr
(
x
):
def
is_repr_round_tripping
(
x
):
# Do exact type checks here - subclasses can override __repr__.
if
type
(
x
)
in
TYPES_WITH_ROUND_TRIPPING_REPR
:
return
True
elif
type
(
x
)
in
COLLECTION_TYPES_WITH_ROUND_TRIPPING_REPR
:
# All standard sequence types are round-trippable if their elements are.
return
all
((
is_repr_round_tripping
(
item
)
for
item
in
x
))
else
:
return
False
if
is_repr_round_tripping
(
x
):
return
x
else
:
return
eval
(
repr
(
x
), {})
# key is type, value is function producing the raw repr
TYPES_WITH_RAW_REPR
=
{
unicode
: (
lambda
s
:
s
)
}
# bytearray is 2.6+
try
:
# getfilesystemencoding is used here because it effectively corresponds to the notion of "locale encoding":
# current ANSI codepage on Windows, LC_CTYPE on Linux, UTF-8 on OS X - which is exactly what we want.
TYPES_WITH_RAW_REPR
[
bytearray
]
=
lambda
b
:
b
.
decode
(
sys
.
getfilesystemencoding
(),
'ignore'
)
except
:
pass
if
sys
.
version
[
0
]
==
'3'
:
TYPES_WITH_RAW_REPR
[
bytes
]
=
TYPES_WITH_RAW_REPR
[
bytearray
]
else
:
TYPES_WITH_RAW_REPR
[
str
]
=
TYPES_WITH_RAW_REPR
[
unicode
]
if
sys
.
version
[
0
]
==
'3'
:
# work around a crashing bug on CPython 3.x where they take a hard stack overflow
# we'll never see this exception but it'll allow us to keep our try/except handler
# the same across all versions of Python
class
StackOverflowException
(
Exception
):
pass
else
:
StackOverflowException
=
RuntimeError
ASBR
=
to_bytes
(
'ASBR'
)
SETL
=
to_bytes
(
'SETL'
)
THRF
=
to_bytes
(
'THRF'
)
DETC
=
to_bytes
(
'DETC'
)
NEWT
=
to_bytes
(
'NEWT'
)
EXTT
=
to_bytes
(
'EXTT'
)
EXIT
=
to_bytes
(
'EXIT'
)
EXCP
=
to_bytes
(
'EXCP'
)
MODL
=
to_bytes
(
'MODL'
)
STPD
=
to_bytes
(
'STPD'
)
BRKS
=
to_bytes
(
'BRKS'
)
BRKF
=
to_bytes
(
'BRKF'
)
BRKH
=
to_bytes
(
'BRKH'
)
BRKC
=
to_bytes
(
'BRKC'
)
BKHC
=
to_bytes
(
'BKHC'
)
LOAD
=
to_bytes
(
'LOAD'
)
EXCE
=
to_bytes
(
'EXCE'
)
EXCR
=
to_bytes
(
'EXCR'
)
CHLD
=
to_bytes
(
'CHLD'
)
OUTP
=
to_bytes
(
'OUTP'
)
REQH
=
to_bytes
(
'REQH'
)
LAST
=
to_bytes
(
'LAST'
)
def
get_thread_from_id
(
id
):
THREADS_LOCK
.
acquire
()
try
:
return
THREADS
.
get
(
id
)
finally
:
THREADS_LOCK
.
release
()
def
should_send_frame
(
frame
):
return
(
frame
is
not
None
and
frame
.
f_code
not
in
DEBUG_ENTRYPOINTS
and
path
.
normcase
(
frame
.
f_code
.
co_filename
)
not
in
DONT_DEBUG
)
KNOWN_DIRECTORIES
=
set
((
None
,
''
))
KNOWN_ZIPS
=
set
()
def
is_file_in_zip
(
filename
):
parent
,
name
=
path
.
split
(
path
.
abspath
(
filename
))
if
parent
in
KNOWN_DIRECTORIES
:
return
False
elif
parent
in
KNOWN_ZIPS
:
return
True
elif
path
.
isdir
(
parent
):
KNOWN_DIRECTORIES
.
add
(
parent
)
return
False
else
:
KNOWN_ZIPS
.
add
(
parent
)
return
True
def
lookup_builtin
(
name
,
frame
):
try
:
return
frame
.
f_builtins
.
get
(
bits
)
except
:
# http://ironpython.codeplex.com/workitem/30908
builtins
=
frame
.
f_globals
[
'__builtins__'
]
if
not
isinstance
(
builtins
,
dict
):
builtins
=
builtins
.
__dict__
return
builtins
.
get
(
name
)
def
lookup_local
(
frame
,
name
):
bits
=
name
.
split
(
'.'
)
obj
=
frame
.
f_locals
.
get
(
bits
[
0
])
or
frame
.
f_globals
.
get
(
bits
[
0
])
or
lookup_builtin
(
bits
[
0
],
frame
)
bits
.
pop
(
0
)
while
bits
and
obj
is
not
None
and
type
(
obj
)
is
types
.
ModuleType
:
obj
=
getattr
(
obj
,
bits
.
pop
(
0
),
None
)
return
obj
if
sys
.
version_info
[
0
]
>=
3
:
_EXCEPTIONS_MODULE
=
'builtins'
else
:
_EXCEPTIONS_MODULE
=
'exceptions'
def
get_exception_name
(
exc_type
):
if
exc_type
.
__module__
==
_EXCEPTIONS_MODULE
:
return
exc_type
.
__name__
else
:
return
exc_type
.
__module__
+
'.'
+
exc_type
.
__name__
# These constants come from Visual Studio - enum_EXCEPTION_STATE
BREAK_MODE_NEVER
=
0
BREAK_MODE_ALWAYS
=
1
BREAK_MODE_UNHANDLED
=
32
BREAK_TYPE_NONE
=
0
BREAK_TYPE_UNHANDLED
=
1
BREAK_TYPE_HANDLED
=
2
class
ExceptionBreakInfo
(
object
):
BUILT_IN_HANDLERS
=
{
path
.
normcase
(
'<frozen importlib._bootstrap>'
): ((
None
,
None
,
'*'
),),
path
.
normcase
(
'build
\\
bdist.win32
\\
egg
\\
pkg_resources.py'
): ((
None
,
None
,
'*'
),),
path
.
normcase
(
'build
\\
bdist.win-amd64
\\
egg
\\
pkg_resources.py'
): ((
None
,
None
,
'*'
),),
}
def
__init__
(
self
):
self
.
default_mode
=
BREAK_MODE_UNHANDLED
self
.
break_on
=
{ }
self
.
handler_cache
=
dict
(
self
.
BUILT_IN_HANDLERS
)
self
.
handler_lock
=
thread
.
allocate_lock
()
self
.
add_exception
(
'exceptions.IndexError'
,
BREAK_MODE_NEVER
)
self
.
add_exception
(
'builtins.IndexError'
,
BREAK_MODE_NEVER
)
self
.
add_exception
(
'exceptions.KeyError'
,
BREAK_MODE_NEVER
)
self
.
add_exception
(
'builtins.KeyError'
,
BREAK_MODE_NEVER
)
self
.
add_exception
(
'exceptions.AttributeError'
,
BREAK_MODE_NEVER
)
self
.
add_exception
(
'builtins.AttributeError'
,
BREAK_MODE_NEVER
)
self
.
add_exception
(
'exceptions.StopIteration'
,
BREAK_MODE_NEVER
)
self
.
add_exception
(
'builtins.StopIteration'
,
BREAK_MODE_NEVER
)
self
.
add_exception
(
'exceptions.GeneratorExit'
,
BREAK_MODE_NEVER
)
self
.
add_exception
(
'builtins.GeneratorExit'
,
BREAK_MODE_NEVER
)
def
clear
(
self
):
self
.
default_mode
=
BREAK_MODE_UNHANDLED
self
.
break_on
.
clear
()
self
.
handler_cache
=
dict
(
self
.
BUILT_IN_HANDLERS
)
def
should_break
(
self
,
thread
,
ex_type
,
ex_value
,
trace
):
probe_stack
()
name
=
get_exception_name
(
ex_type
)
mode
=
self
.
break_on
.
get
(
name
,
self
.
default_mode
)
break_type
=
BREAK_TYPE_NONE
if
mode
&
BREAK_MODE_ALWAYS
:
if
self
.
is_handled
(
thread
,
ex_type
,
ex_value
,
trace
):
break_type
=
BREAK_TYPE_HANDLED
else
:
break_type
=
BREAK_TYPE_UNHANDLED
elif
(
mode
&
BREAK_MODE_UNHANDLED
)
and
not
self
.
is_handled
(
thread
,
ex_type
,
ex_value
,
trace
):
break_type
=
BREAK_TYPE_UNHANDLED
if
break_type
:
if
issubclass
(
ex_type
,
SystemExit
):
if
not
BREAK_ON_SYSTEMEXIT_ZERO
:
if
not
ex_value
or
(
isinstance
(
ex_value
,
SystemExit
)
and
not
ex_value
.
code
):
break_type
=
BREAK_TYPE_NONE
return
break_type
def
is_handled
(
self
,
thread
,
ex_type
,
ex_value
,
trace
):
if
trace
is
None
:
# get out if we didn't get a traceback
return
False
if
trace
.
tb_next
is
not
None
:
if
should_send_frame
(
trace
.
tb_next
.
tb_frame
)
and
should_debug_code
(
trace
.
tb_next
.
tb_frame
.
f_code
):
# don't break if this is not the top of the traceback,
# unless the previous frame was not debuggable
return
True
cur_frame
=
trace
.
tb_frame
while
should_send_frame
(
cur_frame
)
and
cur_frame
.
f_code
is
not
None
and
cur_frame
.
f_code
.
co_filename
is
not
None
:
filename
=
path
.
normcase
(
cur_frame
.
f_code
.
co_filename
)
if
is_file_in_zip
(
filename
):
# File is in a zip, so assume it handles exceptions
return
True
if
not
is_same_py_file
(
filename
,
__file__
):
handlers
=
self
.
handler_cache
.
get
(
filename
)
if
handlers
is
None
:
# req handlers for this file from the debug engine
self
.
handler_lock
.
acquire
()
with
_SendLockCtx
:
write_bytes
(
conn
,
REQH
)
write_string
(
conn
,
filename
)
# wait for the handler data to be received
self
.
handler_lock
.
acquire
()
self
.
handler_lock
.
release
()
handlers
=
self
.
handler_cache
.
get
(
filename
)
if
handlers
is
None
:
# no code available, so assume unhandled
return
False
line
=
cur_frame
.
f_lineno
for
line_start
,
line_end
,
expressions
in
handlers
:
if
line_start
is
None
or
line_start
<=
line
<
line_end
:
if
'*'
in
expressions
:
return
True
for
text
in
expressions
:
try
:
res
=
lookup_local
(
cur_frame
,
text
)
if
res
is
not
None
and
issubclass
(
ex_type
,
res
):
return
True
except
:
pass
cur_frame
=
cur_frame
.
f_back
return
False
def
add_exception
(
self
,
name
,
mode
=
BREAK_MODE_UNHANDLED
):
if
name
.
startswith
(
_EXCEPTIONS_MODULE
+
'.'
):
name
=
name
[
len
(
_EXCEPTIONS_MODULE
)
+
1
:]
self
.
break_on
[
name
]
=
mode
BREAK_ON
=
ExceptionBreakInfo
()
def
probe_stack
(
depth
=
10
):
"""helper to make sure we have enough stack space to proceed w/o corrupting
debugger state."""
if
depth
==
0
:
return
probe_stack
(
depth
-
1
)
PREFIXES
=
[
path
.
normcase
(
sys
.
prefix
)]
# If we're running in a virtual env, DEBUG_STDLIB should respect this too.
if
hasattr
(
sys
,
'base_prefix'
):
PREFIXES
.
append
(
path
.
normcase
(
sys
.
base_prefix
))
if
hasattr
(
sys
,
'real_prefix'
):
PREFIXES
.
append
(
path
.
normcase
(
sys
.
real_prefix
))
def
should_debug_code
(
code
):
if
not
code
or
not
code
.
co_filename
:
return
False
filename
=
path
.
normcase
(
code
.
co_filename
)
if
not
DEBUG_STDLIB
:
for
prefix
in
PREFIXES
:
if
prefix
!=
''
and
filename
.
startswith
(
prefix
):
return
False
for
dont_debug_file
in
DONT_DEBUG
:
if
is_same_py_file
(
filename
,
dont_debug_file
):
return
False
if
is_file_in_zip
(
filename
):
# file in inside an egg or zip, so we can't debug it
return
False
return
True
attach_lock
=
thread
.
allocate
()
attach_sent_break
=
False
local_path_to_vs_path
=
{}
def
breakpoint_path_match
(
vs_path
,
local_path
):
vs_path_norm
=
path
.
normcase
(
vs_path
)
local_path_norm
=
path
.
normcase
(
local_path
)
if
local_path_to_vs_path
.
get
(
local_path_norm
)
==
vs_path_norm
:
return
True
# Walk the local filesystem from local_path up, matching agains win_path component by component,
# and stop when we no longer see an __init__.py. This should give a reasonably close approximation
# of matching the package name.
while
True
:
local_path
,
local_name
=
path
.
split
(
local_path
)
vs_path
,
vs_name
=
ntpath
.
split
(
vs_path
)
# Match the last component in the path. If one or both components are unavailable, then
# we have reached the root on the corresponding path without successfully matching.
if
not
local_name
or
not
vs_name
or
path
.
normcase
(
local_name
)
!=
path
.
normcase
(
vs_name
):
return
False
# If we have an __init__.py, this module was inside the package, and we still need to match
# thatpackage, so walk up one level and keep matching. Otherwise, we've walked as far as we
# needed to, and matched all names on our way, so this is a match.
if
not
path
.
exists
(
path
.
join
(
local_path
,
'__init__.py'
)):
break
local_path_to_vs_path
[
local_path_norm
]
=
vs_path_norm
return
True
def
update_all_thread_stacks
(
blocking_thread
=
None
,
check_is_blocked
=
True
):
THREADS_LOCK
.
acquire
()
all_threads
=
list
(
THREADS
.
values
())
THREADS_LOCK
.
release
()
for
cur_thread
in
all_threads
:
if
cur_thread
is
blocking_thread
:
continue
cur_thread
.
_block_starting_lock
.
acquire
()
if
not
check_is_blocked
or
not
cur_thread
.
_is_blocked
:
# release the lock, we're going to run user code to evaluate the frames
cur_thread
.
_block_starting_lock
.
release
()
frames
=
cur_thread
.
get_frame_list
()
# re-acquire the lock and make sure we're still not blocked. If so send
# the frame list.
cur_thread
.
_block_starting_lock
.
acquire
()
if
not
check_is_blocked
or
not
cur_thread
.
_is_blocked
:
cur_thread
.
send_frame_list
(
frames
)
cur_thread
.
_block_starting_lock
.
release
()
DJANGO_BREAKPOINTS
=
{}
DJANGO_TEMPLATES
=
{}
class
DjangoBreakpointInfo
(
object
):
def
__init__
(
self
,
filename
):
self
.
_line_locations
=
None
self
.
filename
=
filename
self
.
breakpoints
=
{}
self
.
rangeIsPlainText
=
{}
def
add_breakpoint
(
self
,
lineno
,
brkpt_id
):
self
.
breakpoints
[
lineno
]
=
brkpt_id
def
remove_breakpoint
(
self
,
lineno
):
try
:
del
self
.
breakpoints
[
lineno
]
except
:
pass
def
has_breakpoint_for_line
(
self
,
lineNumber
):
return
self
.
breakpoints
.
get
(
lineNumber
)
is
not
None
def
is_range_plain_text
(
self
,
start
,
end
):
key
=
str
(
start
)
+
'-'
+
str
(
end
)
if
self
.
rangeIsPlainText
.
get
(
key
)
is
None
:
# we need to calculate our line number offset information
try
:
with
open
(
self
.
filename
,
'rb'
)
as
contents
:
contents
.
seek
(
start
,
0
)
# 0 = start of file, optional in this case
data
=
contents
.
read
(
end
-
start
)
isPlainText
=
True
if
data
.
startswith
(
'{{'
)
and
data
.
endswith
(
'}}'
):
isPlainText
=
False
if
data
.
startswith
(
'{%'
)
and
data
.
endswith
(
'%}'
):
isPlainText
=
False
self
.
rangeIsPlainText
[
key
]
=
isPlainText
return
isPlainText
except
:
return
False
else
:
return
self
.
rangeIsPlainText
.
get
(
key
)
def
line_number_to_offset
(
self
,
lineNumber
):
line_locs
=
self
.
line_locations
if
line_locs
is
not
None
:
low_line
=
line_locs
[
lineNumber
-
1
]
hi_line
=
line_locs
[
lineNumber
]
return
low_line
,
hi_line
return
(
None
,
None
)
@
property
def
line_locations
(
self
):
if
self
.
_line_locations
is
None
:
# we need to calculate our line number offset information
try
:
contents
=
open
(
self
.
filename
,
'rb'
)
except
:
# file not available, locked, etc...
pass
else
:
with
contents
:
line_info
=
[]
file_len
=
0
for
line
in
contents
:
line_len
=
len
(
line
)
if
not
line_info
and
line
.
startswith
(
BOM_UTF8
):
line_len
-=
len
(
BOM_UTF8
)
# Strip the BOM, Django seems to ignore this...
if
line
.
endswith
(
to_bytes
(
'
\r
\n
'
)):
line_len
-=
1
# Django normalizes newlines to \n
file_len
+=
line_len
line_info
.
append
(
file_len
)
contents
.
close
()
self
.
_line_locations
=
line_info
return
self
.
_line_locations
def
get_line_range
(
self
,
start
,
end
):
line_locs
=
self
.
line_locations
if
line_locs
is
not
None
:
low_line
=
bisect
.
bisect_right
(
line_locs
,
start
)
hi_line
=
bisect
.
bisect_right
(
line_locs
,
end
)
return
low_line
,
hi_line
return
(
None
,
None
)
def
should_break
(
self
,
start
,
end
):
low_line
,
hi_line
=
self
.
get_line_range
(
start
,
end
)
if
low_line
is
not
None
and
hi_line
is
not
None
:
# low_line/hi_line is 0 based, self.breakpoints is 1 based
for
i
in
xrange
(
low_line
+
1
,
hi_line
+
2
):
bkpt_id
=
self
.
breakpoints
.
get
(
i
)
if
bkpt_id
is
not
None
:
return
True
,
bkpt_id
return
False
,
0
def
get_django_frame_source
(
frame
):
global
DJANGO_VERSIONS_IDENTIFIED
global
IS_DJANGO18
global
IS_DJANGO19
global
IS_DJANGO19_OR_HIGHER
if
DJANGO_VERSIONS_IDENTIFIED
==
False
:
DJANGO_VERSIONS_IDENTIFIED
=
True
try
:
import
django
version
=
django
.
VERSION
IS_DJANGO18
=
version
[
0
]
==
1
and
version
[
1
]
==
8
IS_DJANGO19
=
version
[
0
]
==
1
and
version
[
1
]
==
9
IS_DJANGO19_OR_HIGHER
=
((
version
[
0
]
==
1
and
version
[
1
]
>=
9
)
or
version
[
0
]
>
1
)
except
:
pass
if
frame
.
f_code
.
co_name
==
'render'
:
self_obj
=
frame
.
f_locals
.
get
(
'self'
,
None
)
if
self_obj
is
None
:
return
None
origin
=
_get_template_file_name
(
frame
)
line
=
_get_template_line
(
frame
)
position
=
None
if
self_obj
is
not
None
and
hasattr
(
self_obj
,
'token'
)
and
hasattr
(
self_obj
.
token
,
'position'
):
position
=
self_obj
.
token
.
position
if
origin
is
not
None
and
position
is
None
:
active_bps
=
DJANGO_BREAKPOINTS
.
get
(
origin
.
lower
())
if
active_bps
is
None
:
active_bps
=
DJANGO_TEMPLATES
.
get
(
origin
.
lower
())
if
active_bps
is
None
:
DJANGO_BREAKPOINTS
[
origin
.
lower
()]
=
active_bps
=
DjangoBreakpointInfo
(
origin
.
lower
())
if
active_bps
is
not
None
:
if
line
is
not
None
:
position
=
active_bps
.
line_number_to_offset
(
line
)
if
origin
and
position
:
return
str
(
origin
),
position
,
line
return
None
class
ModuleExitFrame
(
object
):
def
__init__
(
self
,
real_frame
):
self
.
real_frame
=
real_frame
self
.
f_lineno
=
real_frame
.
f_lineno
+
1
def
__getattr__
(
self
,
name
):
return
getattr
(
self
.
real_frame
,
name
)
class
Thread
(
object
):
def
__init__
(
self
,
id
=
None
):
if
id
is
not
None
:
self
.
id
=
id
else
:
self
.
id
=
thread
.
get_ident
()
self
.
_events
=
{
'call'
:
self
.
handle_call
,
'line'
:
self
.
handle_line
,
'return'
:
self
.
handle_return
,
'exception'
:
self
.
handle_exception
,
'c_call'
:
self
.
handle_c_call
,
'c_return'
:
self
.
handle_c_return
,
'c_exception'
:
self
.
handle_c_exception
,
}
self
.
cur_frame
=
None
self
.
stepping
=
STEPPING_NONE
self
.
unblock_work
=
None
self
.
_block_lock
=
thread
.
allocate_lock
()
self
.
_block_lock
.
acquire
()
self
.
_block_starting_lock
=
thread
.
allocate_lock
()
self
.
_is_blocked
=
False
self
.
_is_working
=
False
self
.
stopped_on_line
=
None
self
.
detach
=
False
self
.
trace_func
=
self
.
trace_func
# replace self.trace_func w/ a bound method so we don't need to re-create these regularly
self
.
prev_trace_func
=
None
self
.
trace_func_stack
=
[]
self
.
reported_process_loaded
=
False
self
.
django_stepping
=
None
self
.
is_sending
=
False
# stackless changes
if
stackless
is
not
None
:
self
.
_stackless_attach
()
if
sys
.
platform
==
'cli'
:
self
.
frames
=
[]
if
sys
.
platform
==
'cli'
:
# workaround an IronPython bug where we're sometimes missing the back frames
# http://ironpython.codeplex.com/workitem/31437
def
push_frame
(
self
,
frame
):
self
.
cur_frame
=
frame
self
.
frames
.
append
(
frame
)
def
pop_frame
(
self
):
self
.
frames
.
pop
()
self
.
cur_frame
=
self
.
frames
[
-
1
]
else
:
def
push_frame
(
self
,
frame
):
self
.
cur_frame
=
frame
def
pop_frame
(
self
):
self
.
cur_frame
=
self
.
cur_frame
.
f_back
def
_stackless_attach
(
self
):
try
:
stackless
.
tasklet
.
trace_function
except
AttributeError
:
# the tasklets need to be traced on a case by case basis
# sys.trace needs to be called within their calling context
def
__call__
(
tsk
,
*
args
,
**
kwargs
):
f
=
tsk
.
tempval
def
new_f
(
old_f
,
args
,
kwargs
):
sys
.
settrace
(
self
.
trace_func
)
try
:
if
old_f
is
not
None
:
return
old_f
(
*
args
,
**
kwargs
)
finally
:
sys
.
settrace
(
None
)
tsk
.
tempval
=
new_f
stackless
.
tasklet
.
setup
(
tsk
,
f
,
args
,
kwargs
)
return
tsk
def
settrace
(
tsk
,
tb
):
if
hasattr
(
tsk
.
frame
,
"f_trace"
):
tsk
.
frame
.
f_trace
=
tb
sys
.
settrace
(
tb
)
self
.
__oldstacklesscall__
=
stackless
.
tasklet
.
__call__
stackless
.
tasklet
.
settrace
=
settrace
stackless
.
tasklet
.
__call__
=
__call__
if
sys
.
platform
==
'cli'
:
self
.
frames
=
[]
if
sys
.
platform
==
'cli'
:
# workaround an IronPython bug where we're sometimes missing the back frames
# http://ironpython.codeplex.com/workitem/31437
def
push_frame
(
self
,
frame
):
self
.
cur_frame
=
frame
self
.
frames
.
append
(
frame
)
def
pop_frame
(
self
):
self
.
frames
.
pop
()
self
.
cur_frame
=
self
.
frames
[
-
1
]
else
:
def
push_frame
(
self
,
frame
):
self
.
cur_frame
=
frame
def
pop_frame
(
self
):
self
.
cur_frame
=
self
.
cur_frame
.
f_back
def
context_dispatcher
(
self
,
old
,
new
):
self
.
stepping
=
STEPPING_NONE
# for those tasklets that started before we started tracing
# we need to make sure that the trace is set by patching
# it in the context switch
if
old
and
new
:
if
hasattr
(
new
.
frame
,
"f_trace"
)
and
not
new
.
frame
.
f_trace
:
sys
.
call_tracing
(
new
.
settrace
,(
self
.
trace_func
,))
def
_stackless_schedule_cb
(
self
,
prev
,
next
):
current
=
stackless
.
getcurrent
()
if
not
current
:
return
current_tf
=
current
.
trace_function
try
:
current
.
trace_function
=
None
self
.
stepping
=
STEPPING_NONE
# If the current frame has no trace function, we may need to get it
# from the previous frame, depending on how we ended up in the
# callback.
if
current_tf
is
None
:
f_back
=
current
.
frame
.
f_back
if
f_back
is
not
None
:
current_tf
=
f_back
.
f_trace
if
next
is
not
None
:
# Assign our trace function to the current stack
f
=
next
.
frame
if
next
is
current
:
f
=
f
.
f_back
while
f
:
if
isinstance
(
f
,
types
.
FrameType
):
f
.
f_trace
=
self
.
trace_func
f
=
f
.
f_back
next
.
trace_function
=
self
.
trace_func
finally
:
current
.
trace_function
=
current_tf
def
trace_func
(
self
,
frame
,
event
,
arg
):
# If we're so far into process shutdown that sys is already gone, just stop tracing.
if
sys
is
None
:
return
None
elif
self
.
is_sending
:
# https://pytools.codeplex.com/workitem/1864
# we're currently doing I/O w/ the socket, we don't want to deliver
# any breakpoints or async breaks because we'll deadlock. Continue
# to return the trace function so all of our frames remain
# balanced. A better way to deal with this might be to do
# sys.settrace(None) when we take the send lock, but that's much
# more difficult because our send context manager is used both
# inside and outside of the trace function, and so is used when
# tracing is enabled and disabled, and so it's very easy to get our
# current frame tracking to be thrown off...
return
self
.
trace_func
try
:
# if should_debug_code(frame.f_code) is not true during attach
# the current frame is None and a pop_frame will cause an exception and
# break the debugger
if
self
.
cur_frame
is
None
:
# happens during attach, we need frame for blocking
self
.
push_frame
(
frame
)
if
self
.
stepping
==
STEPPING_BREAK
and
should_debug_code
(
frame
.
f_code
):
if
self
.
detach
:
if
stackless
is
not
None
:
stackless
.
set_schedule_callback
(
None
)
stackless
.
tasklet
.
__call__
=
self
.
__oldstacklesscall__
sys
.
settrace
(
None
)
return
None
self
.
async_break
()
return
self
.
_events
[
event
](
frame
,
arg
)
except
(
StackOverflowException
,
KeyboardInterrupt
):
# stack overflow, disable tracing
return
self
.
trace_func
def
handle_call
(
self
,
frame
,
arg
):
self
.
push_frame
(
frame
)
if
DJANGO_BREAKPOINTS
:
source_obj
=
get_django_frame_source
(
frame
)
if
source_obj
is
not
None
:
origin
, (
start
,
end
),
lineNumber
=
source_obj
active_bps
=
DJANGO_BREAKPOINTS
.
get
(
origin
.
lower
())
should_break
=
False
if
active_bps
is
not
None
and
origin
!=
'<unknown source>'
:
should_break
,
bkpt_id
=
active_bps
.
should_break
(
start
,
end
)
isPlainText
=
active_bps
.
is_range_plain_text
(
start
,
end
)
if
isPlainText
:
should_break
=
False
if
should_break
:
probe_stack
()
update_all_thread_stacks
(
self
)
self
.
block
(
lambda
: (
report_breakpoint_hit
(
bkpt_id
,
self
.
id
),
mark_all_threads_for_break
(
skip_thread
=
self
)))
if
not
should_break
and
self
.
django_stepping
:
self
.
django_stepping
=
None
self
.
stepping
=
STEPPING_OVER
self
.
block_maybe_attach
()
if
frame
.
f_code
.
co_name
==
'<module>'
and
frame
.
f_code
.
co_filename
not
in
[
'<string>'
,
'<stdin>'
]:
probe_stack
()
code
,
module
=
new_module
(
frame
)
if
not
DETACHED
:
View remainder of file in raw view
Back
|
FazBrowse Home
|
New Git URL