From c8a12174c34f3065f405c3529312f9df37d7c5f7 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sat, 4 Oct 2025 15:56:24 +0200 Subject: [PATCH 1/6] varlink: type return `None` where possible --- varlink/client.py | 10 +++++----- varlink/mock.py | 10 +++++----- varlink/tests/test_basic_network.py | 8 ++++---- varlink/tests/test_orgexamplemore.py | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/varlink/client.py b/varlink/client.py index 2b60e7e..a2ba4e9 100644 --- a/varlink/client.py +++ b/varlink/client.py @@ -40,11 +40,11 @@ def __init__(self, interface, namespaced=False): if isinstance(member, _Method): self._add_method(member) - def close(self): + def close(self) -> None: """To be implemented.""" raise NotImplementedError - def _send_message(self, out): + def _send_message(self, out) -> None: """To be implemented. This should send a varlink message to the varlink service adding a trailing zero byte. @@ -205,7 +205,7 @@ def __enter__(self): def __exit__(self, _type, _value, _traceback): self.close() - def close(self): + def close(self) -> None: try: if hasattr(self._connection, "shutdown"): self._connection.shutdown(socket.SHUT_RDWR) @@ -214,7 +214,7 @@ def close(self): self._connection.close() - def _send_message(self, out): + def _send_message(self, out) -> None: if self._send_bytes: self._connection.send_bytes(out + b"\0") elif self._sendall: @@ -546,7 +546,7 @@ def _with_resolved_interface(self, interface, resolver_address=None): return self - def cleanup(self): + def cleanup(self) -> None: if hasattr(self, "_tmpdir") and self._tmpdir is not None: try: shutil.rmtree(self._tmpdir) diff --git a/varlink/mock.py b/varlink/mock.py index 1a452c7..6742352 100644 --- a/varlink/mock.py +++ b/varlink/mock.py @@ -248,7 +248,7 @@ def __init__( product="mock", version=1, url="http://localhost", - ): + ) -> None: if not name: module = service.__module__ try: @@ -291,22 +291,22 @@ def generate_interface(self): def get_interface_file_path(self): return f"/tmp/{self.name}" - def generate_interface_file(self): + def generate_interface_file(self) -> None: tfp = open(self.get_interface_file_path(), "w+") tfp.write("\n".join(self.interface_description)) tfp.close() - def delete_interface_files(self): + def delete_interface_files(self) -> None: os.remove(self.get_interface_file_path()) os.remove(self.mocked_service_file) - def service_start(self): + def service_start(self) -> None: self.service_pid = subprocess.Popen( [sys.executable, self.mocked_service_file], env={"PYTHONPATH": ":".join(sys.path)} ) time.sleep(2) - def service_stop(self): + def service_stop(self) -> None: self.service_pid.kill() self.service_pid.communicate() diff --git a/varlink/tests/test_basic_network.py b/varlink/tests/test_basic_network.py index fcabe3b..e6c374a 100755 --- a/varlink/tests/test_basic_network.py +++ b/varlink/tests/test_basic_network.py @@ -43,18 +43,18 @@ def do_run(self, address): server.shutdown() server.server_close() - def test_tcp(self): + def test_tcp(self) -> None: self.do_run("tcp:127.0.0.1:23450") - def test_anon_unix(self): + def test_anon_unix(self) -> None: if platform.startswith("linux"): self.do_run(f"unix:@org.varlink.service_anon_test{os.getpid()}{threading.current_thread().name}") - def test_unix(self): + def test_unix(self) -> None: if hasattr(socket, "AF_UNIX"): self.do_run(f"unix:org.varlink.service_anon_test_{os.getpid()}{threading.current_thread().name}") - def test_wrong_url(self): + def test_wrong_url(self) -> None: self.assertRaises( ConnectionError, self.do_run, f"uenix:org.varlink.service_wrong_url_test_{os.getpid()}" ) diff --git a/varlink/tests/test_orgexamplemore.py b/varlink/tests/test_orgexamplemore.py index 46ae92b..0cb7610 100755 --- a/varlink/tests/test_orgexamplemore.py +++ b/varlink/tests/test_orgexamplemore.py @@ -214,7 +214,7 @@ def epilog(): class TestService(unittest.TestCase): - def test_service(self): + def test_service(self) -> None: address = "tcp:127.0.0.1:23451" Example.sleep_duration = 0.1 From c305ab26f74bdc8b5057c5b15e7281d0a0ae390b Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sat, 4 Oct 2025 15:58:58 +0200 Subject: [PATCH 2/6] varlink: add trivial typing to mock.py --- varlink/mock.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/varlink/mock.py b/varlink/mock.py index 6742352..c86a9d6 100644 --- a/varlink/mock.py +++ b/varlink/mock.py @@ -16,7 +16,7 @@ def cast_type(typeof): return cast.get(typeof, typeof) -def get_ignored(): +def get_ignored() -> list[str]: ignore = dir(MockedService) return ignore @@ -258,7 +258,7 @@ def __init__( else: self.name = name self.identifier = str(uuid.uuid4()) - self.interface_description = None + self.interface_description: list[str] = [] self.service = service self.types = types self.address = address @@ -278,7 +278,7 @@ def __init__( } self.generate_interface() - def generate_interface(self): + def generate_interface(self) -> None: ignore = get_ignored() self.interface_description = [f"interface {self.name}"] if self.types: @@ -288,7 +288,7 @@ def generate_interface(self): for attr in attributs["callables"]: self.interface_description.append(generate_callable_interface(self.service, attr)) - def get_interface_file_path(self): + def get_interface_file_path(self) -> str: return f"/tmp/{self.name}" def generate_interface_file(self) -> None: From 677e7f62bdf8285906a519a4d8c4c7a4e47b3d03 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sat, 4 Oct 2025 15:59:28 +0200 Subject: [PATCH 3/6] varlink: type trivial functions in server.py --- varlink/server.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/varlink/server.py b/varlink/server.py index 8031cd5..f8a128c 100644 --- a/varlink/server.py +++ b/varlink/server.py @@ -12,7 +12,7 @@ from socketserver import ForkingMixIn from types import GeneratorType -from typing import Optional +from typing import Optional, Union class Service: @@ -91,7 +91,7 @@ def GetInfo(self): "interfaces": list(self.interfaces.keys()), } - def GetInterfaceDescription(self, interface): + def GetInterfaceDescription(self, interface: str) -> dict[str, str]: """The standardized org.varlink.service.GetInterfaceDescription() varlink method.""" try: i = self.interfaces[interface] @@ -282,7 +282,7 @@ def decorator(interface_class): return decorator -def get_listen_fd(): +def get_listen_fd() -> Union[int, None]: if "LISTEN_FDS" not in os.environ: return None if "LISTEN_PID" not in os.environ: @@ -319,6 +319,8 @@ def get_listen_fd(): except OSError: return None + return None + class RequestHandler(StreamRequestHandler): """Varlink request handler @@ -500,7 +502,7 @@ def server_close(self): pass self.socket.close() - def fileno(self): + def fileno(self) -> int: """Return socket file number. Interface required by selector. From 0a7ec601d388acfa704708a8c5861d0ef9545b83 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sat, 4 Oct 2025 16:00:07 +0200 Subject: [PATCH 4/6] varlink: change `sleep_duration` to a float In another test it is set to 0.1 which upsets mypy as it changes the type from a float to an int. --- varlink/tests/test_orgexamplemore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/varlink/tests/test_orgexamplemore.py b/varlink/tests/test_orgexamplemore.py index 0cb7610..561e231 100755 --- a/varlink/tests/test_orgexamplemore.py +++ b/varlink/tests/test_orgexamplemore.py @@ -88,7 +88,7 @@ def __init__(self, reason): @service.interface("org.example.more") class Example: - sleep_duration = 1 + sleep_duration = 1.0 def TestMore(self, n, _more=True, _server=None): try: From 68d8429059fbf28ae5e20fae9753751081699d32 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Tue, 7 Oct 2025 21:12:55 +0200 Subject: [PATCH 5/6] varlink: further type the client class --- varlink/client.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/varlink/client.py b/varlink/client.py index a2ba4e9..fd92b5c 100644 --- a/varlink/client.py +++ b/varlink/client.py @@ -9,6 +9,7 @@ import sys import tempfile import threading +from typing import Optional from .error import InterfaceNotFound, VarlinkEncoder, VarlinkError from .scanner import Interface, _Method @@ -443,7 +444,7 @@ def new_bridge_socket(): self._child_pid = p.pid return sp[0] - def new_bridge_socket_compat(): + def new_bridge_socket_compat() -> socket.socket: sp = socket.socketpair() p = subprocess.Popen( " ".join(argv), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True @@ -563,7 +564,9 @@ def cleanup(self) -> None: except Exception: # TODO: maybe just ChildProcessError? pass - def open(self, interface_name, namespaced=False, connection=None): + def open( + self, interface_name: str, namespaced: bool = False, connection: Optional[socket.socket] = None + ) -> SimpleClientInterfaceHandler: """Open a new connection and get a client interface handle with the varlink methods installed. :param interface_name: an interface name, which the service this client object is @@ -586,11 +589,12 @@ def open(self, interface_name, namespaced=False, connection=None): return self.handler(self._interfaces[interface_name], connection, namespaced=namespaced) - def open_connection(self): + def open_connection(self) -> socket.socket: """Open a new connection and return the socket. :exception OSError: anything socket.connect() throws """ + assert self._socket_fn, "socket_fn not initialised" return self._socket_fn() def get_interfaces(self, socket_connection=None): @@ -628,7 +632,7 @@ def get_interface(self, interface_name, socket_connection=None): return interface - def add_interface(self, interface): + def add_interface(self, interface: Interface) -> None: """Manually add or overwrite an interface definition from an Interface object. :param interface: an Interface() object From c051cf62ce9ababfe138842ea86c44362c5479a4 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Tue, 7 Oct 2025 21:15:23 +0200 Subject: [PATCH 6/6] varlink: tests: add missing connection parameter case --- varlink/tests/test_basic_network.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/varlink/tests/test_basic_network.py b/varlink/tests/test_basic_network.py index e6c374a..611c80e 100755 --- a/varlink/tests/test_basic_network.py +++ b/varlink/tests/test_basic_network.py @@ -58,3 +58,24 @@ def test_wrong_url(self) -> None: self.assertRaises( ConnectionError, self.do_run, f"uenix:org.varlink.service_wrong_url_test_{os.getpid()}" ) + + def test_reuse_open(self) -> None: + address = "tcp:127.0.0.1:23450" + server = varlink.ThreadingServer(address, ServiceRequestHandler) + server_thread = threading.Thread(target=server.serve_forever) + server_thread.daemon = True + server_thread.start() + + try: + with varlink.Client(address) as client: + connection = client.open_connection() + re_use = client.open("org.varlink.service", False, connection) + + info = re_use.GetInfo() + self.assertEqual(len(info["interfaces"]), 1) + self.assertEqual(info["interfaces"][0], "org.varlink.service") + self.assertEqual(info, service.GetInfo()) + connection.close() + finally: + server.shutdown() + server.server_close()