GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — pythonVSCode/pythonFiles/PythonTools/visualstudio_py_debugger.py at master · gag/pythonVSCode Expand file tree/ Copy pathvisualstudio_py_debugger.pyMore file actions 2752 lines (2331 loc) · 102 KB / Copy pathvisualstudio_py_debugger.pyFile metadata and controls2752 lines (2331 loc) · 102 KB 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 __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 RICH_EXCEPTIONS = 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') EXC2 = to_bytes('EXC2') 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>']: |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |