Skip to content

Commit b358af1

Browse files
authored
[lldb] Add type hints to gdbclientutils.py and use abstract base class (llvm#162172)
Everything in this commit should be python 3.8 compatible which has required using older styles of type hints (e.g. `Optional[T]` rather than 3.10's `T | None` and `List[T]` rather than 3.9's `list[T]`. There are some python 3.9 type hints in other files which have not been changed by this commit. Issues: qEcho() is passed an argument by the callers that the function didn't have Several functions in the base class would silently do nothing if not overriden. These now use `@abstractmethod` to require overrides sendall() had inconsistent return types between overrides Compatibility was checked with: ``` uvx vermin -t 3.8 $(find lldb/packages/Python -name '*.py') ``` Compability of the type hints was checked with: ``` uvx vermin -t 3.8 --eval-annotations $(find lldb/packages/Python -name '*.py') ``` and type hint correctness was checked with ``` uvx pyright lldb/packages/Python/lldbsuite/test/gdbclientutils.py ```
1 parent d6b22a3 commit b358af1

File tree

1 file changed

+43
-27
lines changed

1 file changed

+43
-27
lines changed

lldb/packages/Python/lldbsuite/test/gdbclientutils.py

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
from abc import ABC, abstractmethod
12
import ctypes
23
import errno
34
import io
45
import threading
56
import socket
67
import traceback
78
from lldbsuite.support import seven
9+
from typing import Optional, List, Tuple
810

911

1012
def checksum(message):
@@ -86,7 +88,7 @@ class MockGDBServerResponder:
8688
handles any packet not recognized in the common packet handling code.
8789
"""
8890

89-
registerCount = 40
91+
registerCount: int = 40
9092

9193
class RESPONSE_DISCONNECT:
9294
pass
@@ -95,7 +97,7 @@ class RESPONSE_NONE:
9597
pass
9698

9799
def __init__(self):
98-
self.packetLog = []
100+
self.packetLog: List[str] = []
99101

100102
def respond(self, packet):
101103
"""
@@ -241,7 +243,7 @@ def qProcessInfo(self):
241243
def qHostInfo(self):
242244
return "ptrsize:8;endian:little;"
243245

244-
def qEcho(self):
246+
def qEcho(self, num: int):
245247
return "E04"
246248

247249
def qQueryGDBServer(self):
@@ -262,10 +264,10 @@ def A(self, packet):
262264
def D(self, packet):
263265
return "OK"
264266

265-
def readRegisters(self):
267+
def readRegisters(self) -> str:
266268
return "00000000" * self.registerCount
267269

268-
def readRegister(self, register):
270+
def readRegister(self, register: int) -> str:
269271
return "00000000"
270272

271273
def writeRegisters(self, registers_hex):
@@ -305,7 +307,9 @@ def haltReason(self):
305307
# SIGINT is 2, return type is 2 digit hex string
306308
return "S02"
307309

308-
def qXferRead(self, obj, annex, offset, length):
310+
def qXferRead(
311+
self, obj: str, annex: str, offset: int, length: int
312+
) -> Tuple[Optional[str], bool]:
309313
return None, False
310314

311315
def _qXferResponse(self, data, has_more):
@@ -373,15 +377,17 @@ class UnexpectedPacketException(Exception):
373377
pass
374378

375379

376-
class ServerChannel:
380+
class ServerChannel(ABC):
377381
"""
378382
A wrapper class for TCP or pty-based server.
379383
"""
380384

381-
def get_connect_address(self):
385+
@abstractmethod
386+
def get_connect_address(self) -> str:
382387
"""Get address for the client to connect to."""
383388

384-
def get_connect_url(self):
389+
@abstractmethod
390+
def get_connect_url(self) -> str:
385391
"""Get URL suitable for process connect command."""
386392

387393
def close_server(self):
@@ -393,10 +399,12 @@ def accept(self):
393399
def close_connection(self):
394400
"""Close all resources used by the accepted connection."""
395401

396-
def recv(self):
402+
@abstractmethod
403+
def recv(self) -> bytes:
397404
"""Receive a data packet from the connected client."""
398405

399-
def sendall(self, data):
406+
@abstractmethod
407+
def sendall(self, data: bytes) -> None:
400408
"""Send the data to the connected client."""
401409

402410

@@ -427,11 +435,11 @@ def close_connection(self):
427435
self._connection.close()
428436
self._connection = None
429437

430-
def recv(self):
438+
def recv(self) -> bytes:
431439
assert self._connection is not None
432440
return self._connection.recv(4096)
433441

434-
def sendall(self, data):
442+
def sendall(self, data: bytes) -> None:
435443
assert self._connection is not None
436444
return self._connection.sendall(data)
437445

@@ -443,21 +451,21 @@ def __init__(self):
443451
)[0]
444452
super().__init__(family, type, proto, addr)
445453

446-
def get_connect_address(self):
454+
def get_connect_address(self) -> str:
447455
return "[{}]:{}".format(*self._server_socket.getsockname())
448456

449-
def get_connect_url(self):
457+
def get_connect_url(self) -> str:
450458
return "connect://" + self.get_connect_address()
451459

452460

453461
class UnixServerSocket(ServerSocket):
454462
def __init__(self, addr):
455463
super().__init__(socket.AF_UNIX, socket.SOCK_STREAM, 0, addr)
456464

457-
def get_connect_address(self):
465+
def get_connect_address(self) -> str:
458466
return self._server_socket.getsockname()
459467

460-
def get_connect_url(self):
468+
def get_connect_url(self) -> str:
461469
return "unix-connect://" + self.get_connect_address()
462470

463471

@@ -471,7 +479,7 @@ def __init__(self):
471479
self._primary = io.FileIO(primary, "r+b")
472480
self._secondary = io.FileIO(secondary, "r+b")
473481

474-
def get_connect_address(self):
482+
def get_connect_address(self) -> str:
475483
libc = ctypes.CDLL(None)
476484
libc.ptsname.argtypes = (ctypes.c_int,)
477485
libc.ptsname.restype = ctypes.c_char_p
@@ -484,7 +492,7 @@ def close_server(self):
484492
self._secondary.close()
485493
self._primary.close()
486494

487-
def recv(self):
495+
def recv(self) -> bytes:
488496
try:
489497
return self._primary.read(4096)
490498
except OSError as e:
@@ -493,8 +501,8 @@ def recv(self):
493501
return b""
494502
raise
495503

496-
def sendall(self, data):
497-
return self._primary.write(data)
504+
def sendall(self, data: bytes) -> None:
505+
self._primary.write(data)
498506

499507

500508
class MockGDBServer:
@@ -527,18 +535,21 @@ def stop(self):
527535
self._thread.join()
528536
self._thread = None
529537

530-
def get_connect_address(self):
538+
def get_connect_address(self) -> str:
539+
assert self._socket is not None
531540
return self._socket.get_connect_address()
532541

533-
def get_connect_url(self):
542+
def get_connect_url(self) -> str:
543+
assert self._socket is not None
534544
return self._socket.get_connect_url()
535545

536546
def run(self):
547+
assert self._socket is not None
537548
# For testing purposes, we only need to worry about one client
538549
# connecting just one time.
539550
try:
540551
self._socket.accept()
541-
except:
552+
except Exception:
542553
traceback.print_exc()
543554
return
544555
self._shouldSendAck = True
@@ -553,7 +564,7 @@ def run(self):
553564
self._receive(data)
554565
except self.TerminateConnectionException:
555566
pass
556-
except Exception as e:
567+
except Exception:
557568
print(
558569
"An exception happened when receiving the response from the gdb server. Closing the client..."
559570
)
@@ -586,7 +597,9 @@ def _parsePacket(self):
586597
Once a complete packet is found at the front of self._receivedData,
587598
its data is removed form self._receivedData.
588599
"""
600+
assert self._receivedData is not None
589601
data = self._receivedData
602+
assert self._receivedDataOffset is not None
590603
i = self._receivedDataOffset
591604
data_len = len(data)
592605
if data_len == 0:
@@ -639,10 +652,13 @@ def _parsePacket(self):
639652
self._receivedDataOffset = 0
640653
return packet
641654

642-
def _sendPacket(self, packet):
643-
self._socket.sendall(seven.bitcast_to_bytes(frame_packet(packet)))
655+
def _sendPacket(self, packet: str):
656+
assert self._socket is not None
657+
framed_packet = seven.bitcast_to_bytes(frame_packet(packet))
658+
self._socket.sendall(framed_packet)
644659

645660
def _handlePacket(self, packet):
661+
assert self._socket is not None
646662
if packet is self.PACKET_ACK:
647663
# Ignore ACKs from the client. For the future, we can consider
648664
# adding validation code to make sure the client only sends ACKs

0 commit comments

Comments
 (0)