diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a6f53a4..8263ff9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,7 @@ jobs: fetch-depth: 0 - name: Install ruff run: | - python3 -m pip install --break-system-packages ruff + python3 -m pip install --break-system-packages ruff mypy - name: Run ruff format run: | ruff --version @@ -47,4 +47,8 @@ jobs: run: | ruff --version ruff check varlink + - name: Type Checking (mypy) + run: | + python3 -m mypy --version + python3 -m mypy varlink/ runs-on: ubuntu-latest diff --git a/varlink/client.py b/varlink/client.py index e103e43..b330002 100644 --- a/varlink/client.py +++ b/varlink/client.py @@ -217,7 +217,7 @@ def _send_message(self, out): self._connection.send_bytes(out + b"\0") elif self._sendall: self._connection.sendall(out + b"\0") - elif hasattr: + else: self._connection.write(out + b"\0") def _next_message(self): @@ -245,15 +245,8 @@ def _next_message(self): def pipe_bridge(reader, writer): - if hasattr(reader, "recv"): - recv = True - else: - recv = False - - if hasattr(writer, "sendall"): - sendall = True - else: - sendall = False + recv = hasattr(reader, "recv") + sendall = hasattr(writer, "sendall") while True: try: @@ -272,7 +265,7 @@ def pipe_bridge(reader, writer): try: if sendall: writer.sendall(data) - elif hasattr: + else: writer.write(data) writer.flush() except Exception: # TODO: maybe just OsError? diff --git a/varlink/scanner.py b/varlink/scanner.py index 71bc27c..b9c98e1 100644 --- a/varlink/scanner.py +++ b/varlink/scanner.py @@ -10,11 +10,13 @@ class Scanner: """Class for scanning a varlink interface definition.""" def __init__(self, string): - self.whitespace = re.compile(r"([ \t\n]|#.*$)+", re.ASCII | re.MULTILINE) + # whitespace or comment + wsc = r"([ \t\n]|#.*$)" + self.whitespace = re.compile(rf"{wsc}+", re.ASCII | re.MULTILINE) self.docstring = re.compile(r"(?:.?)+#(.*)(?:\n|\r\n)") # FIXME: nested () self.method_signature = re.compile( - r"([ \t\n]|#.*$)*(\([^)]*\))([ \t\n]|#.*$)*->([ \t\n]|#.*$)*(\([^)]*\))", + rf"{wsc}*(\(.*?\)){wsc}*->{wsc}*(\(.*?\))", re.ASCII | re.MULTILINE, ) @@ -34,8 +36,7 @@ def __init__(self, string): self.current_doc = "" def get(self, expected): - m = self.whitespace.match(self.string, self.pos) - if m: + if m := self.whitespace.match(self.string, self.pos): doc = self.docstring.findall(self.string[m.start() : m.end()]) if len(doc): try: @@ -44,10 +45,8 @@ def get(self, expected): self.current_doc += "\n".join([el.decode("utf-8") for el in doc]) self.pos = m.end() - pattern = self.patterns.get(expected) - if pattern: - m = pattern.match(self.string, self.pos) - if m: + if pattern := self.patterns.get(expected): + if m := pattern.match(self.string, self.pos): self.pos = m.end() return m.group(0) else: @@ -57,14 +56,12 @@ def get(self, expected): return True def expect(self, expected): - value = self.get(expected) - if not value: - raise SyntaxError(f"expected '{expected}'") - return value + if value := self.get(expected): + return value + raise SyntaxError(f"expected '{expected}'") def end(self): - m = self.whitespace.match(self.string, self.pos) - if m: + if m := self.whitespace.match(self.string, self.pos): doc = self.docstring.findall(self.string[m.start() : m.end()]) if len(doc): try: diff --git a/varlink/server.py b/varlink/server.py index 4dc54d8..43d9178 100644 --- a/varlink/server.py +++ b/varlink/server.py @@ -13,6 +13,7 @@ from socketserver import ForkingMixIn from types import GeneratorType +from typing import Optional class Service: @@ -324,7 +325,7 @@ class RequestHandler(StreamRequestHandler): Instantiate your own class and set the class variable service to your global :class:`Service` object. """ - service = None + service: Optional[Service] = None def handle(self): message = b"" diff --git a/varlink/tests/test_certification.py b/varlink/tests/test_certification.py index 8fc59fd..a4b84e1 100755 --- a/varlink/tests/test_certification.py +++ b/varlink/tests/test_certification.py @@ -88,7 +88,7 @@ def __init__(self, wants, got): @service.interface("org.varlink.certification") class CertService: - next_method = {} + next_method: dict[str, str] = {} def new_client_id(self, _server): client_id = codecs.getencoder("hex")(os.urandom(16))[0].decode("ascii") @@ -477,6 +477,8 @@ def usage(): with varlink.Client.new_with_activate([__file__, "--varlink=$VARLINK_ADDRESS"]) as client: run_client(client) elif client_mode: + if client is None: + raise ValueError("--client requires at either of --varlink, --bridge or --activate") with client: run_client(client) else: diff --git a/varlink/tests/test_mocks.py b/varlink/tests/test_mocks.py index 6bdac1d..3702240 100644 --- a/varlink/tests/test_mocks.py +++ b/varlink/tests/test_mocks.py @@ -12,21 +12,23 @@ class Service: - def Test1(self, param1: int) -> str: + # TODO: these type annotations are wrong as Python type hints, but are used + # by varlink.mock.generate_callable_interface + def Test1(self, param1: int) -> str: # type: ignore """return test: MyPersonalType""" return { "test": { "foo": "bim", "bar": "boom", }, - } + } # type: ignore def Test2(self, param1: str = "test") -> None: pass - def Test3(self, param1: str) -> str: + def Test3(self, param1: str) -> str: # type: ignore """return test""" - return {"test": param1} + return {"test": param1} # type: ignore class TestMockMechanisms(unittest.TestCase): diff --git a/varlink/tests/test_orgexamplemore.py b/varlink/tests/test_orgexamplemore.py index 51ed30a..46ae92b 100755 --- a/varlink/tests/test_orgexamplemore.py +++ b/varlink/tests/test_orgexamplemore.py @@ -200,6 +200,8 @@ def epilog(): with varlink.Client.new_with_activate([__file__, "--varlink=$VARLINK_ADDRESS"]) as client: run_client(client) elif client_mode: + if client is None: + raise ValueError("--client requires at either of --varlink, --bridge or --activate") with client: run_client(client) else: