Skip to content

Commit a7307ef

Browse files
committed
Applying reviewer suggestions.
1 parent 4246fdf commit a7307ef

File tree

1 file changed

+66
-51
lines changed

1 file changed

+66
-51
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 66 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,43 +21,53 @@
2121
List,
2222
Optional,
2323
Tuple,
24-
TypeGuard,
2524
TypeVar,
25+
Generic,
2626
TypedDict,
2727
Union,
2828
BinaryIO,
2929
TextIO,
3030
Literal,
3131
cast,
32+
TYPE_CHECKING,
3233
)
3334

35+
if sys.version_info >= (3, 10):
36+
from typing import TypeGuard
37+
else:
38+
if TYPE_CHECKING:
39+
from typing_extensions import TypeGuard
40+
3441
## DAP type references
3542

3643
T = TypeVar("T")
44+
Te = TypeVar("Te") # Generic type for event body
45+
Ta = TypeVar("Ta") # Generic type for request arguments
46+
Tb = TypeVar("Tb") # Generic type for response body
3747

3848

39-
class Event(TypedDict):
49+
class Event(Generic[Te], TypedDict):
4050
type: Literal["event"]
41-
seq: Literal[0]
51+
seq: int
4252
event: str
43-
body: Optional[dict]
53+
body: Optional[Te]
4454

4555

46-
class Request(TypedDict):
56+
class Request(Generic[Ta], TypedDict, total=False):
4757
type: Literal["request"]
4858
seq: int
4959
command: str
50-
arguments: Optional[dict]
60+
arguments: Ta
5161

5262

53-
class Response(TypedDict):
63+
class Response(Generic[Tb], TypedDict):
5464
type: Literal["response"]
55-
seq: Literal[0]
65+
seq: int
5666
request_seq: int
5767
success: bool
5868
command: str
5969
message: Optional[str]
60-
body: Optional[dict]
70+
body: Optional[Tb]
6171

6272

6373
ProtocolMessage = Union[Event, Request, Response]
@@ -94,14 +104,18 @@ class LaunchArguments(AttachOrLaunchArguments, total=False):
94104
launchCommands: List[str]
95105

96106

97-
class AttachArguments(AttachOrLaunchArguments, total=False):
107+
# Using the function form of TypedDict to allow for hyphenated keys.
108+
AttachGdbServer = TypedDict(
109+
"AttachGdbServer", {"gdb-remote-port": int, "gdb-remote-hostname": str}, total=False
110+
)
111+
112+
113+
class AttachArguments(AttachGdbServer, AttachOrLaunchArguments, total=False):
98114
program: str
99115
pid: int
100116
waitFor: bool
101117
attachCommands: List[str]
102118
coreFile: str
103-
gdbRemotePort: int
104-
gdbRemoteHostname: str
105119

106120

107121
class BreakpointData(TypedDict, total=False):
@@ -159,7 +173,7 @@ def dump_memory(base_addr, data, num_per_line, outfile):
159173

160174

