mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Finish typing debupy python code
This commit is contained in:
parent
e528e2dbe3
commit
622e3c6967
8 changed files with 75 additions and 55 deletions
|
|
@ -8,6 +8,7 @@ import codecs
|
|||
import locale
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
# WARNING: debugpy and submodules must not be imported on top level in this module,
|
||||
# and should be imported locally inside main() instead.
|
||||
|
|
@ -53,7 +54,7 @@ def main(args):
|
|||
if args.for_server is None:
|
||||
adapter.access_token = codecs.encode(os.urandom(32), "hex").decode("ascii")
|
||||
|
||||
endpoints = {}
|
||||
endpoints: dict[str, Any] = {}
|
||||
try:
|
||||
client_host, client_port = clients.serve(args.host, args.port)
|
||||
except Exception as exc:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ from __future__ import annotations
|
|||
|
||||
import atexit
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
from typing import Literal, Union
|
||||
|
||||
import debugpy
|
||||
from debugpy import adapter, common, launcher
|
||||
|
|
@ -41,7 +43,7 @@ class Client(components.Component):
|
|||
"pathFormat": json.enum("path", optional=True), # we don't support "uri"
|
||||
}
|
||||
|
||||
def __init__(self, sock):
|
||||
def __init__(self, sock: Union[Literal["stdio"], socket.socket]):
|
||||
if sock == "stdio":
|
||||
log.info("Connecting to client over stdio...", self)
|
||||
self.using_stdio = True
|
||||
|
|
@ -67,7 +69,7 @@ class Client(components.Component):
|
|||
fully handled.
|
||||
"""
|
||||
|
||||
self.start_request = None
|
||||
self.start_request: Union[messaging.Request, None] = None
|
||||
"""The "launch" or "attach" request as received from the client.
|
||||
"""
|
||||
|
||||
|
|
@ -124,11 +126,12 @@ class Client(components.Component):
|
|||
self.client.channel.propagate(event)
|
||||
|
||||
def _propagate_deferred_events(self):
|
||||
log.debug("Propagating deferred events to {0}...", self.client)
|
||||
for event in self._deferred_events:
|
||||
log.debug("Propagating deferred {0}", event.describe())
|
||||
self.client.channel.propagate(event)
|
||||
log.info("All deferred events propagated to {0}.", self.client)
|
||||
if self._deferred_events is not None:
|
||||
log.debug("Propagating deferred events to {0}...", self.client)
|
||||
for event in self._deferred_events:
|
||||
log.debug("Propagating deferred {0}", event.describe())
|
||||
self.client.channel.propagate(event)
|
||||
log.info("All deferred events propagated to {0}.", self.client)
|
||||
self._deferred_events = None
|
||||
|
||||
# Generic event handler. There are no specific handlers for client events, because
|
||||
|
|
@ -202,9 +205,10 @@ class Client(components.Component):
|
|||
#
|
||||
# See https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522
|
||||
# for the sequence of request and events necessary to orchestrate the start.
|
||||
@staticmethod
|
||||
def _start_message_handler(f):
|
||||
@components.Component.message_handler
|
||||
def handle(self, request):
|
||||
def handle(self, request: messaging.Message):
|
||||
assert request.is_request("launch", "attach")
|
||||
if self._initialize_request is None:
|
||||
raise request.isnt_valid("Session is not initialized yet")
|
||||
|
|
@ -215,15 +219,16 @@ class Client(components.Component):
|
|||
if self.session.no_debug:
|
||||
servers.dont_wait_for_first_connection()
|
||||
|
||||
request_options = request("debugOptions", json.array(str))
|
||||
self.session.debug_options = debug_options = set(
|
||||
request("debugOptions", json.array(str))
|
||||
|
||||
)
|
||||
|
||||
f(self, request)
|
||||
if request.response is not None:
|
||||
if isinstance(request, messaging.Request) and request.response is not None:
|
||||
return
|
||||
|
||||
if self.server:
|
||||
if self.server and isinstance(request, messaging.Request):
|
||||
self.server.initialize(self._initialize_request)
|
||||
self._initialize_request = None
|
||||
|
||||
|
|
@ -267,7 +272,7 @@ class Client(components.Component):
|
|||
except messaging.MessageHandlingError as exc:
|
||||
exc.propagate(request)
|
||||
|
||||
if self.session.no_debug:
|
||||
if self.session.no_debug and isinstance(request, messaging.Request):
|
||||
self.start_request = request
|
||||
self.has_started = True
|
||||
request.respond({})
|
||||
|
|
@ -335,6 +340,7 @@ class Client(components.Component):
|
|||
launcher_python = python[0]
|
||||
|
||||
program = module = code = ()
|
||||
args = []
|
||||
if "program" in request:
|
||||
program = request("program", str)
|
||||
args = [program]
|
||||
|
|
@ -391,7 +397,7 @@ class Client(components.Component):
|
|||
if cwd == ():
|
||||
# If it's not specified, but we're launching a file rather than a module,
|
||||
# and the specified path has a directory in it, use that.
|
||||
cwd = None if program == () else (os.path.dirname(program) or None)
|
||||
cwd = None if program == () else (os.path.dirname(str(program)) or None)
|
||||
|
||||
sudo = bool(property_or_debug_option("sudo", "Sudo"))
|
||||
if sudo and sys.platform == "win32":
|
||||
|
|
@ -484,7 +490,7 @@ class Client(components.Component):
|
|||
else:
|
||||
if not servers.is_serving():
|
||||
servers.serve()
|
||||
host, port = servers.listener.getsockname()
|
||||
host, port = servers.listener.getsockname() if servers.listener is not None else ("", 0)
|
||||
|
||||
# There are four distinct possibilities here.
|
||||
#
|
||||
|
|
@ -576,9 +582,9 @@ class Client(components.Component):
|
|||
request.cant_handle("{0} is already being debugged.", conn)
|
||||
|
||||
@message_handler
|
||||
def configurationDone_request(self, request):
|
||||
def configurationDone_request(self, request: messaging.Request):
|
||||
if self.start_request is None or self.has_started:
|
||||
request.cant_handle(
|
||||
raise request.cant_handle(
|
||||
'"configurationDone" is only allowed during handling of a "launch" '
|
||||
'or an "attach" request'
|
||||
)
|
||||
|
|
@ -623,7 +629,8 @@ class Client(components.Component):
|
|||
def handle_response(response):
|
||||
request.respond(response.body)
|
||||
|
||||
propagated_request.on_response(handle_response)
|
||||
if propagated_request is not None:
|
||||
propagated_request.on_response(handle_response)
|
||||
|
||||
return messaging.NO_RESPONSE
|
||||
|
||||
|
|
@ -649,7 +656,7 @@ class Client(components.Component):
|
|||
result = {"debugpy": {"version": debugpy.__version__}}
|
||||
if self.server:
|
||||
try:
|
||||
pydevd_info = self.server.channel.request("pydevdSystemInfo")
|
||||
pydevd_info: messaging.AssociatableMessageDict = self.server.channel.request("pydevdSystemInfo")
|
||||
except Exception:
|
||||
# If the server has already disconnected, or couldn't handle it,
|
||||
# report what we've got.
|
||||
|
|
@ -754,7 +761,7 @@ class Client(components.Component):
|
|||
if "host" not in body["connect"]:
|
||||
body["connect"]["host"] = host if host is not None else "127.0.0.1"
|
||||
if "port" not in body["connect"]:
|
||||
if port is None:
|
||||
if port is None and listener is not None:
|
||||
_, port = listener.getsockname()
|
||||
body["connect"]["port"] = port
|
||||
|
||||
|
|
@ -770,7 +777,7 @@ class Client(components.Component):
|
|||
|
||||
def serve(host, port):
|
||||
global listener
|
||||
listener = sockets.serve("Client", Client, host, port)
|
||||
listener = sockets.serve("Client", Client, host, port) # type: ignore
|
||||
sessions.report_sockets()
|
||||
return listener.getsockname()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# for license information.
|
||||
|
||||
import functools
|
||||
from typing import Union
|
||||
from typing import Type, TypeVar, Union, cast
|
||||
|
||||
from debugpy.adapter.sessions import Session
|
||||
from debugpy.common import json, log, messaging, util
|
||||
|
|
@ -33,7 +33,7 @@ class Component(util.Observable):
|
|||
to wait_for() a change caused by another component.
|
||||
"""
|
||||
|
||||
def __init__(self, session: Session, stream: Union[messaging.JsonIOStream, None]=None, channel=None):
|
||||
def __init__(self, session: Session, stream: Union[messaging.JsonIOStream, None]=None, channel: Union[messaging.JsonMessageChannel, None]=None):
|
||||
assert (stream is None) ^ (channel is None)
|
||||
|
||||
try:
|
||||
|
|
@ -53,6 +53,7 @@ class Component(util.Observable):
|
|||
elif channel is not None:
|
||||
channel.name = channel.stream.name = str(self)
|
||||
channel.handlers = self
|
||||
assert channel is not None
|
||||
self.channel = channel
|
||||
self.is_connected = True
|
||||
|
||||
|
|
@ -110,8 +111,9 @@ class Component(util.Observable):
|
|||
self.is_connected = False
|
||||
self.session.finalize("{0} has disconnected".format(self))
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
def missing(session, type):
|
||||
def missing(session, type: Type[T]) -> T:
|
||||
class Missing(object):
|
||||
"""A dummy component that raises ComponentNotAvailable whenever some
|
||||
attribute is accessed on it.
|
||||
|
|
@ -126,7 +128,7 @@ def missing(session, type):
|
|||
except Exception as exc:
|
||||
log.reraise_exception("{0} in {1}", exc, session)
|
||||
|
||||
return Missing()
|
||||
return cast(type, Missing())
|
||||
|
||||
|
||||
class Capabilities(dict):
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ class Server(components.Component):
|
|||
|
||||
# Generic request handler, used if there's no specific handler below.
|
||||
@message_handler
|
||||
def request(self, request):
|
||||
def request(self, request: messaging.Message):
|
||||
# Do not delegate requests from the server by default. There is a security
|
||||
# boundary between the server and the adapter, and we cannot trust arbitrary
|
||||
# requests sent over that boundary, since they may contain arbitrary code
|
||||
|
|
@ -397,7 +397,7 @@ class Server(components.Component):
|
|||
|
||||
def serve(host="127.0.0.1", port=0):
|
||||
global listener
|
||||
listener = sockets.serve("Server", Connection, host, port)
|
||||
listener = sockets.serve("Server", Connection, host, port) # type: ignore
|
||||
sessions.report_sockets()
|
||||
return listener.getsockname()
|
||||
|
||||
|
|
@ -422,21 +422,21 @@ def connections():
|
|||
return list(_connections)
|
||||
|
||||
|
||||
def wait_for_connection(session, predicate, timeout=None):
|
||||
def wait_for_connection(session, predicate, timeout: Union[float, None]=None):
|
||||
"""Waits until there is a server matching the specified predicate connected to
|
||||
this adapter, and returns the corresponding Connection.
|
||||
|
||||
If there is more than one server connection already available, returns the oldest
|
||||
one.
|
||||
"""
|
||||
|
||||
def wait_for_timeout():
|
||||
time.sleep(timeout)
|
||||
wait_for_timeout.timed_out = True
|
||||
if timeout is not None:
|
||||
time.sleep(timeout)
|
||||
setattr(wait_for_timeout, "timed_out", True)
|
||||
with _lock:
|
||||
_connections_changed.set()
|
||||
|
||||
wait_for_timeout.timed_out = timeout == 0
|
||||
setattr(wait_for_timeout, "timed_out", timeout == 0)
|
||||
if timeout:
|
||||
thread = threading.Thread(
|
||||
target=wait_for_timeout, name="servers.wait_for_connection() timeout"
|
||||
|
|
@ -451,7 +451,7 @@ def wait_for_connection(session, predicate, timeout=None):
|
|||
_connections_changed.clear()
|
||||
conns = (conn for conn in _connections if predicate(conn))
|
||||
conn = next(conns, None)
|
||||
if conn is not None or wait_for_timeout.timed_out:
|
||||
if conn is not None or getattr(wait_for_timeout, "timed_out") is True:
|
||||
return conn
|
||||
_connections_changed.wait()
|
||||
|
||||
|
|
@ -479,7 +479,7 @@ def dont_wait_for_first_connection():
|
|||
|
||||
|
||||
def inject(pid, debugpy_args, on_output):
|
||||
host, port = listener.getsockname()
|
||||
host, port = listener.getsockname() if listener is not None else ("", 0)
|
||||
|
||||
cmdline = [
|
||||
sys.executable,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import os
|
|||
import signal
|
||||
import threading
|
||||
import time
|
||||
from typing import Union
|
||||
from typing import Union, cast
|
||||
|
||||
from debugpy import common
|
||||
from debugpy.common import log, util
|
||||
|
|
@ -182,7 +182,7 @@ class Session(util.Observable):
|
|||
# can ask the launcher to kill it, do so instead of disconnecting
|
||||
# from the server to prevent debuggee from running any more code.
|
||||
self.launcher.terminate_debuggee()
|
||||
else:
|
||||
elif self.server.channel is not None:
|
||||
# Otherwise, let the server handle it the best it can.
|
||||
try:
|
||||
self.server.channel.request(
|
||||
|
|
@ -220,7 +220,8 @@ class Session(util.Observable):
|
|||
self.wait_for(lambda: not self.launcher.is_connected)
|
||||
|
||||
try:
|
||||
self.launcher.channel.close()
|
||||
if self.launcher.channel is not None:
|
||||
self.launcher.channel.close()
|
||||
except Exception:
|
||||
log.swallow_exception()
|
||||
|
||||
|
|
@ -232,7 +233,8 @@ class Session(util.Observable):
|
|||
if self.client.restart_requested:
|
||||
body["restart"] = True
|
||||
try:
|
||||
self.client.channel.send_event("terminated", body)
|
||||
if self.client.channel is not None:
|
||||
self.client.channel.send_event("terminated", body)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import builtins
|
|||
import json
|
||||
import numbers
|
||||
import operator
|
||||
from typing import Any, Callable, Literal, Tuple, Union
|
||||
|
||||
|
||||
JsonDecoder = json.JSONDecoder
|
||||
|
|
@ -21,14 +22,14 @@ class JsonEncoder(json.JSONEncoder):
|
|||
result is serialized instead of the object itself.
|
||||
"""
|
||||
|
||||
def default(self, value):
|
||||
def default(self, o):
|
||||
try:
|
||||
get_state = value.__getstate__
|
||||
get_state = o.__getstate__
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
return get_state()
|
||||
return super().default(value)
|
||||
return super().default(o)
|
||||
|
||||
|
||||
class JsonObject(object):
|
||||
|
|
@ -93,12 +94,12 @@ class JsonObject(object):
|
|||
# some substitutions - e.g. replacing () with some default value.
|
||||
|
||||
|
||||
def _converter(value, classinfo):
|
||||
def _converter(value: str, classinfo) -> Any:
|
||||
"""Convert value (str) to number, otherwise return None if is not possible"""
|
||||
for one_info in classinfo:
|
||||
if issubclass(one_info, numbers.Number):
|
||||
try:
|
||||
return one_info(value)
|
||||
return one_info(value) # pyright: ignore
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
|
@ -171,7 +172,7 @@ def enum(*values, **kwargs):
|
|||
return validate
|
||||
|
||||
|
||||
def array(validate_item=False, vectorize=False, size=None):
|
||||
def array(validate_item: Union[Callable[..., Any], Literal[False]]=False, vectorize=False, size=None):
|
||||
"""Returns a validator for a JSON array.
|
||||
|
||||
If the property is missing, it is treated as if it were []. Otherwise, it must
|
||||
|
|
@ -213,11 +214,11 @@ def array(validate_item=False, vectorize=False, size=None):
|
|||
)
|
||||
elif isinstance(size, tuple):
|
||||
assert 1 <= len(size) <= 2
|
||||
size = tuple(operator.index(n) for n in size)
|
||||
min_len, max_len = (size + (None,))[0:2]
|
||||
sizes = tuple(operator.index(n) for n in size)
|
||||
min_len, max_len = (sizes + (None,))[0:2]
|
||||
validate_size = lambda value: (
|
||||
"must have at least {0} elements".format(min_len)
|
||||
if len(value) < min_len
|
||||
if min_len is None or len(value) < min_len
|
||||
else "must have at most {0} elements".format(max_len)
|
||||
if max_len is not None and len(value) < max_len
|
||||
else True
|
||||
|
|
@ -250,7 +251,7 @@ def array(validate_item=False, vectorize=False, size=None):
|
|||
return validate
|
||||
|
||||
|
||||
def object(validate_value=False):
|
||||
def object(validate_value: Union[Callable[..., Any], Tuple, Literal[False]]=False):
|
||||
"""Returns a validator for a JSON object.
|
||||
|
||||
If the property is missing, it is treated as if it were {}. Otherwise, it must
|
||||
|
|
|
|||
|
|
@ -344,7 +344,7 @@ class MessageDict(collections.OrderedDict):
|
|||
such guarantee for outgoing messages.
|
||||
"""
|
||||
|
||||
def __init__(self, message, items=None):
|
||||
def __init__(self, message, items: Union[dict, None]=None):
|
||||
assert message is None or isinstance(message, Message)
|
||||
|
||||
if items is None:
|
||||
|
|
@ -683,7 +683,7 @@ class Request(Message):
|
|||
arguments.associate_with(self)
|
||||
self.arguments = arguments
|
||||
|
||||
self.response = None
|
||||
self.response: Union[Response, None] = None
|
||||
"""Response to this request.
|
||||
|
||||
For incoming requests, it is set as soon as the request handler returns.
|
||||
|
|
@ -809,7 +809,7 @@ class OutgoingRequest(Request):
|
|||
while self.response is None:
|
||||
self.channel._handlers_enqueued.wait()
|
||||
|
||||
if raise_if_failed and not self.response.success and isinstance( self.response.body, Exception):
|
||||
if raise_if_failed and not self.response.success and isinstance( self.response.body, BaseException):
|
||||
raise self.response.body
|
||||
return self.response.body
|
||||
|
||||
|
|
@ -1297,7 +1297,10 @@ class JsonMessageChannel(object):
|
|||
|
||||
def request(self, *args, **kwargs):
|
||||
"""Same as send_request(...).wait_for_response()"""
|
||||
return self.send_request(*args, **kwargs).wait_for_response()
|
||||
# This should always raise an exception on failure
|
||||
result = self.send_request(*args, **kwargs).wait_for_response()
|
||||
assert not isinstance(result, BaseException)
|
||||
return result
|
||||
|
||||
def propagate(self, message):
|
||||
"""Sends a new message with the same type and payload.
|
||||
|
|
|
|||
|
|
@ -86,12 +86,16 @@ class Singleton(object):
|
|||
|
||||
def __enter__(self):
|
||||
"""Lock this singleton to prevent concurrent access."""
|
||||
type(self)._lock.acquire()
|
||||
lock = type(self)._lock
|
||||
assert lock is not None
|
||||
lock.acquire()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||
"""Unlock this singleton to allow concurrent access."""
|
||||
type(self)._lock.release()
|
||||
lock = type(self)._lock
|
||||
assert lock is not None
|
||||
lock.release()
|
||||
|
||||
def share(self):
|
||||
"""Share this singleton, if it was originally created with shared=False."""
|
||||
|
|
@ -138,7 +142,7 @@ class ThreadSafeSingleton(Singleton):
|
|||
# ensure thread safety for the callers.
|
||||
|
||||
@staticmethod
|
||||
def assert_locked(self):
|
||||
def assert_locked(self): # type: ignore
|
||||
lock = type(self)._lock
|
||||
assert lock.acquire(blocking=False), (
|
||||
"ThreadSafeSingleton accessed without locking. Either use with-statement, "
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue