mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-02 14:27:31 +00:00
first commit
This commit is contained in:
9
.venv/Lib/site-packages/debugpy/server/__init__.py
Normal file
9
.venv/Lib/site-packages/debugpy/server/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
# "force_pydevd" must be imported first to ensure (via side effects)
|
||||
# that the debugpy-vendored copy of pydevd gets used.
|
||||
import debugpy._vendored.force_pydevd # noqa
|
336
.venv/Lib/site-packages/debugpy/server/api.py
Normal file
336
.venv/Lib/site-packages/debugpy/server/api.py
Normal file
@ -0,0 +1,336 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import codecs
|
||||
import json
|
||||
import os
|
||||
import pydevd
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
|
||||
import debugpy
|
||||
from debugpy import adapter
|
||||
from debugpy.common import compat, fmt, log, sockets
|
||||
from _pydevd_bundle.pydevd_constants import get_global_debugger
|
||||
from pydevd_file_utils import absolute_path
|
||||
|
||||
|
||||
_tls = threading.local()
|
||||
|
||||
# TODO: "gevent", if possible.
|
||||
_config = {
|
||||
"qt": "none",
|
||||
"subProcess": True,
|
||||
"python": sys.executable,
|
||||
}
|
||||
|
||||
_config_valid_values = {
|
||||
# If property is not listed here, any value is considered valid, so long as
|
||||
# its type matches that of the default value in _config.
|
||||
"qt": ["auto", "none", "pyside", "pyside2", "pyqt4", "pyqt5"],
|
||||
}
|
||||
|
||||
# This must be a global to prevent it from being garbage collected and triggering
|
||||
# https://bugs.python.org/issue37380.
|
||||
_adapter_process = None
|
||||
|
||||
|
||||
def _settrace(*args, **kwargs):
|
||||
log.debug("pydevd.settrace(*{0!r}, **{1!r})", args, kwargs)
|
||||
# The stdin in notification is not acted upon in debugpy, so, disable it.
|
||||
kwargs.setdefault("notify_stdin", False)
|
||||
try:
|
||||
return pydevd.settrace(*args, **kwargs)
|
||||
except Exception:
|
||||
raise
|
||||
else:
|
||||
_settrace.called = True
|
||||
|
||||
|
||||
_settrace.called = False
|
||||
|
||||
|
||||
def ensure_logging():
|
||||
"""Starts logging to log.log_dir, if it hasn't already been done.
|
||||
"""
|
||||
if ensure_logging.ensured:
|
||||
return
|
||||
ensure_logging.ensured = True
|
||||
log.to_file(prefix="debugpy.server")
|
||||
log.describe_environment("Initial environment:")
|
||||
|
||||
|
||||
ensure_logging.ensured = False
|
||||
|
||||
|
||||
def log_to(path):
|
||||
if ensure_logging.ensured:
|
||||
raise RuntimeError("logging has already begun")
|
||||
|
||||
log.debug("log_to{0!r}", (path,))
|
||||
if path is sys.stderr:
|
||||
log.stderr.levels |= set(log.LEVELS)
|
||||
else:
|
||||
log.log_dir = path
|
||||
|
||||
|
||||
def configure(properties, **kwargs):
|
||||
if _settrace.called:
|
||||
raise RuntimeError("debug adapter is already running")
|
||||
|
||||
ensure_logging()
|
||||
log.debug("configure{0!r}", (properties, kwargs))
|
||||
|
||||
if properties is None:
|
||||
properties = kwargs
|
||||
else:
|
||||
properties = dict(properties)
|
||||
properties.update(kwargs)
|
||||
|
||||
for k, v in properties.items():
|
||||
if k not in _config:
|
||||
raise ValueError(fmt("Unknown property {0!r}", k))
|
||||
expected_type = type(_config[k])
|
||||
if type(v) is not expected_type:
|
||||
raise ValueError(fmt("{0!r} must be a {1}", k, expected_type.__name__))
|
||||
valid_values = _config_valid_values.get(k)
|
||||
if (valid_values is not None) and (v not in valid_values):
|
||||
raise ValueError(fmt("{0!r} must be one of: {1!r}", k, valid_values))
|
||||
_config[k] = v
|
||||
|
||||
|
||||
def _starts_debugging(func):
|
||||
def debug(address, **kwargs):
|
||||
if _settrace.called:
|
||||
raise RuntimeError("this process already has a debug adapter")
|
||||
|
||||
try:
|
||||
_, port = address
|
||||
except Exception:
|
||||
port = address
|
||||
address = ("127.0.0.1", port)
|
||||
try:
|
||||
port.__index__() # ensure it's int-like
|
||||
except Exception:
|
||||
raise ValueError("expected port or (host, port)")
|
||||
if not (0 <= port < 2 ** 16):
|
||||
raise ValueError("invalid port number")
|
||||
|
||||
ensure_logging()
|
||||
log.debug("{0}({1!r}, **{2!r})", func.__name__, address, kwargs)
|
||||
log.info("Initial debug configuration: {0!j}", _config)
|
||||
|
||||
qt_mode = _config.get("qt", "none")
|
||||
if qt_mode != "none":
|
||||
pydevd.enable_qt_support(qt_mode)
|
||||
|
||||
settrace_kwargs = {
|
||||
"suspend": False,
|
||||
"patch_multiprocessing": _config.get("subProcess", True),
|
||||
}
|
||||
|
||||
debugpy_path = os.path.dirname(absolute_path(debugpy.__file__))
|
||||
settrace_kwargs["dont_trace_start_patterns"] = (debugpy_path,)
|
||||
settrace_kwargs["dont_trace_end_patterns"] = (str("debugpy_launcher.py"),)
|
||||
|
||||
try:
|
||||
return func(address, settrace_kwargs, **kwargs)
|
||||
except Exception:
|
||||
log.reraise_exception("{0}() failed:", func.__name__, level="info")
|
||||
|
||||
return debug
|
||||
|
||||
|
||||
@_starts_debugging
|
||||
def listen(address, settrace_kwargs):
|
||||
# Errors below are logged with level="info", because the caller might be catching
|
||||
# and handling exceptions, and we don't want to spam their stderr unnecessarily.
|
||||
|
||||
import subprocess
|
||||
|
||||
server_access_token = compat.force_str(codecs.encode(os.urandom(32), "hex"))
|
||||
|
||||
try:
|
||||
endpoints_listener = sockets.create_server("127.0.0.1", 0, timeout=10)
|
||||
except Exception as exc:
|
||||
log.swallow_exception("Can't listen for adapter endpoints:")
|
||||
raise RuntimeError("can't listen for adapter endpoints: " + str(exc))
|
||||
|
||||
try:
|
||||
endpoints_host, endpoints_port = endpoints_listener.getsockname()
|
||||
log.info(
|
||||
"Waiting for adapter endpoints on {0}:{1}...",
|
||||
endpoints_host,
|
||||
endpoints_port,
|
||||
)
|
||||
|
||||
host, port = address
|
||||
adapter_args = [
|
||||
_config.get("python", sys.executable),
|
||||
os.path.dirname(adapter.__file__),
|
||||
"--for-server",
|
||||
str(endpoints_port),
|
||||
"--host",
|
||||
host,
|
||||
"--port",
|
||||
str(port),
|
||||
"--server-access-token",
|
||||
server_access_token,
|
||||
]
|
||||
if log.log_dir is not None:
|
||||
adapter_args += ["--log-dir", log.log_dir]
|
||||
log.info("debugpy.listen() spawning adapter: {0!j}", adapter_args)
|
||||
|
||||
# On Windows, detach the adapter from our console, if any, so that it doesn't
|
||||
# receive Ctrl+C from it, and doesn't keep it open once we exit.
|
||||
creationflags = 0
|
||||
if sys.platform == "win32":
|
||||
creationflags |= 0x08000000 # CREATE_NO_WINDOW
|
||||
creationflags |= 0x00000200 # CREATE_NEW_PROCESS_GROUP
|
||||
|
||||
# Adapter will outlive this process, so we shouldn't wait for it. However, we
|
||||
# need to ensure that the Popen instance for it doesn't get garbage-collected
|
||||
# by holding a reference to it in a non-local variable, to avoid triggering
|
||||
# https://bugs.python.org/issue37380.
|
||||
try:
|
||||
global _adapter_process
|
||||
_adapter_process = subprocess.Popen(
|
||||
adapter_args, close_fds=True, creationflags=creationflags
|
||||
)
|
||||
if os.name == "posix":
|
||||
# It's going to fork again to daemonize, so we need to wait on it to
|
||||
# clean it up properly.
|
||||
_adapter_process.wait()
|
||||
else:
|
||||
# Suppress misleading warning about child process still being alive when
|
||||
# this process exits (https://bugs.python.org/issue38890).
|
||||
_adapter_process.returncode = 0
|
||||
pydevd.add_dont_terminate_child_pid(_adapter_process.pid)
|
||||
except Exception as exc:
|
||||
log.swallow_exception("Error spawning debug adapter:", level="info")
|
||||
raise RuntimeError("error spawning debug adapter: " + str(exc))
|
||||
|
||||
try:
|
||||
sock, _ = endpoints_listener.accept()
|
||||
try:
|
||||
sock.settimeout(None)
|
||||
sock_io = sock.makefile("rb", 0)
|
||||
try:
|
||||
endpoints = json.loads(sock_io.read().decode("utf-8"))
|
||||
finally:
|
||||
sock_io.close()
|
||||
finally:
|
||||
sockets.close_socket(sock)
|
||||
except socket.timeout:
|
||||
log.swallow_exception(
|
||||
"Timed out waiting for adapter to connect:", level="info"
|
||||
)
|
||||
raise RuntimeError("timed out waiting for adapter to connect")
|
||||
except Exception as exc:
|
||||
log.swallow_exception("Error retrieving adapter endpoints:", level="info")
|
||||
raise RuntimeError("error retrieving adapter endpoints: " + str(exc))
|
||||
|
||||
finally:
|
||||
endpoints_listener.close()
|
||||
|
||||
log.info("Endpoints received from adapter: {0!j}", endpoints)
|
||||
|
||||
if "error" in endpoints:
|
||||
raise RuntimeError(str(endpoints["error"]))
|
||||
|
||||
try:
|
||||
server_host = str(endpoints["server"]["host"])
|
||||
server_port = int(endpoints["server"]["port"])
|
||||
client_host = str(endpoints["client"]["host"])
|
||||
client_port = int(endpoints["client"]["port"])
|
||||
except Exception as exc:
|
||||
log.swallow_exception(
|
||||
"Error parsing adapter endpoints:\n{0!j}\n", endpoints, level="info"
|
||||
)
|
||||
raise RuntimeError("error parsing adapter endpoints: " + str(exc))
|
||||
log.info(
|
||||
"Adapter is accepting incoming client connections on {0}:{1}",
|
||||
client_host,
|
||||
client_port,
|
||||
)
|
||||
|
||||
_settrace(
|
||||
host=server_host,
|
||||
port=server_port,
|
||||
wait_for_ready_to_run=False,
|
||||
block_until_connected=True,
|
||||
access_token=server_access_token,
|
||||
**settrace_kwargs
|
||||
)
|
||||
log.info("pydevd is connected to adapter at {0}:{1}", server_host, server_port)
|
||||
return client_host, client_port
|
||||
|
||||
|
||||
@_starts_debugging
|
||||
def connect(address, settrace_kwargs, access_token):
|
||||
host, port = address
|
||||
_settrace(host=host, port=port, client_access_token=access_token, **settrace_kwargs)
|
||||
|
||||
|
||||
def wait_for_client():
|
||||
ensure_logging()
|
||||
log.debug("wait_for_client()")
|
||||
|
||||
pydb = get_global_debugger()
|
||||
if pydb is None:
|
||||
raise RuntimeError("listen() or connect() must be called first")
|
||||
|
||||
cancel_event = threading.Event()
|
||||
debugpy.wait_for_client.cancel = wait_for_client.cancel = cancel_event.set
|
||||
pydevd._wait_for_attach(cancel=cancel_event)
|
||||
|
||||
|
||||
def is_client_connected():
|
||||
return pydevd._is_attached()
|
||||
|
||||
|
||||
def breakpoint():
|
||||
ensure_logging()
|
||||
if not is_client_connected():
|
||||
log.info("breakpoint() ignored - debugger not attached")
|
||||
return
|
||||
log.debug("breakpoint()")
|
||||
|
||||
# Get the first frame in the stack that's not an internal frame.
|
||||
pydb = get_global_debugger()
|
||||
stop_at_frame = sys._getframe().f_back
|
||||
while (
|
||||
stop_at_frame is not None
|
||||
and pydb.get_file_type(stop_at_frame) == pydb.PYDEV_FILE
|
||||
):
|
||||
stop_at_frame = stop_at_frame.f_back
|
||||
|
||||
_settrace(
|
||||
suspend=True,
|
||||
trace_only_current_thread=True,
|
||||
patch_multiprocessing=False,
|
||||
stop_at_frame=stop_at_frame,
|
||||
)
|
||||
stop_at_frame = None
|
||||
|
||||
|
||||
def debug_this_thread():
|
||||
ensure_logging()
|
||||
log.debug("debug_this_thread()")
|
||||
|
||||
_settrace(suspend=False)
|
||||
|
||||
|
||||
def trace_this_thread(should_trace):
|
||||
ensure_logging()
|
||||
log.debug("trace_this_thread({0!r})", should_trace)
|
||||
|
||||
pydb = get_global_debugger()
|
||||
if should_trace:
|
||||
pydb.enable_tracing()
|
||||
else:
|
||||
pydb.disable_tracing()
|
@ -0,0 +1,94 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
"""Script injected into the debuggee process during attach-to-PID."""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
__file__ = os.path.abspath(__file__)
|
||||
_debugpy_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
def attach(setup):
|
||||
log = None
|
||||
try:
|
||||
import sys
|
||||
|
||||
if "threading" not in sys.modules:
|
||||
try:
|
||||
|
||||
def on_warn(msg):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def on_exception(msg):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def on_critical(msg):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
pydevd_attach_to_process_path = os.path.join(
|
||||
_debugpy_dir,
|
||||
"debugpy",
|
||||
"_vendored",
|
||||
"pydevd",
|
||||
"pydevd_attach_to_process",
|
||||
)
|
||||
assert os.path.exists(pydevd_attach_to_process_path)
|
||||
sys.path.insert(0, pydevd_attach_to_process_path)
|
||||
|
||||
# NOTE: that it's not a part of the pydevd PYTHONPATH
|
||||
import attach_script
|
||||
|
||||
attach_script.fix_main_thread_id(
|
||||
on_warn=on_warn, on_exception=on_exception, on_critical=on_critical
|
||||
)
|
||||
|
||||
# NOTE: At this point it should be safe to remove this.
|
||||
sys.path.remove(pydevd_attach_to_process_path)
|
||||
except:
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
raise
|
||||
|
||||
sys.path.insert(0, _debugpy_dir)
|
||||
try:
|
||||
import debugpy
|
||||
import debugpy.server
|
||||
from debugpy.common import log
|
||||
import pydevd
|
||||
finally:
|
||||
assert sys.path[0] == _debugpy_dir
|
||||
del sys.path[0]
|
||||
|
||||
py_db = pydevd.get_global_debugger()
|
||||
if py_db is not None:
|
||||
py_db.dispose_and_kill_all_pydevd_threads(wait=False)
|
||||
|
||||
if setup["log_to"] is not None:
|
||||
debugpy.log_to(setup["log_to"])
|
||||
log.info("Configuring injected debugpy: {0!j}", setup)
|
||||
|
||||
if setup["mode"] == "listen":
|
||||
debugpy.listen(setup["address"])
|
||||
elif setup["mode"] == "connect":
|
||||
debugpy.connect(
|
||||
setup["address"], access_token=setup["adapter_access_token"]
|
||||
)
|
||||
else:
|
||||
raise AssertionError(repr(setup))
|
||||
|
||||
except:
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
if log is None:
|
||||
raise
|
||||
else:
|
||||
log.reraise_exception()
|
||||
|
||||
log.info("debugpy injected successfully")
|
448
.venv/Lib/site-packages/debugpy/server/cli.py
Normal file
448
.venv/Lib/site-packages/debugpy/server/cli.py
Normal file
@ -0,0 +1,448 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See LICENSE in the project root
|
||||
# for license information.
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import runpy
|
||||
import sys
|
||||
|
||||
# debugpy.__main__ should have preloaded pydevd properly before importing this module.
|
||||
# Otherwise, some stdlib modules above might have had imported threading before pydevd
|
||||
# could perform the necessary detours in it.
|
||||
assert "pydevd" in sys.modules
|
||||
import pydevd
|
||||
|
||||
import debugpy
|
||||
from debugpy.common import compat, fmt, log
|
||||
from debugpy.server import api
|
||||
|
||||
|
||||
TARGET = "<filename> | -m <module> | -c <code> | --pid <pid>"
|
||||
|
||||
HELP = """debugpy {0}
|
||||
See https://aka.ms/debugpy for documentation.
|
||||
|
||||
Usage: debugpy --listen | --connect
|
||||
[<host>:]<port>
|
||||
[--wait-for-client]
|
||||
[--configure-<name> <value>]...
|
||||
[--log-to <path>] [--log-to-stderr]
|
||||
{1}
|
||||
[<arg>]...
|
||||
""".format(
|
||||
debugpy.__version__, TARGET
|
||||
)
|
||||
|
||||
|
||||
class Options(object):
|
||||
mode = None
|
||||
address = None
|
||||
log_to = None
|
||||
log_to_stderr = False
|
||||
target = None # unicode
|
||||
target_kind = None
|
||||
wait_for_client = False
|
||||
adapter_access_token = None
|
||||
|
||||
|
||||
options = Options()
|
||||
options.config = {"qt": "none", "subProcess": True}
|
||||
|
||||
|
||||
def in_range(parser, start, stop):
|
||||
def parse(s):
|
||||
n = parser(s)
|
||||
if start is not None and n < start:
|
||||
raise ValueError(fmt("must be >= {0}", start))
|
||||
if stop is not None and n >= stop:
|
||||
raise ValueError(fmt("must be < {0}", stop))
|
||||
return n
|
||||
|
||||
return parse
|
||||
|
||||
|
||||
pid = in_range(int, 0, None)
|
||||
|
||||
|
||||
def print_help_and_exit(switch, it):
|
||||
print(HELP, file=sys.stderr)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def print_version_and_exit(switch, it):
|
||||
print(debugpy.__version__)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def set_arg(varname, parser=(lambda x: x)):
|
||||
def do(arg, it):
|
||||
value = parser(next(it))
|
||||
setattr(options, varname, value)
|
||||
|
||||
return do
|
||||
|
||||
|
||||
def set_const(varname, value):
|
||||
def do(arg, it):
|
||||
setattr(options, varname, value)
|
||||
|
||||
return do
|
||||
|
||||
|
||||
def set_address(mode):
|
||||
def do(arg, it):
|
||||
if options.address is not None:
|
||||
raise ValueError("--listen and --connect are mutually exclusive")
|
||||
|
||||
# It's either host:port, or just port.
|
||||
value = next(it)
|
||||
host, sep, port = value.partition(":")
|
||||
if not sep:
|
||||
host = "127.0.0.1"
|
||||
port = value
|
||||
try:
|
||||
port = int(port)
|
||||
except Exception:
|
||||
port = -1
|
||||
if not (0 <= port < 2 ** 16):
|
||||
raise ValueError("invalid port number")
|
||||
|
||||
options.mode = mode
|
||||
options.address = (host, port)
|
||||
|
||||
return do
|
||||
|
||||
|
||||
def set_config(arg, it):
|
||||
prefix = "--configure-"
|
||||
assert arg.startswith(prefix)
|
||||
name = arg[len(prefix) :]
|
||||
value = next(it)
|
||||
|
||||
if name not in options.config:
|
||||
raise ValueError(fmt("unknown property {0!r}", name))
|
||||
|
||||
expected_type = type(options.config[name])
|
||||
try:
|
||||
if expected_type is bool:
|
||||
value = {"true": True, "false": False}[value.lower()]
|
||||
else:
|
||||
value = expected_type(value)
|
||||
except Exception:
|
||||
raise ValueError(fmt("{0!r} must be a {1}", name, expected_type.__name__))
|
||||
|
||||
options.config[name] = value
|
||||
|
||||
|
||||
def set_target(kind, parser=(lambda x: x), positional=False):
|
||||
def do(arg, it):
|
||||
options.target_kind = kind
|
||||
target = parser(arg if positional else next(it))
|
||||
|
||||
if isinstance(target, bytes):
|
||||
# target may be the code, so, try some additional encodings...
|
||||
try:
|
||||
target = target.decode(sys.getfilesystemencoding())
|
||||
except UnicodeDecodeError:
|
||||
try:
|
||||
target = target.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
import locale
|
||||
|
||||
target = target.decode(locale.getpreferredencoding(False))
|
||||
options.target = target
|
||||
|
||||
return do
|
||||
|
||||
|
||||
# fmt: off
|
||||
switches = [
|
||||
# Switch Placeholder Action
|
||||
# ====== =========== ======
|
||||
|
||||
# Switches that are documented for use by end users.
|
||||
("-(\\?|h|-help)", None, print_help_and_exit),
|
||||
("-(V|-version)", None, print_version_and_exit),
|
||||
("--log-to" , "<path>", set_arg("log_to")),
|
||||
("--log-to-stderr", None, set_const("log_to_stderr", True)),
|
||||
("--listen", "<address>", set_address("listen")),
|
||||
("--connect", "<address>", set_address("connect")),
|
||||
("--wait-for-client", None, set_const("wait_for_client", True)),
|
||||
("--configure-.+", "<value>", set_config),
|
||||
|
||||
# Switches that are used internally by the client or debugpy itself.
|
||||
("--adapter-access-token", "<token>", set_arg("adapter_access_token")),
|
||||
|
||||
# Targets. The "" entry corresponds to positional command line arguments,
|
||||
# i.e. the ones not preceded by any switch name.
|
||||
("", "<filename>", set_target("file", positional=True)),
|
||||
("-m", "<module>", set_target("module")),
|
||||
("-c", "<code>", set_target("code")),
|
||||
("--pid", "<pid>", set_target("pid", pid)),
|
||||
]
|
||||
# fmt: on
|
||||
|
||||
|
||||
def consume_argv():
|
||||
while len(sys.argv) >= 2:
|
||||
value = sys.argv[1]
|
||||
del sys.argv[1]
|
||||
yield value
|
||||
|
||||
|
||||
def parse_argv():
|
||||
seen = set()
|
||||
it = consume_argv()
|
||||
|
||||
while True:
|
||||
try:
|
||||
arg = next(it)
|
||||
except StopIteration:
|
||||
raise ValueError("missing target: " + TARGET)
|
||||
|
||||
switch = compat.filename(arg)
|
||||
if not switch.startswith("-"):
|
||||
switch = ""
|
||||
for pattern, placeholder, action in switches:
|
||||
if re.match("^(" + pattern + ")$", switch):
|
||||
break
|
||||
else:
|
||||
raise ValueError("unrecognized switch " + switch)
|
||||
|
||||
if switch in seen:
|
||||
raise ValueError("duplicate switch " + switch)
|
||||
else:
|
||||
seen.add(switch)
|
||||
|
||||
try:
|
||||
action(arg, it)
|
||||
except StopIteration:
|
||||
assert placeholder is not None
|
||||
raise ValueError(fmt("{0}: missing {1}", switch, placeholder))
|
||||
except Exception as exc:
|
||||
raise ValueError(fmt("invalid {0} {1}: {2}", switch, placeholder, exc))
|
||||
|
||||
if options.target is not None:
|
||||
break
|
||||
|
||||
if options.mode is None:
|
||||
raise ValueError("either --listen or --connect is required")
|
||||
if options.adapter_access_token is not None and options.mode != "connect":
|
||||
raise ValueError("--adapter-access-token requires --connect")
|
||||
if options.target_kind == "pid" and options.wait_for_client:
|
||||
raise ValueError("--pid does not support --wait-for-client")
|
||||
|
||||
assert options.target is not None
|
||||
assert options.target_kind is not None
|
||||
assert options.address is not None
|
||||
|
||||
|
||||
def start_debugging(argv_0):
|
||||
# We need to set up sys.argv[0] before invoking either listen() or connect(),
|
||||
# because they use it to report the "process" event. Thus, we can't rely on
|
||||
# run_path() and run_module() doing that, even though they will eventually.
|
||||
sys.argv[0] = compat.filename_str(argv_0)
|
||||
|
||||
log.debug("sys.argv after patching: {0!r}", sys.argv)
|
||||
|
||||
debugpy.configure(options.config)
|
||||
|
||||
if options.mode == "listen":
|
||||
debugpy.listen(options.address)
|
||||
elif options.mode == "connect":
|
||||
debugpy.connect(options.address, access_token=options.adapter_access_token)
|
||||
else:
|
||||
raise AssertionError(repr(options.mode))
|
||||
|
||||
if options.wait_for_client:
|
||||
debugpy.wait_for_client()
|
||||
|
||||
|
||||
def run_file():
|
||||
target = options.target
|
||||
start_debugging(target)
|
||||
|
||||
target_as_str = compat.filename_str(target)
|
||||
|
||||
# run_path has one difference with invoking Python from command-line:
|
||||
# if the target is a file (rather than a directory), it does not add its
|
||||
# parent directory to sys.path. Thus, importing other modules from the
|
||||
# same directory is broken unless sys.path is patched here.
|
||||
|
||||
if os.path.isfile(target_as_str):
|
||||
dir = os.path.dirname(target_as_str)
|
||||
sys.path.insert(0, dir)
|
||||
else:
|
||||
log.debug("Not a file: {0!r}", target)
|
||||
|
||||
log.describe_environment("Pre-launch environment:")
|
||||
|
||||
log.info("Running file {0!r}", target)
|
||||
runpy.run_path(target_as_str, run_name=compat.force_str("__main__"))
|
||||
|
||||
|
||||
def run_module():
|
||||
# Add current directory to path, like Python itself does for -m. This must
|
||||
# be in place before trying to use find_spec below to resolve submodules.
|
||||
sys.path.insert(0, str(""))
|
||||
|
||||
# We want to do the same thing that run_module() would do here, without
|
||||
# actually invoking it. On Python 3, it's exposed as a public API, but
|
||||
# on Python 2, we have to invoke a private function in runpy for this.
|
||||
# Either way, if it fails to resolve for any reason, just leave argv as is.
|
||||
argv_0 = sys.argv[0]
|
||||
target_as_str = compat.filename_str(options.target)
|
||||
try:
|
||||
if sys.version_info >= (3,):
|
||||
from importlib.util import find_spec
|
||||
|
||||
spec = find_spec(target_as_str)
|
||||
if spec is not None:
|
||||
argv_0 = spec.origin
|
||||
else:
|
||||
_, _, _, argv_0 = runpy._get_module_details(target_as_str)
|
||||
except Exception:
|
||||
log.swallow_exception("Error determining module path for sys.argv")
|
||||
|
||||
start_debugging(argv_0)
|
||||
|
||||
# On Python 2, module name must be a non-Unicode string, because it ends up
|
||||
# a part of module's __package__, and Python will refuse to run the module
|
||||
# if __package__ is Unicode.
|
||||
|
||||
log.describe_environment("Pre-launch environment:")
|
||||
log.info("Running module {0!r}", options.target)
|
||||
|
||||
# Docs say that runpy.run_module is equivalent to -m, but it's not actually
|
||||
# the case for packages - -m sets __name__ to "__main__", but run_module sets
|
||||
# it to "pkg.__main__". This breaks everything that uses the standard pattern
|
||||
# __name__ == "__main__" to detect being run as a CLI app. On the other hand,
|
||||
# runpy._run_module_as_main is a private function that actually implements -m.
|
||||
try:
|
||||
run_module_as_main = runpy._run_module_as_main
|
||||
except AttributeError:
|
||||
log.warning("runpy._run_module_as_main is missing, falling back to run_module.")
|
||||
runpy.run_module(target_as_str, alter_sys=True)
|
||||
else:
|
||||
run_module_as_main(target_as_str, alter_argv=True)
|
||||
|
||||
|
||||
def run_code():
|
||||
# Add current directory to path, like Python itself does for -c.
|
||||
sys.path.insert(0, str(""))
|
||||
code = compile(options.target, str("<string>"), str("exec"))
|
||||
|
||||
start_debugging(str("-c"))
|
||||
|
||||
log.describe_environment("Pre-launch environment:")
|
||||
log.info("Running code:\n\n{0}", options.target)
|
||||
|
||||
eval(code, {})
|
||||
|
||||
|
||||
def attach_to_pid():
|
||||
pid = options.target
|
||||
log.info("Attaching to process with PID={0}", pid)
|
||||
|
||||
encode = lambda s: list(bytearray(s.encode("utf-8"))) if s is not None else None
|
||||
|
||||
script_dir = os.path.dirname(debugpy.server.__file__)
|
||||
assert os.path.exists(script_dir)
|
||||
script_dir = encode(script_dir)
|
||||
|
||||
setup = {
|
||||
"mode": options.mode,
|
||||
"address": options.address,
|
||||
"wait_for_client": options.wait_for_client,
|
||||
"log_to": options.log_to,
|
||||
"adapter_access_token": options.adapter_access_token,
|
||||
}
|
||||
setup = encode(json.dumps(setup))
|
||||
|
||||
python_code = """
|
||||
import codecs;
|
||||
import json;
|
||||
import sys;
|
||||
|
||||
decode = lambda s: codecs.utf_8_decode(bytearray(s))[0] if s is not None else None;
|
||||
|
||||
script_dir = decode({script_dir});
|
||||
setup = json.loads(decode({setup}));
|
||||
|
||||
sys.path.insert(0, script_dir);
|
||||
import attach_pid_injected;
|
||||
del sys.path[0];
|
||||
|
||||
attach_pid_injected.attach(setup);
|
||||
"""
|
||||
python_code = (
|
||||
python_code.replace("\r", "")
|
||||
.replace("\n", "")
|
||||
.format(script_dir=script_dir, setup=setup)
|
||||
)
|
||||
log.info("Code to be injected: \n{0}", python_code.replace(";", ";\n"))
|
||||
|
||||
# pydevd restriction on characters in injected code.
|
||||
assert not (
|
||||
{'"', "'", "\r", "\n"} & set(python_code)
|
||||
), "Injected code should not contain any single quotes, double quotes, or newlines."
|
||||
|
||||
pydevd_attach_to_process_path = os.path.join(
|
||||
os.path.dirname(pydevd.__file__), "pydevd_attach_to_process"
|
||||
)
|
||||
|
||||
assert os.path.exists(pydevd_attach_to_process_path)
|
||||
sys.path.append(pydevd_attach_to_process_path)
|
||||
|
||||
try:
|
||||
import add_code_to_python_process # noqa
|
||||
|
||||
log.info("Injecting code into process with PID={0} ...", pid)
|
||||
add_code_to_python_process.run_python_code(
|
||||
pid,
|
||||
python_code,
|
||||
connect_debugger_tracing=True,
|
||||
show_debug_info=int(os.getenv("DEBUGPY_ATTACH_BY_PID_DEBUG_INFO", "0")),
|
||||
)
|
||||
except Exception:
|
||||
log.reraise_exception("Code injection into PID={0} failed:", pid)
|
||||
log.info("Code injection into PID={0} completed.", pid)
|
||||
|
||||
|
||||
def main():
|
||||
original_argv = list(sys.argv)
|
||||
try:
|
||||
parse_argv()
|
||||
except Exception as exc:
|
||||
print(str(HELP) + str("\nError: ") + str(exc), file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
if options.log_to is not None:
|
||||
debugpy.log_to(options.log_to)
|
||||
if options.log_to_stderr:
|
||||
debugpy.log_to(sys.stderr)
|
||||
|
||||
api.ensure_logging()
|
||||
|
||||
log.info(
|
||||
str("sys.argv before parsing: {0!r}\n" " after parsing: {1!r}"),
|
||||
original_argv,
|
||||
sys.argv,
|
||||
)
|
||||
|
||||
try:
|
||||
run = {
|
||||
"file": run_file,
|
||||
"module": run_module,
|
||||
"code": run_code,
|
||||
"pid": attach_to_pid,
|
||||
}[options.target_kind]
|
||||
run()
|
||||
except SystemExit as exc:
|
||||
log.reraise_exception(
|
||||
"Debuggee exited via SystemExit: {0!r}", exc.code, level="debug"
|
||||
)
|
Reference in New Issue
Block a user