# ############################################################################
#
# Copyright (c) Microsoft Corporation.
#
# This source code is subject to terms and conditions of the Apache License, Version 2.0. A
# copy of the license can be found in the License.html file at the root of this distribution. If
# you cannot locate the Apache License, Version 2.0, please send an email to
# vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
# by the terms of the Apache License, Version 2.0.
#
# You must not remove this notice, or any other, from this software.
#
# ###########################################################################
from __future__ import with_statement
# 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
# 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
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()