161175
def read_packet(
162-
f: IO[bytes], verbose: bool = False, trace_file: Optional[IO[str]] = None
176+
f: IO[bytes], trace_file: Optional[IO[str]] = None
163177
) -> Optional[ProtocolMessage]:
164178
"""Decode a JSON packet that starts with the content length and is
165179
followed by the JSON bytes from a file 'f'. Returns None on EOF.
@@ -172,19 +186,11 @@ def read_packet(
172186
prefix = "Content-Length: "
173187
if line.startswith(prefix):
174188
# Decode length of JSON bytes
175-
if verbose:
176-
print(f"content: {line}")
177189
length = int(line[len(prefix) :])
178-
if verbose:
179-
print(f"length: {length}")
180190
# Skip empty line
181191
line = f.readline().decode()
182-
if verbose:
183-
print(f"empty: {line!r}")
184192
# Read JSON bytes
185193
json_str = f.read(length)
186-
if verbose:
187-
print(f"json: {json_str!r}")
188194
if trace_file:
189195
trace_file.write(f"from adapter:\n{json_str!r}\n")
190196
# Decode the JSON bytes into a python dictionary
@@ -217,12 +223,12 @@ def name(self) -> Optional[str]:
217223
def __init__(
218224
self, path: Optional[str] = None, source_reference: Optional[int] = None
219225
):
220-
self.path = path
221-
self.source_reference = source_reference
222-
223226
if path is None and source_reference is None:
224227
raise ValueError("Either path or source_reference must be provided")
225228

229+
self.path = path
230+
self.source_reference = source_reference
231+
226232
def to_DAP(self) -> dict:
227233
if self.path:
228234
return {"path": self.path, "name": self.name}
@@ -238,7 +244,7 @@ def __init__(
238244
self,
239245
recv: BinaryIO,
240246
send: BinaryIO,
241-
init_commands: list[str],
247+
init_commands: List[str],
242248
log_file: Optional[TextIO] = None,
243249
):
244250
# For debugging test failures, try setting `trace_file = sys.stderr`.
@@ -334,10 +340,10 @@ def _recv_packet(
334340
predicate: Optional[Callable[[ProtocolMessage], bool]] = None,
335341
timeout: Optional[float] = None,
336342
) -> Optional[ProtocolMessage]:
337-
"""Processes recived packets from the adapter.
343+
"""Processes received packets from the adapter.
338344
339345
Updates the DebugCommunication stateful properties based on the received
340-
packets in the order they are recieved.
346+
packets in the order they are received.
341347
342348
NOTE: The only time the session state properties should be updated is
343349
during this call to ensure consistency during tests.
@@ -394,7 +400,7 @@ def _handle_event(self, packet: Event) -> None:
394400
event = packet["event"]
395401
body: Optional[Dict] = packet.get("body", None)
396402

397-
if event == "output":
403+
if event == "output" and body:
398404
# Store any output we receive so clients can retrieve it later.
399405
category = body["category"]
400406
output = body["output"]
@@ -408,21 +414,19 @@ def _handle_event(self, packet: Event) -> None:
408414
# When a new process is attached or launched, remember the
409415
# details that are available in the body of the event
410416
self.process_event_body = body
411-
elif event == "exited":
417+
elif event == "exited" and body:
412418
# Process exited, mark the status to indicate the process is not
413419
# alive.
414420
self.exit_status = body["exitCode"]
415-
elif event == "continued":
421+
elif event == "continued" and body:
416422
# When the process continues, clear the known threads and
417423
# thread_stop_reasons.
418-
all_threads_continued = (
419-
body.get("allThreadsContinued", True) if body else True
420-
)
424+
all_threads_continued = body.get("allThreadsContinued", True)
421425
tid = body["threadId"]
422426
if tid in self.thread_stop_reasons:
423427
del self.thread_stop_reasons[tid]
424428
self._process_continued(all_threads_continued)
425-
elif event == "stopped":
429+
elif event == "stopped" and body:
426430
# Each thread that stops with a reason will send a
427431
# 'stopped' event. We need to remember the thread stop
428432
# reasons since the 'threads' command doesn't return
@@ -435,10 +439,12 @@ def _handle_event(self, packet: Event) -> None:
435439
# and 'progressEnd' events. Keep these around in case test
436440
# cases want to verify them.
437441
self.progress_events.append(packet)
438-
elif event == "breakpoint":
442+
elif event == "breakpoint" and body:
439443
# Breakpoint events are sent when a breakpoint is resolved
440444
self._update_verified_breakpoints([body["breakpoint"]])
441-
elif event == "capabilities":
445+
elif event == "capabilities" and body:
446+
if self.capabilities is None:
447+
self.capabilities = {}
442448
# Update the capabilities with new ones from the event.
443449
self.capabilities.update(body["capabilities"])
444450

@@ -496,26 +502,23 @@ def _process_continued(self, all_threads_continued: bool):
496502
self.thread_stop_reasons = {}
497503

498504
def _update_verified_breakpoints(self, breakpoints: list[Breakpoint]):
499-
for breakpoint in breakpoints:
505+
for bp in breakpoints:
500506
# If no id is set, we cannot correlate the given breakpoint across
501507
# requests, ignore it.
502-
if "id" not in breakpoint:
508+
if "id" not in bp:
503509
continue
504510

505-
self.resolved_breakpoints[str(breakpoint["id"])] = breakpoint.get(
506-
"verified", False
507-
)
511+
self.resolved_breakpoints[str(bp["id"])] = bp.get("verified", False)
508512

509-
def _send_recv(self, request: Request) -> Optional[Response]:
513+
def _send_recv(self, request: Request[Ta]) -> Optional[Response[Tb]]:
510514
"""Send a command python dictionary as JSON and receive the JSON
511515
response. Validates that the response is the correct sequence and
512516
command in the reply. Any events that are received are added to the
513517
events list in this object"""
514518
seq = self.send_packet(request)
515519
response = self.receive_response(seq)
516520
if response is None:
517-
desc = 'no response for "%s"' % (request["command"])
518-
raise ValueError(desc)
521+
raise ValueError(f"no response for {request!r}")
519522
self.validate_response(request, response)
520523
return response
521524

@@ -592,7 +595,9 @@ def collect_output(
592595
"""
593596
deadline = time.monotonic() + timeout_secs
594597
output = self.get_output(category, clear)
595-
while deadline >= time.monotonic() and pattern is None or pattern not in output:
598+
while deadline >= time.monotonic() and (
599+
pattern is None or pattern not in output
600+
):
596601
event = self.wait_for_event(["output"], timeout=deadline - time.monotonic())
597602
if not event: # Timeout or EOF
598603
break
@@ -874,7 +879,11 @@ def request_attach(
874879
args_dict["gdb-remote-port"] = gdbRemotePort
875880
if gdbRemoteHostname is not None:
876881
args_dict["gdb-remote-hostname"] = gdbRemoteHostname
877-
command_dict = {"command": "attach", "type": "request", "arguments": args_dict}
882+
command_dict: Request = {
883+
"command": "attach",
884+
"type": "request",
885+
"arguments": args_dict,
886+
}
878887
return self._send_recv(command_dict)
879888

880889
def request_breakpointLocations(
@@ -1130,7 +1139,11 @@ def request_launch(
11301139
args_dict["displayExtendedBacktrace"] = displayExtendedBacktrace
11311140
if commandEscapePrefix is not None:
11321141
args_dict["commandEscapePrefix"] = commandEscapePrefix
1133-
command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
1142+
command_dict: Request = {
1143+
"command": "launch",
1144+
"type": "request",
1145+
"arguments": args_dict,
1146+
}
11341147
return self._send_recv(command_dict)
11351148

11361149
def request_next(self, threadId, granularity="statement"):
@@ -1190,12 +1203,14 @@ def request_setBreakpoints(
11901203
self,
11911204
source: Union[Source, str],
11921205
line_array: Optional[List[int]],
1193-
data: Optional[List[BreakpiontData]] = None,
1206+
data: Optional[List[BreakpointData]] = None,
11941207
):
11951208
"""data is array of parameters for breakpoints in line_array.
11961209
Each parameter object is 1:1 mapping with entries in line_entry.
11971210
It contains optional location/hitCondition/logMessage parameters.
11981211
"""
1212+
if isinstance(source, str):
1213+
source = Source(path=source)
11991214
args_dict = {
12001215
"source": source.to_DAP(),
12011216
"sourceModified": False,
@@ -1204,20 +1219,20 @@ def request_setBreakpoints(
12041219
args_dict["lines"] = line_array
12051220
breakpoints = []
12061221
for i, line in enumerate(line_array):
1207-
breakpoint_data: BreakpiontData = {}
1222+
breakpoint_data: BreakpointData = {}
12081223
if data is not None and i < len(data):
12091224
breakpoint_data = data[i]
12101225
bp: SourceBreakpoint = {"line": line, **breakpoint_data}
12111226
breakpoints.append(bp)
12121227
args_dict["breakpoints"] = breakpoints
12131228

1214-
command_dict = {
1229+
command_dict: Request = {
12151230
"command": "setBreakpoints",
12161231
"type": "request",
12171232
"arguments": args_dict,
12181233
}
12191234
response = self._send_recv(command_dict)
1220-
if response["success"]:
1235+
if response and response["success"] and response["body"]:
12211236
self._update_verified_breakpoints(response["body"]["breakpoints"])
12221237
return response
12231238

0 commit comments

Comments
 (0)