diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8bd1e1e..a6f53a4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,3 +25,26 @@ jobs: # Use GitHub's Linux Docker host runs-on: ubuntu-latest + + lints: + name: Lints + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install ruff + run: | + python3 -m pip install --break-system-packages ruff + - name: Run ruff format + run: | + ruff --version + if ! ruff format --check --quiet varlink + then + echo "Please run 'ruff format' on the above files or apply the diffs below manually" + ruff format --check --quiet --diff varlink + fi + - name: Run ruff check + run: | + ruff --version + ruff check varlink + runs-on: ubuntu-latest diff --git a/docs/conf.py b/docs/conf.py index 30b498d..ad6691e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a @@ -19,14 +17,14 @@ # -- Project information ----------------------------------------------------- -project = u'varlink' -copyright = u'2018, Harald Hoyer, Lars Karlitski' -author = u'Harald Hoyer, Lars Karlitski' +project = "varlink" +copyright = "2018, Harald Hoyer, Lars Karlitski" +author = "Harald Hoyer, Lars Karlitski" # The short X.Y version -version = u'' +version = "" # The full version, including alpha/beta/rc tags -release = u'' +release = "" # -- General configuration --------------------------------------------------- @@ -39,21 +37,21 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.viewcode', - 'sphinx.ext.githubpages', + "sphinx.ext.autodoc", + "sphinx.ext.viewcode", + "sphinx.ext.githubpages", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # -source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"] # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -68,7 +66,7 @@ exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'default' +pygments_style = "default" # -- Options for HTML output ------------------------------------------------- @@ -76,7 +74,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'classic' +html_theme = "classic" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -87,7 +85,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -103,7 +101,7 @@ # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'varlinkdoc' +htmlhelp_basename = "varlinkdoc" # -- Options for LaTeX output ------------------------------------------------ @@ -112,15 +110,12 @@ # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -130,8 +125,13 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'varlink.tex', u'varlink Documentation', - u'Harald Hoyer\\textless{}harald@redhat.com\\textgreater{}, Lars Karlitski\\textless{}lars@karlitski.net\\textgreater{}', 'manual'), + ( + master_doc, + "varlink.tex", + "varlink Documentation", + "Harald Hoyer\\textless{}harald@redhat.com\\textgreater{}, Lars Karlitski\\textless{}lars@karlitski.net\\textgreater{}", + "manual", + ), ] @@ -139,10 +139,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'varlink', u'varlink Documentation', - [author], 1) -] +man_pages = [(master_doc, "varlink", "varlink Documentation", [author], 1)] # -- Options for Texinfo output ---------------------------------------------- @@ -151,9 +148,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'varlink', u'varlink Documentation', - author, 'varlink', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "varlink", + "varlink Documentation", + author, + "varlink", + "One line description of project.", + "Miscellaneous", + ), ] diff --git a/pyproject.toml b/pyproject.toml index 16afedd..35488ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,3 +9,8 @@ build-backend = 'setuptools.build_meta' [tool.setuptools_scm] local_scheme = "no-local-version" + +[tool.ruff] +target-version = "py39" +line-length = 109 +lint.select = ["E", "F", "I", "UP"] diff --git a/setup.py b/setup.py index 4abb68b..07b902d 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,15 @@ from setuptools import setup + def local_scheme(_): """Enables a version format so that upload to TestPyPI is successful. - For example: 2.6.2.dev8 - See https://github.com/pypa/setuptools_scm/issues/342. - """ + For example: 2.6.2.dev8 + See https://github.com/pypa/setuptools_scm/issues/342. + """ return "" + setup( use_scm_version={"local_scheme": "no-local-version"}, - setup_requires=['setuptools_scm'], + setup_requires=["setuptools_scm"], ) diff --git a/varlink/__init__.py b/varlink/__init__.py index 36c66fd..2d501f2 100644 --- a/varlink/__init__.py +++ b/varlink/__init__.py @@ -16,24 +16,57 @@ import os if hasattr(os, "fork"): - __all__ = ['Client', 'ClientInterfaceHandler', 'SimpleClientInterfaceHandler', - 'Service', 'RequestHandler', 'Server', 'ThreadingServer', 'ForkingServer', - 'InterfaceNotFound', 'MethodNotFound', 'MethodNotImplemented', 'InvalidParameter', - 'VarlinkEncoder', 'VarlinkError', - 'Interface', 'Scanner', 'get_listen_fd'] + __all__ = [ + "Client", + "ClientInterfaceHandler", + "SimpleClientInterfaceHandler", + "Service", + "RequestHandler", + "Server", + "ThreadingServer", + "ForkingServer", + "InterfaceNotFound", + "MethodNotFound", + "MethodNotImplemented", + "InvalidParameter", + "VarlinkEncoder", + "VarlinkError", + "Interface", + "Scanner", + "get_listen_fd", + ] from .server import ForkingServer else: - __all__ = ['Client', 'ClientInterfaceHandler', 'SimpleClientInterfaceHandler', - 'Service', 'RequestHandler', 'Server', 'ThreadingServer', - 'InterfaceNotFound', 'MethodNotFound', 'MethodNotImplemented', 'InvalidParameter', - 'VarlinkEncoder', 'VarlinkError', - 'Interface', 'Scanner', 'get_listen_fd'] - -from .client import (Client, ClientInterfaceHandler, SimpleClientInterfaceHandler) -from .error import (VarlinkEncoder, VarlinkError, InvalidParameter, InterfaceNotFound, MethodNotImplemented, - MethodNotFound) -from .scanner import (Scanner, Interface) -from .server import (Service, get_listen_fd, Server, ThreadingServer, RequestHandler) + __all__ = [ + "Client", + "ClientInterfaceHandler", + "SimpleClientInterfaceHandler", + "Service", + "RequestHandler", + "Server", + "ThreadingServer", + "InterfaceNotFound", + "MethodNotFound", + "MethodNotImplemented", + "InvalidParameter", + "VarlinkEncoder", + "VarlinkError", + "Interface", + "Scanner", + "get_listen_fd", + ] + +from .client import Client, ClientInterfaceHandler, SimpleClientInterfaceHandler +from .error import ( + InterfaceNotFound, + InvalidParameter, + MethodNotFound, + MethodNotImplemented, + VarlinkEncoder, + VarlinkError, +) +from .scanner import Interface, Scanner +from .server import RequestHandler, Server, Service, ThreadingServer, get_listen_fd # There are no tests here, so don't try to run anything discovered from diff --git a/varlink/cli.py b/varlink/cli.py index 58a66cb..c9a2ee7 100644 --- a/varlink/cli.py +++ b/varlink/cli.py @@ -21,13 +21,13 @@ def varlink_call(args): print("No method found", file=sys.stderr) sys.exit(1) - method = args.METHOD[deli + 1:] + method = args.METHOD[deli + 1 :] interface = args.METHOD[:deli] deli = interface.rfind("/") if deli != -1: address = interface[:deli] - interface = interface[deli + 1:] + interface = interface[deli + 1 :] else: address = None @@ -47,8 +47,12 @@ def new_client(address): got = False try: with client.open(interface) as con: - out = {'method': interface + '.' + method, 'more': args.more, 'parameters': json.loads(args.ARGUMENTS or "{}" )} - con._send_message(json.dumps(out, cls=varlink.VarlinkEncoder).encode('utf-8')) + out = { + "method": interface + "." + method, + "more": args.more, + "parameters": json.loads(args.ARGUMENTS or "{}"), + } + con._send_message(json.dumps(out, cls=varlink.VarlinkEncoder).encode("utf-8")) more = True while more: (message, more) = con._next_varlink_message() @@ -62,8 +66,9 @@ def new_client(address): print("Connection closed") sys.exit(1) + def varlink_bridge(args): - message = b'' + message = b"" last_interface = None con = None client = None @@ -80,13 +85,12 @@ def varlink_bridge(args): else: print("No --connect or --bridge or --activate") - - if hasattr(sys.stdout, 'buffer'): + if hasattr(sys.stdout, "buffer"): stdout = sys.stdout.buffer else: stdout = sys.stdout - if hasattr(sys.stdin, 'buffer'): + if hasattr(sys.stdin, "buffer"): stdin = sys.stdin.buffer else: stdin = sys.stdin @@ -94,35 +98,39 @@ def varlink_bridge(args): while not sys.stdin.closed: c = stdin.read(1) - if c == b'': + if c == b"": break - if c != b'\0': + if c != b"\0": message += c continue - req = json.loads(message.decode('utf-8')) + req = json.loads(message.decode("utf-8")) if not args.connect and not args.activate and not args.bridge: - if req['method'] == "org.varlink.service.GetInfo": - req['method'] = "org.varlink.resolver.GetInfo" + if req["method"] == "org.varlink.service.GetInfo": + req["method"] = "org.varlink.resolver.GetInfo" - interface_name, _, method_name = req.get('method', '').rpartition('.') + interface_name, _, method_name = req.get("method", "").rpartition(".") - if req['method'] == "org.varlink.service.GetInterfaceDescription": - resolving_interface = req['parameters']['interface'] + if req["method"] == "org.varlink.service.GetInterfaceDescription": + resolving_interface = req["parameters"]["interface"] else: resolving_interface = interface_name if not interface_name or not method_name: - stdout.write(json.dumps(varlink.InterfaceNotFound(interface_name), cls=varlink.VarlinkEncoder).encode( - 'utf-8') + b'\0') + stdout.write( + json.dumps(varlink.InterfaceNotFound(interface_name), cls=varlink.VarlinkEncoder).encode( + "utf-8" + ) + + b"\0" + ) sys.stdout.flush() continue if last_interface != resolving_interface: if con: - if hasattr(con, 'shutdown'): + if hasattr(con, "shutdown"): con.shutdown(socket.SHUT_RDWR) else: con.close() @@ -131,12 +139,11 @@ def varlink_bridge(args): client.cleanup() try: - client = varlink.Client.new_with_resolved_interface(resolving_interface, - resolver_address=args.resolver) + client = varlink.Client.new_with_resolved_interface( + resolving_interface, resolver_address=args.resolver + ) except varlink.VarlinkError as e: - stdout.write( - json.dumps(e, cls=varlink.VarlinkEncoder).encode( - 'utf-8') + b'\0') + stdout.write(json.dumps(e, cls=varlink.VarlinkEncoder).encode("utf-8") + b"\0") sys.stdout.flush() continue @@ -144,37 +151,37 @@ def varlink_bridge(args): last_interface = resolving_interface if hasattr(con, "send"): - con.send(message + b'\0') + con.send(message + b"\0") else: - con.write(message + b'\0') + con.write(message + b"\0") if req.get("oneway", False): continue - message = b'' + message = b"" - ret_message = b'' + ret_message = b"" while True: c = con.recv(1) - if c == b'': + if c == b"": break - if c != b'\0': + if c != b"\0": ret_message += c continue - stdout.write(ret_message + b'\0') + stdout.write(ret_message + b"\0") sys.stdout.flush() - ret = json.loads(ret_message.decode('utf-8')) - ret_message = b'' + ret = json.loads(ret_message.decode("utf-8")) + ret_message = b"" - if not ret.get('continues', False): + if not ret.get("continues", False): break - if ret.get('upgraded', False): + if ret.get("upgraded", False): raise NotImplementedError("Bridging upgraded connection not yet supported") @@ -182,7 +189,7 @@ def varlink_help(args): deli = args.INTERFACE.rfind("/") if deli != -1: address = args.INTERFACE[:deli] - interface_name = args.INTERFACE[deli + 1:] + interface_name = args.INTERFACE[deli + 1 :] client = varlink.Client.new_with_address(address) else: interface_name = args.INTERFACE @@ -197,6 +204,7 @@ def varlink_help(args): del client print(interface.description) + def varlink_info(args): if args.ADDRESS: client = varlink.Client.new_with_address(args.ADDRESS) @@ -207,7 +215,6 @@ def varlink_info(args): else: print("No ADDRESS or --bridge or --activate") - client.get_interfaces() info = client.info del client @@ -220,33 +227,45 @@ def varlink_info(args): print(" ", i) -if __name__ == '__main__': +if __name__ == "__main__": parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(title="commands") - parser.add_argument('-r', '--resolver', default=None, help='address of the resolver') - parser.add_argument('-A', '--activate', default=None, - help='Service to socket-activate and connect to. ' - 'The temporary UNIX socket address is exported as $VARLINK_ADDRESS.') - parser.add_argument('-b', '--bridge', default=None, help='Command to execute and connect to') - - parser_info = subparsers.add_parser('info', help='Print information about a service') - parser_info.add_argument('ADDRESS', nargs='?') + parser.add_argument("-r", "--resolver", default=None, help="address of the resolver") + parser.add_argument( + "-A", + "--activate", + default=None, + help="Service to socket-activate and connect to. " + "The temporary UNIX socket address is exported as $VARLINK_ADDRESS.", + ) + parser.add_argument("-b", "--bridge", default=None, help="Command to execute and connect to") + + parser_info = subparsers.add_parser("info", help="Print information about a service") + parser_info.add_argument("ADDRESS", nargs="?") parser_info.set_defaults(func=varlink_info) - parser_help = subparsers.add_parser('help', help='Print interface description or service information') - parser_help.add_argument('INTERFACE') + parser_help = subparsers.add_parser("help", help="Print interface description or service information") + parser_help.add_argument("INTERFACE") parser_help.set_defaults(func=varlink_help) - parser_bridge = subparsers.add_parser('bridge', help='Bridge varlink messages to services on this machine') - parser_bridge.add_argument('-c', '--connect', default=None, help='Optional varlink address to connect to, ' - 'without using the resolver.') + parser_bridge = subparsers.add_parser( + "bridge", help="Bridge varlink messages to services on this machine" + ) + parser_bridge.add_argument( + "-c", + "--connect", + default=None, + help="Optional varlink address to connect to, without using the resolver.", + ) parser_bridge.set_defaults(func=varlink_bridge) - parser_call = subparsers.add_parser('call', help='Call a method') - parser_call.add_argument('-m', '--more', action='store_true', help='wait for multiple method returns if supported') - parser_call.add_argument('METHOD') - parser_call.add_argument('ARGUMENTS', nargs='?', default="") + parser_call = subparsers.add_parser("call", help="Call a method") + parser_call.add_argument( + "-m", "--more", action="store_true", help="wait for multiple method returns if supported" + ) + parser_call.add_argument("METHOD") + parser_call.add_argument("ARGUMENTS", nargs="?", default="") parser_call.set_defaults(func=varlink_call) args = parser.parse_args() diff --git a/varlink/client.py b/varlink/client.py index ee2acd6..e103e43 100644 --- a/varlink/client.py +++ b/varlink/client.py @@ -1,17 +1,15 @@ -# coding=utf-8 - import json import os import shutil import signal import socket +import subprocess import sys import tempfile -import subprocess import threading -from .error import (VarlinkError, InterfaceNotFound, VarlinkEncoder) -from .scanner import (Interface, _Method) +from .error import InterfaceNotFound, VarlinkEncoder, VarlinkError +from .scanner import Interface, _Method class ClientInterfaceHandler: @@ -59,7 +57,6 @@ def _next_message(self): raise NotImplementedError def _add_method(self, method): - def _wrapped(*args, **kwargs): if "_more" in kwargs and kwargs.pop("_more"): return self._call_more(method.name, *args, **kwargs) @@ -82,15 +79,15 @@ def _next_varlink_message(self): message = next(self._next_message()) message = json.loads(message) - if 'parameters' not in message: - message['parameters'] = {} + if "parameters" not in message: + message["parameters"] = {} - if 'error' in message and message["error"] is not None: + if "error" in message and message["error"] is not None: self._in_use = False e = VarlinkError.new(message, self._namespaced) raise e else: - return message['parameters'], ('continues' in message) and message['continues'] + return message["parameters"], ("continues" in message) and message["continues"] def _call(self, method_name, *args, **kwargs): if self._in_use: @@ -104,15 +101,15 @@ def _call(self, method_name, *args, **kwargs): parameters = self._interface.filter_params("client.call", method.in_type, False, args, kwargs) - out = {'method': self._interface.name + "." + method_name} + out = {"method": self._interface.name + "." + method_name} if oneway: - out['oneway'] = True + out["oneway"] = True if parameters: - out['parameters'] = parameters + out["parameters"] = parameters - self._send_message(json.dumps(out, cls=VarlinkEncoder).encode('utf-8')) + self._send_message(json.dumps(out, cls=VarlinkEncoder).encode("utf-8")) if oneway: return None @@ -126,7 +123,9 @@ def _call(self, method_name, *args, **kwargs): self._in_use = False if message: - message = self._interface.filter_params("client.reply", method.out_type, self._namespaced, message, None) + message = self._interface.filter_params( + "client.reply", method.out_type, self._namespaced, message, None + ) return message @@ -137,17 +136,18 @@ def _call_more(self, method_name, *args, **kwargs): method = self._interface.get_method(method_name) parameters = self._interface.filter_params("client.call", method.in_type, False, args, kwargs) - out = {'method': self._interface.name + "." + method_name, 'more': True, 'parameters': parameters} + out = {"method": self._interface.name + "." + method_name, "more": True, "parameters": parameters} - self._send_message(json.dumps(out, cls=VarlinkEncoder).encode('utf-8')) + self._send_message(json.dumps(out, cls=VarlinkEncoder).encode("utf-8")) more = True self._in_use = True while more: (message, more) = self._next_varlink_message() if message: - message = self._interface.filter_params("client.reply", method.out_type, self._namespaced, message, - None) + message = self._interface.filter_params( + "client.reply", method.out_type, self._namespaced, message, None + ) yield message self._in_use = False @@ -171,31 +171,31 @@ def __init__(self, interface, file_or_socket, namespaced=False): ClientInterfaceHandler.__init__(self, interface, namespaced=namespaced) self._connection = file_or_socket - if hasattr(self._connection, 'send_bytes'): + if hasattr(self._connection, "send_bytes"): self._send_bytes = True self._sendall = False - elif hasattr(self._connection, 'sendall'): + elif hasattr(self._connection, "sendall"): self._send_bytes = False self._sendall = True else: - if not hasattr(self._connection, 'write'): + if not hasattr(self._connection, "write"): raise TypeError self._sendall = False self._send_bytes = False - if hasattr(self._connection, 'recv_bytes'): + if hasattr(self._connection, "recv_bytes"): self._recv_bytes = True self._recv = False - elif hasattr(self._connection, 'recv'): + elif hasattr(self._connection, "recv"): self._recv = True self._recv_bytes = False else: - if not hasattr(self._connection, 'read'): + if not hasattr(self._connection, "read"): raise TypeError self._recv = False self._recv_bytes = False - self._in_buffer = b'' + self._in_buffer = b"" def __enter__(self): return self @@ -205,31 +205,31 @@ def __exit__(self, _type, _value, _traceback): def close(self): try: - if hasattr(self._connection, 'shutdown'): + if hasattr(self._connection, "shutdown"): self._connection.shutdown(socket.SHUT_RDWR) - except: + except Exception: # TODO: maybe just OSError? pass self._connection.close() def _send_message(self, out): if self._send_bytes: - self._connection.send_bytes(out + b'\0') + self._connection.send_bytes(out + b"\0") elif self._sendall: - self._connection.sendall(out + b'\0') + self._connection.sendall(out + b"\0") elif hasattr: - self._connection.write(out + b'\0') + self._connection.write(out + b"\0") def _next_message(self): while True: - message, sep, self._in_buffer = self._in_buffer.partition(b'\0') + message, sep, self._in_buffer = self._in_buffer.partition(b"\0") if not sep: # No zero byte found self._in_buffer = message message = None if message: - yield message.decode('utf-8') + yield message.decode("utf-8") continue if self._recv_bytes: @@ -261,7 +261,7 @@ def pipe_bridge(reader, writer): data = reader.recv(8192) else: data = reader.read(1) - except: + except Exception: # TODO: maybe just OsError? data = [] if len(data) == 0: @@ -275,7 +275,7 @@ def pipe_bridge(reader, writer): elif hasattr: writer.write(data) writer.flush() - except Exception as e: + except Exception: # TODO: maybe just OsError? reader.close() writer.close() return @@ -317,7 +317,7 @@ class Client: >>> for m in connection.Monitor(_more=True): >>> for e in m.entries: - >>> print("%s: %s" % (e.time, e.message)) + >>> print(f"{e.time}: {e.message}") 2018-01-29 12:19:59Z: [system] Activating via systemd: service name='[…] 2018-01-29 12:19:59Z: Starting Network Manager Script Dispatcher Service... 2018-01-29 12:19:59Z: bound to 10.200.159.150 -- renewal in 1423 seconds. @@ -330,7 +330,8 @@ class Client: not return a normal namespace wrapped varlink return value, but a generator, which yields the return values and waits (blocks) for the service to return more return values in the generator's .__next__() call. - """ + """ # noqa: E501 + handler = SimpleClientInterfaceHandler def __init__(self, address=None, resolve_interface=None, resolver=None): @@ -350,7 +351,7 @@ def __init__(self, address=None, resolve_interface=None, resolver=None): self._child_pid = 0 self._str = "Client" - with open(os.path.join(os.path.dirname(__file__), 'org.varlink.service.varlink')) as f: + with open(os.path.join(os.path.dirname(__file__), "org.varlink.service.varlink")) as f: interface = Interface(f.read()) self.add_interface(interface) @@ -382,8 +383,9 @@ def new_with_activate(cls, argv): def _with_activate(self, argv): s = socket.socket(socket.AF_UNIX) s.setblocking(False) + # TODO: this is not cleaned up self._tmpdir = tempfile.mkdtemp() - address = self._tmpdir + "/" + str(os.getpid()) + address = f"{self._tmpdir}/{os.getpid()}" s.bind(address) s.listen(100) @@ -402,16 +404,17 @@ def _with_activate(self, argv): pass os.dup2(n, 3) - address = address.replace('\0', '@', 1) + address = address.replace("\0", "@", 1) for i in range(1, len(argv)): - argv[i] = argv[i].replace("$VARLINK_ADDRESS", "unix:" + address) + argv[i] = argv[i].replace("$VARLINK_ADDRESS", f"unix:{address}") - os.environ["VARLINK_ADDRESS"] = "unix:" + address + os.environ["VARLINK_ADDRESS"] = f"unix:{address}" os.environ["LISTEN_FDS"] = "1" os.environ["LISTEN_FDNAMES"] = "varlink" os.environ["LISTEN_PID"] = str(os.getpid()) os.execvp(argv[0], argv) + # TODO: this is unreachable sys.exit(1) # parent s.close() @@ -442,8 +445,9 @@ def new_bridge_socket(): def new_bridge_socket_compat(): sp = socket.socketpair() - p = subprocess.Popen(" ".join(argv), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - close_fds=True) + p = subprocess.Popen( + " ".join(argv), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True + ) self._child_pid = p.pid thread1 = threading.Thread(daemon=True, target=pipe_bridge, args=(sp[1], p.stdin)) @@ -452,8 +456,8 @@ def new_bridge_socket_compat(): thread2.start() return sp[0] - self._str = "Bridge with: '%s'" % " ".join(argv) - if sys.platform == 'win32': + self._str = f"Bridge with: '{' '.join(argv)}'" + if sys.platform == "win32": self._socket_fn = new_bridge_socket_compat else: self._socket_fn = new_bridge_socket @@ -475,11 +479,11 @@ def _with_address(self, address): if address.startswith("unix:"): self._str = address address = address[5:] - mode = address.find(';') + mode = address.find(";") if mode != -1: address = address[:mode] - if address[0] == '@': - address = address.replace('@', '\0', 1) + if address[0] == "@": + address = address.replace("@", "\0", 1) def open_unix(): s = socket.socket(socket.AF_UNIX) @@ -492,14 +496,14 @@ def open_unix(): elif address.startswith("tcp:"): self._str = address address = address[4:] - p = address.rfind(':') + p = address.rfind(":") if p != -1: - port = address[p + 1:] + port = address[p + 1 :] address = address[:p] else: - raise ConnectionError("Invalid address 'tcp:%s'" % address) - address = address.replace('[', '') - address = address.replace(']', '') + raise ConnectionError(f"Invalid address 'tcp:{address}'") + address = address.replace("[", "") + address = address.replace("]", "") def open_tcp(): s = socket.create_connection((address, int(port))) @@ -509,7 +513,7 @@ def open_tcp(): self._socket_fn = open_tcp elif address is not None: - raise ConnectionError("Invalid address '%s'" % address) + raise ConnectionError(f"Invalid address '{address}'") return self @@ -529,14 +533,16 @@ def _with_resolved_interface(self, interface, resolver_address=None): if not resolver_address: resolver_address = "unix:/run/org.varlink.resolver" - if interface == 'org.varlink.resolver': + if interface == "org.varlink.resolver": self._with_address(resolver_address) else: - with Client.new_with_address(resolver_address) as client, \ - client.open('org.varlink.resolver') as _rc: + with ( + Client.new_with_address(resolver_address) as client, + client.open("org.varlink.resolver") as _rc, + ): # noinspection PyUnresolvedReferences _r = _rc.Resolve(interface) - self._with_address(_r['address']) + self._with_address(_r["address"]) return self @@ -554,7 +560,7 @@ def cleanup(self): pass try: os.waitpid(self._child_pid, 0) - except: + except Exception: # TODO: maybe just ChildProcessError? pass def open(self, interface_name, namespaced=False, connection=None): @@ -602,7 +608,7 @@ def get_interfaces(self, socket_connection=None): if close_socket: socket_connection.close() - return self.info['interfaces'] + return self.info["interfaces"] def get_interface(self, interface_name, socket_connection=None): if not socket_connection: @@ -614,7 +620,7 @@ def get_interface(self, interface_name, socket_connection=None): _service = self.handler(self._interfaces["org.varlink.service"], socket_connection) # noinspection PyUnresolvedReferences desc = _service.GetInterfaceDescription(interface_name) - interface = Interface(desc['description']) + interface = Interface(desc["description"]) self._interfaces[interface.name] = interface if close_socket: diff --git a/varlink/error.py b/varlink/error.py index 2a8de47..d72e872 100644 --- a/varlink/error.py +++ b/varlink/error.py @@ -1,6 +1,4 @@ import json - - from types import SimpleNamespace @@ -22,16 +20,16 @@ class VarlinkError(Exception): @classmethod def new(cls, message, namespaced=False): - if message['error'] == 'org.varlink.service.InterfaceNotFound': + if message["error"] == "org.varlink.service.InterfaceNotFound": return InterfaceNotFound.new(message, namespaced) - elif message['error'] == 'org.varlink.service.InvalidParameter': + elif message["error"] == "org.varlink.service.InvalidParameter": return InvalidParameter.new(message, namespaced) - elif message['error'] == 'org.varlink.service.MethodNotImplemented': + elif message["error"] == "org.varlink.service.MethodNotImplemented": return MethodNotImplemented.new(message, namespaced) - elif message['error'] == 'org.varlink.service.MethodNotImplemented': + elif message["error"] == "org.varlink.service.MethodNotImplemented": return MethodNotImplemented.new(message, namespaced) else: @@ -45,14 +43,17 @@ def __init__(self, message, namespaced=False): def error(self): """returns the exception varlink error name""" - return self.args[0].get('error') + return self.args[0].get("error") def parameters(self, namespaced=False): """returns the exception varlink error parameters""" if namespaced: - return json.loads(json.dumps(self.args[0]['parameters']), object_hook=lambda d: SimpleNamespace(**d)) + return json.loads( + json.dumps(self.args[0]["parameters"]), + object_hook=lambda d: SimpleNamespace(**d), + ) else: - return self.args[0].get('parameters') + return self.args[0].get("parameters") def as_dict(self): return self.args[0] @@ -63,11 +64,18 @@ class InterfaceNotFound(VarlinkError): @classmethod def new(cls, message, namespaced=False): - return cls(namespaced and message['parameters'].interface or message['parameters'].get('interface', None)) + return cls( + namespaced and message["parameters"].interface or message["parameters"].get("interface", None) + ) def __init__(self, interface): - VarlinkError.__init__(self, {'error': 'org.varlink.service.InterfaceNotFound', - 'parameters': {'interface': interface}}) + VarlinkError.__init__( + self, + { + "error": "org.varlink.service.InterfaceNotFound", + "parameters": {"interface": interface}, + }, + ) class MethodNotFound(VarlinkError): @@ -75,10 +83,16 @@ class MethodNotFound(VarlinkError): @classmethod def new(cls, message, namespaced=False): - return cls(namespaced and message['parameters'].method or message['parameters'].get('method', None)) + return cls(namespaced and message["parameters"].method or message["parameters"].get("method", None)) def __init__(self, method): - VarlinkError.__init__(self, {'error': 'org.varlink.service.MethodNotFound', 'parameters': {'method': method}}) + VarlinkError.__init__( + self, + { + "error": "org.varlink.service.MethodNotFound", + "parameters": {"method": method}, + }, + ) class MethodNotImplemented(VarlinkError): @@ -86,11 +100,16 @@ class MethodNotImplemented(VarlinkError): @classmethod def new(cls, message, namespaced=False): - return cls(namespaced and message['parameters'].method or message['parameters'].get('method', None)) + return cls(namespaced and message["parameters"].method or message["parameters"].get("method", None)) def __init__(self, method): - VarlinkError.__init__(self, - {'error': 'org.varlink.service.MethodNotImplemented', 'parameters': {'method': method}}) + VarlinkError.__init__( + self, + { + "error": "org.varlink.service.MethodNotImplemented", + "parameters": {"method": method}, + }, + ) class InvalidParameter(VarlinkError): @@ -98,8 +117,15 @@ class InvalidParameter(VarlinkError): @classmethod def new(cls, message, namespaced=False): - return cls(namespaced and message['parameters'].parameter or message['parameters'].get('parameter', None)) + return cls( + namespaced and message["parameters"].parameter or message["parameters"].get("parameter", None) + ) def __init__(self, name): - VarlinkError.__init__(self, - {'error': 'org.varlink.service.InvalidParameter', 'parameters': {'parameter': name}}) + VarlinkError.__init__( + self, + { + "error": "org.varlink.service.InvalidParameter", + "parameters": {"parameter": name}, + }, + ) diff --git a/varlink/mock.py b/varlink/mock.py index ab25d4c..1a452c7 100644 --- a/varlink/mock.py +++ b/varlink/mock.py @@ -11,7 +11,7 @@ def cast_type(typeof): - cast = {'str': 'string'} + cast = {"str": "string"} typeof = str(typeof).replace("", "") return cast.get(typeof, typeof) @@ -43,30 +43,24 @@ def generate_callable_interface(interface, attr): if param.name == "self": continue typeof = param.annotation - sign.append("{}: {}".format(param.name, cast_type(typeof))) + sign.append(f"{param.name}: {cast_type(typeof)}") returned = signature.return_annotation if returned: returned = cast_type(returned) doc = attribut.__doc__ if not doc: - raise ValueError( - "docstring format must be:" - "return name: type") + raise ValueError("docstring format must be:return name: type") doc = doc.replace("return ", "") if ":" in doc: returned = doc else: - returned = "{}: {}".format(doc, returned) + returned = f"{doc}: {returned}" else: returned = "" - return "method {name}({signature}) -> ({returned})".format( - name=attr, - signature=",".join(sign), - returned=returned - ) + return f"method {attr}({','.join(sign)}) -> ({returned})" -class MockedServiceProcess(): +class MockedServiceProcess: address = None vendor = None product = None @@ -83,30 +77,32 @@ def run(self): vendor=self.vendor, product=self.product, version=self.version, - url=self.url) + url=self.url, + ) instanciated_service = self.service_to_mock() - mocked_service._set_interface( - self.interface_file, - instanciated_service) + mocked_service._set_interface(self.interface_file, instanciated_service) class ServiceRequestHandler(varlink.RequestHandler): service = mocked_service - self.varlink_server = varlink.ThreadingServer( - self.address, ServiceRequestHandler) + self.varlink_server = varlink.ThreadingServer(self.address, ServiceRequestHandler) self.varlink_server.serve_forever() def service_generator(service, info, filename="mockedservice.py"): with open(filename, "w+") as pyfp: - pyfp.write(textwrap.dedent("""\ + pyfp.write( + textwrap.dedent( + f"""\ ''' Generated by varlink mocking system - {datetime} + {datetime.datetime.now()} Only for testing purpose and unit testing ''' - """.format(datetime=datetime.datetime.now()))) + """ + ) + ) pyfp.write("import varlink\n\n") pyfp.write(inspect.getsource(service)) pyfp.write("\n\n") @@ -118,15 +114,20 @@ def service_generator(service, info, filename="mockedservice.py"): surround = "'" if value["type"] == "raw": surround = "" - pyfp.write(" msp.{key} = {surround}{value}{surround}\n".format( - key=key, value=value["value"], surround=surround)) + pyfp.write(f" msp.{key} = {surround}{value['value']}{surround}\n") pyfp.write(" msp.run()\n") -def mockedservice(fake_service=None, fake_types=None, - address='unix:@test', name=None, - vendor='varlink', product='mock', version=1, - url='http://localhost'): +def mockedservice( + fake_service=None, + fake_types=None, + address="unix:@test", + name=None, + vendor="varlink", + product="mock", + version=1, + url="http://localhost", +): """ Varlink mocking service @@ -220,29 +221,38 @@ def mockedservice(fake_service=None, fake_types=None, If you try to use it with python 2.x it will raise an ``ImportError``. """ + def decorator(func): def wrapper(*args, **kwargs): - with MockedService(fake_service, fake_types, name=name, - address=address): + with MockedService(fake_service, fake_types, name=name, address=address): try: func(*args, **kwargs) except BrokenPipeError: # manage fake service stopping pass return + return wrapper - return decorator + return decorator -class MockedService(): - def __init__(self, service, types, address='unix:@test', name=None, - vendor='varlink', product='mock', version=1, - url='http://localhost'): +class MockedService: + def __init__( + self, + service, + types, + address="unix:@test", + name=None, + vendor="varlink", + product="mock", + version=1, + url="http://localhost", + ): if not name: module = service.__module__ try: - self.name = os.path.splitext(module)[1].replace('.', '') + self.name = os.path.splitext(module)[1].replace(".", "") except IndexError: self.name = module else: @@ -257,32 +267,29 @@ def __init__(self, service, types, address='unix:@test', name=None, self.version = version self.url = url self.service_info = { - "address": {'type': 'inherited', 'value': address}, - "vendor": {'type': 'inherited', 'value': vendor}, - "product": {'type': 'inherited', 'value': product}, - "version": {'type': 'raw', 'value': version}, - "url": {'type': 'inherited', 'value': url}, - "interface_name": {'type': 'inherited', 'value': self.name}, - "interface_file": { - 'type': 'inherited', - 'value': self.get_interface_file_path()}, - "service_to_mock": {'type': 'raw', 'value': service.__name__}, + "address": {"type": "inherited", "value": address}, + "vendor": {"type": "inherited", "value": vendor}, + "product": {"type": "inherited", "value": product}, + "version": {"type": "raw", "value": version}, + "url": {"type": "inherited", "value": url}, + "interface_name": {"type": "inherited", "value": self.name}, + "interface_file": {"type": "inherited", "value": self.get_interface_file_path()}, + "service_to_mock": {"type": "raw", "value": service.__name__}, } self.generate_interface() def generate_interface(self): ignore = get_ignored() - self.interface_description = ["interface {}".format(self.name)] + self.interface_description = [f"interface {self.name}"] if self.types: for line in self.types.split("\n"): self.interface_description.append(line) attributs = get_interface_attributs(self.service, ignore) for attr in attributs["callables"]: - self.interface_description.append(generate_callable_interface( - self.service, attr)) + self.interface_description.append(generate_callable_interface(self.service, attr)) def get_interface_file_path(self): - return "/tmp/{}".format(self.name) + return f"/tmp/{self.name}" def generate_interface_file(self): tfp = open(self.get_interface_file_path(), "w+") @@ -295,8 +302,7 @@ def delete_interface_files(self): def service_start(self): self.service_pid = subprocess.Popen( - [sys.executable, self.mocked_service_file], - env = { "PYTHONPATH": ':'.join(sys.path) } + [sys.executable, self.mocked_service_file], env={"PYTHONPATH": ":".join(sys.path)} ) time.sleep(2) @@ -305,10 +311,8 @@ def service_stop(self): self.service_pid.communicate() def __enter__(self): - self.mocked_service_file = "/tmp/{}".format(self.identifier) - service_generator( - self.service, self.service_info, - filename=self.mocked_service_file) + self.mocked_service_file = f"/tmp/{self.identifier}" + service_generator(self.service, self.service_info, filename=self.mocked_service_file) self.generate_interface_file() self.service_start() return self diff --git a/varlink/scanner.py b/varlink/scanner.py index f4f9fcf..71bc27c 100644 --- a/varlink/scanner.py +++ b/varlink/scanner.py @@ -1,30 +1,32 @@ -#!-*-coding:utf8-*- - import re - -from types import SimpleNamespace -from collections.abc import (Set, Mapping) - from collections import OrderedDict +from collections.abc import Mapping, Set +from types import SimpleNamespace -from .error import (MethodNotFound, InvalidParameter) +from .error import InvalidParameter, MethodNotFound class Scanner: """Class for scanning a varlink interface definition.""" def __init__(self, string): - self.whitespace = re.compile(r'([ \t\n]|#.*$)+', re.ASCII | re.MULTILINE) - self.docstring = re.compile(r'(?:.?)+#(.*)(?:\n|\r\n)') + self.whitespace = re.compile(r"([ \t\n]|#.*$)+", 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]|#.*$)*(\([^)]*\))', - re.ASCII | re.MULTILINE) - - self.keyword_pattern = re.compile(r'\b[a-z]+\b|[:,(){}]|->|\[\]|\?|\[string\]\(\)|\[string\]', re.ASCII) + self.method_signature = re.compile( + r"([ \t\n]|#.*$)*(\([^)]*\))([ \t\n]|#.*$)*->([ \t\n]|#.*$)*(\([^)]*\))", + re.ASCII | re.MULTILINE, + ) + + self.keyword_pattern = re.compile( + r"\b[a-z]+\b|[:,(){}]|->|\[\]|\?|\[string\]\(\)|\[string\]", re.ASCII + ) self.patterns = { - 'interface-name': re.compile(r'[A-Za-z]([A-Za-z])*([.][A-Za-z0-9]([-]*[A-Za-z0-9])*)+|xn--([0-9a-z])*([.][A-Za-z0-9]([-]*[A-Za-z0-9])*)+'), - 'member-name': re.compile(r'\b[A-Z][A-Za-z0-9]*\b', re.ASCII), - 'identifier': re.compile(r'\b[A-Za-z]([_]?[A-Za-z0-9])*\b', re.ASCII), + "interface-name": re.compile( + r"[A-Za-z]([A-Za-z])*([.][A-Za-z0-9]([-]*[A-Za-z0-9])*)+|xn--([0-9a-z])*([.][A-Za-z0-9]([-]*[A-Za-z0-9])*)+" + ), + "member-name": re.compile(r"\b[A-Z][A-Za-z0-9]*\b", re.ASCII), + "identifier": re.compile(r"\b[A-Za-z]([_]?[A-Za-z0-9])*\b", re.ASCII), } self.string = string @@ -34,13 +36,12 @@ def __init__(self, string): def get(self, expected): m = self.whitespace.match(self.string, self.pos) if m: - doc = self.docstring.findall(self.string[m.start():m.end()]) + doc = self.docstring.findall(self.string[m.start() : m.end()]) if len(doc): try: self.current_doc += "\n".join(doc) except UnicodeError: - self.current_doc += "\n".join( - [el.decode("utf-8") for el in doc]) + self.current_doc += "\n".join([el.decode("utf-8") for el in doc]) self.pos = m.end() pattern = self.patterns.get(expected) @@ -58,51 +59,50 @@ def get(self, expected): def expect(self, expected): value = self.get(expected) if not value: - raise SyntaxError("expected '{}'".format(expected)) + raise SyntaxError(f"expected '{expected}'") return value def end(self): m = self.whitespace.match(self.string, self.pos) if m: - doc = self.docstring.findall(self.string[m.start():m.end()]) + doc = self.docstring.findall(self.string[m.start() : m.end()]) if len(doc): try: self.current_doc += "\n".join(doc) except UnicodeError: - self.current_doc += "\n".join( - [el.decode("utf-8") for el in doc]) + self.current_doc += "\n".join([el.decode("utf-8") for el in doc]) self.pos = m.end() return self.pos >= len(self.string) def read_type(self, lastmaybe=False): - if self.get('?'): + if self.get("?"): if lastmaybe: raise SyntaxError("double '??'") return _Maybe(self.read_type(lastmaybe=True)) - if self.get('[string]()'): + if self.get("[string]()"): return set() - if self.get('[string]'): + if self.get("[string]"): return _Dict(self.read_type()) - if self.get('[]'): + if self.get("[]"): return _Array(self.read_type()) - if self.get('object'): + if self.get("object"): return _Object() - if self.get('bool'): - t = bool() - elif self.get('int'): - t = int() - elif self.get('float'): - t = float() - elif self.get('string'): - t = str() + if self.get("bool"): + t = False + elif self.get("int"): + t = 0 + elif self.get("float"): + t = 0.0 + elif self.get("string"): + t = "" else: - name = self.get('member-name') + name = self.get("member-name") if name: t = _CustomType(name) else: @@ -112,45 +112,45 @@ def read_type(self, lastmaybe=False): def read_struct(self): _isenum = None - self.expect('(') + self.expect("(") fields = OrderedDict() - if not self.get(')'): + if not self.get(")"): while True: - name = self.expect('identifier') + name = self.expect("identifier") if _isenum is None: - if self.get(':'): + if self.get(":"): _isenum = False fields[name] = self.read_type() - if not self.get(','): + if not self.get(","): break continue - elif self.get(','): + elif self.get(","): _isenum = True fields[name] = True continue else: - raise SyntaxError("after '{}'".format(name)) + raise SyntaxError(f"after '{name}'") elif not _isenum: try: - self.expect(':') + self.expect(":") fields[name] = self.read_type() except SyntaxError as e: - raise SyntaxError("after '{}': {}".format(name, e)) + raise SyntaxError(f"after '{name}': {e}") else: fields[name] = True - if not self.get(','): + if not self.get(","): break - self.expect(')') + self.expect(")") if _isenum: return _Enum(fields.keys()) else: return _Struct(fields) def read_member(self): - if self.get('type'): + if self.get("type"): try: - _name = self.expect('member-name') + _name = self.expect("member-name") except SyntaxError: m = self.whitespace.match(self.string, self.pos) if m: @@ -163,32 +163,32 @@ def read_member(self): else: stop = start - raise SyntaxError("'{}' not a valid type name.".format(self.string[start:stop])) + raise SyntaxError(f"'{self.string[start:stop]}' not a valid type name.") try: _type = self.read_type() except SyntaxError as e: - raise SyntaxError("in '{}': {}".format(_name, e)) + raise SyntaxError(f"in '{_name}': {e}") doc = self.current_doc self.current_doc = "" return _Alias(_name, _type, doc) - elif self.get('method'): - name = self.expect('member-name') + elif self.get("method"): + name = self.expect("member-name") # FIXME sig = self.method_signature.match(self.string, self.pos) if sig: sig = name + sig.group(0) in_type = self.read_struct() - self.expect('->') + self.expect("->") out_type = self.read_struct() doc = self.current_doc self.current_doc = "" return _Method(name, in_type, out_type, sig, doc) - elif self.get('error'): + elif self.get("error"): doc = self.current_doc self.current_doc = "" - return _Error(self.expect('member-name'), self.read_type(), doc) + return _Error(self.expect("member-name"), self.read_type(), doc) else: - raise SyntaxError('expected type, method, or error') + raise SyntaxError("expected type, method, or error") class _Object: @@ -196,43 +196,36 @@ class _Object: class _Struct: - def __init__(self, fields): self.fields = OrderedDict(fields) class _Enum: - def __init__(self, fields): self.fields = fields class _Array: - def __init__(self, element_type): self.element_type = element_type class _Maybe: - def __init__(self, element_type): self.element_type = element_type class _Dict: - def __init__(self, element_type): self.element_type = element_type class _CustomType: - def __init__(self, name): self.name = name class _Alias: - def __init__(self, name, varlink_type, doc=None): self.name = name self.type = varlink_type @@ -240,7 +233,6 @@ def __init__(self, name, varlink_type, doc=None): class _Method: - def __init__(self, name, in_type, out_type, _signature, doc=None): self.name = name self.in_type = in_type @@ -250,7 +242,6 @@ def __init__(self, name, in_type, out_type, _signature, doc=None): class _Error: - def __init__(self, name, varlink_type, doc=None): self.name = name self.type = varlink_type @@ -265,8 +256,8 @@ def __init__(self, description): self.description = description scanner = Scanner(description) - scanner.expect('interface') - self.name = scanner.expect('interface-name') + scanner.expect("interface") + self.name = scanner.expect("interface-name") self.doc = scanner.current_doc scanner.current_doc = "" self.members = OrderedDict() @@ -297,16 +288,19 @@ def filter_params(self, parent_name, varlink_type, _namespaced, args, kwargs): return {} if isinstance(args, Mapping): - for (k, v) in args.items(): - args[k] = self.filter_params(parent_name + '[' + k + ']', varlink_type.element_type, _namespaced, v, - None) + for k, v in args.items(): + args[k] = self.filter_params( + parent_name + "[" + k + "]", varlink_type.element_type, _namespaced, v, None + ) return args else: InvalidParameter(parent_name) if isinstance(varlink_type, _CustomType): # print("CustomType", varlink_type.name) - return self.filter_params(parent_name, self.members.get(varlink_type.name), _namespaced, args, kwargs) + return self.filter_params( + parent_name, self.members.get(varlink_type.name), _namespaced, args, kwargs + ) if isinstance(varlink_type, _Alias): # print("Alias", varlink_type.name) @@ -323,8 +317,10 @@ def filter_params(self, parent_name, varlink_type, _namespaced, args, kwargs): if args is None: return [] - return [self.filter_params(parent_name + '[]', varlink_type.element_type, _namespaced, x, None) for x in - args] + return [ + self.filter_params(parent_name + "[]", varlink_type.element_type, _namespaced, x, None) + for x in args + ] if isinstance(varlink_type, Set): # print("Returned set:", set(args)) @@ -349,8 +345,7 @@ def filter_params(self, parent_name, varlink_type, _namespaced, args, kwargs): if not isinstance(varlink_type, _Struct): raise InvalidParameter(parent_name) - # SyntaxError("Expected type %s, got %s with value '%s'" % (type(varlink_type), type(args), - # args)) + # SyntaxError(f"Expected type {type(varlink_type)}, got {type(args)} with value '{args}'" if _namespaced: out = SimpleNamespace() @@ -370,8 +365,9 @@ def filter_params(self, parent_name, varlink_type, _namespaced, args, kwargs): args = args[1:] else: args = None - ret = self.filter_params(parent_name + "." + name, varlink_type.fields[name], _namespaced, val, - None) + ret = self.filter_params( + parent_name + "." + name, varlink_type.fields[name], _namespaced, val, None + ) if ret is not None: # print("SetOUT:", name) if _namespaced: @@ -381,8 +377,13 @@ def filter_params(self, parent_name, varlink_type, _namespaced, args, kwargs): continue else: if name in kwargs: - ret = self.filter_params(parent_name + "." + name, varlink_type.fields[name], _namespaced, - kwargs[name], None) + ret = self.filter_params( + parent_name + "." + name, + varlink_type.fields[name], + _namespaced, + kwargs[name], + None, + ) if ret is not None: # print("SetOUT:", name) if _namespaced: @@ -397,8 +398,9 @@ def filter_params(self, parent_name, varlink_type, _namespaced, args, kwargs): continue val = varlink_struct[name] - ret = self.filter_params(parent_name + "." + name, varlink_type.fields[name], _namespaced, val, - None) + ret = self.filter_params( + parent_name + "." + name, varlink_type.fields[name], _namespaced, val, None + ) if ret is not None: # print("SetOUT:", name) if _namespaced: @@ -407,8 +409,9 @@ def filter_params(self, parent_name, varlink_type, _namespaced, args, kwargs): out[name] = ret elif hasattr(varlink_struct, name): val = getattr(varlink_struct, name) - ret = self.filter_params(parent_name + "." + name, varlink_type.fields[name], _namespaced, val, - None) + ret = self.filter_params( + parent_name + "." + name, varlink_type.fields[name], _namespaced, val, None + ) if ret is not None: # print("SetOUT:", name) if _namespaced: diff --git a/varlink/server.py b/varlink/server.py index bab7ef8..4dc54d8 100644 --- a/varlink/server.py +++ b/varlink/server.py @@ -1,18 +1,14 @@ -# coding=utf-8 - import inspect import json import os import socket import stat import string +from socketserver import BaseServer, StreamRequestHandler, ThreadingMixIn -from .error import (InterfaceNotFound, InvalidParameter, MethodNotImplemented, VarlinkEncoder, VarlinkError) +from .error import InterfaceNotFound, InvalidParameter, MethodNotImplemented, VarlinkEncoder, VarlinkError from .scanner import Interface - -from socketserver import (StreamRequestHandler, BaseServer, ThreadingMixIn) - if hasattr(os, "fork"): from socketserver import ForkingMixIn @@ -55,7 +51,7 @@ class Service: """ - def __init__(self, vendor='', product='', version='', url='', interface_dir='.', namespaced=False): + def __init__(self, vendor="", product="", version="", url="", interface_dir=".", namespaced=False): """Initialize the service with the data org.varlink.service.GetInfo() returns :param interface_dir: the directory with the \\*.varlink files for the interfaces @@ -71,16 +67,16 @@ def __init__(self, vendor='', product='', version='', url='', interface_dir='.', self.interfaces = {} self.interfaces_handlers = {} directory = os.path.dirname(__file__) - self._add_interface(os.path.abspath(os.path.join(directory, 'org.varlink.service.varlink')), self) + self._add_interface(os.path.abspath(os.path.join(directory, "org.varlink.service.varlink")), self) def GetInfo(self): """The standardized org.varlink.service.GetInfo() varlink method.""" return { - 'vendor': self.vendor, - 'product': self.product, - 'version': self.version, - 'url': self.url, - 'interfaces': list(self.interfaces.keys()) + "vendor": self.vendor, + "product": self.product, + "version": self.version, + "url": self.url, + "interfaces": list(self.interfaces.keys()), } def GetInterfaceDescription(self, interface): @@ -90,11 +86,11 @@ def GetInterfaceDescription(self, interface): except KeyError: raise InterfaceNotFound(interface) - return {'description': i.description} + return {"description": i.description} def _handle(self, message, raw_message, _server=None, _request=None): try: - interface_name, _, method_name = message.get('method', '').rpartition('.') + interface_name, _, method_name = message.get("method", "").rpartition(".") if not interface_name or not method_name: raise InterfaceNotFound(interface_name) @@ -104,7 +100,7 @@ def _handle(self, message, raw_message, _server=None, _request=None): method = interface.get_method(method_name) - parameters = message.get('parameters', {}) + parameters = message.get("parameters", {}) if parameters is None: parameters = {} @@ -118,7 +114,9 @@ def _handle(self, message, raw_message, _server=None, _request=None): if name not in parameters: parameters[name] = None - parameters = interface.filter_params("server.call", method.in_type, self._namespaced, parameters, None) + parameters = interface.filter_params( + "server.call", method.in_type, self._namespaced, parameters, None + ) func = getattr(handler, method_name, None) @@ -129,41 +127,50 @@ def _handle(self, message, raw_message, _server=None, _request=None): if hasattr(inspect, "signature"): sig = inspect.signature(func) - arg_names = [(sig.parameters[k].kind in ( - inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) and k or None) for k in - sig.parameters.keys()] + arg_names = [ + ( + sig.parameters[k].kind + in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY) + and k + or None + ) + for k in sig.parameters.keys() + ] else: from itertools import izip + spec = inspect.getargspec(func) matched_args = [reversed(x) for x in [spec.args, spec.defaults or []]] arg_names = dict(izip(*matched_args)) - if message.get('more', False) or message.get('oneway', False) or message.get('upgrade', False): - if message.get('more', False) and '_more' in arg_names: + if message.get("more", False) or message.get("oneway", False) or message.get("upgrade", False): + if message.get("more", False) and "_more" in arg_names: kwargs["_more"] = True - if message.get('oneway', False) and '_oneway' in arg_names: + if message.get("oneway", False) and "_oneway" in arg_names: kwargs["_oneway"] = True - if message.get('upgrade', False) and '_upgrade' in arg_names: + if message.get("upgrade", False) and "_upgrade" in arg_names: kwargs["_upgrade"] = True - if '_raw' in arg_names: + if "_raw" in arg_names: kwargs["_raw"] = raw_message - if '_message' in arg_names: + if "_message" in arg_names: kwargs["_message"] = message - if '_interface' in arg_names: + if "_interface" in arg_names: kwargs["_interface"] = interface - if '_method' in arg_names: + if "_method" in arg_names: kwargs["_method"] = method - if '_server' in arg_names: + if "_server" in arg_names: kwargs["_server"] = _server - if '_request' in arg_names: + if "_request" in arg_names: kwargs["_request"] = _request if self._namespaced: # FIXME: check for Maybe before taking None as default value - out = func(*(getattr(parameters, k, default=None) for k in method.in_type.fields.keys()), **kwargs) + out = func( + *(getattr(parameters, k, default=None) for k in method.in_type.fields.keys()), **kwargs + ) else: # FIXME: check for Maybe before taking None as default value out = func(*(parameters.get(k) for k in method.in_type.fields.keys()), **kwargs) @@ -178,17 +185,31 @@ def _handle(self, message, raw_message, _server=None, _request=None): continue cont = True - if '_continues' in o: - cont = o['_continues'] - del o['_continues'] - yield {'continues': bool(cont), - 'parameters': interface.filter_params("server.reply", method.out_type, - self._namespaced, o, - None) or {}} + if "_continues" in o: + cont = o["_continues"] + del o["_continues"] + yield { + "continues": bool(cont), + "parameters": interface.filter_params( + "server.reply", + method.out_type, + self._namespaced, + o, + None, + ) + or {}, + } else: - yield {'parameters': interface.filter_params("server.reply", method.out_type, - self._namespaced, o, - None) or {}} + yield { + "parameters": interface.filter_params( + "server.reply", + method.out_type, + self._namespaced, + o, + None, + ) + or {} + } if not cont: return @@ -198,10 +219,10 @@ def _handle(self, message, raw_message, _server=None, _request=None): except StopIteration: pass else: - if message.get('oneway', False): + if message.get("oneway", False): yield None else: - yield {'parameters': out or {}} + yield {"parameters": out or {}} except VarlinkError as error: yield error @@ -220,13 +241,13 @@ def handle(self, message, _server=None, _request=None): if message[-1] == 0: message = message[:-1] - string = message.decode('utf-8') + string = message.decode("utf-8") handle = self._handle(json.loads(string), message, _server, _request) for out in handle: if out is None: return try: - yield json.dumps(out, cls=VarlinkEncoder).encode('utf-8') + yield json.dumps(out, cls=VarlinkEncoder).encode("utf-8") except ConnectionError as e: try: handle.throw(e) @@ -235,7 +256,7 @@ def handle(self, message, _server=None, _request=None): def _add_interface(self, filename, handler): if not os.path.isabs(filename): - filename = os.path.join(self.interface_dir, filename + '.varlink') + filename = os.path.join(self.interface_dir, filename + ".varlink") with open(filename) as f: interface = Interface(f.read()) @@ -243,7 +264,7 @@ def _add_interface(self, filename, handler): self.interfaces_handlers[interface.name] = handler def _set_interface(self, filename, interface_class): - if 'class' in str(type(interface_class)): + if "class" in str(type(interface_class)): ic = interface_class else: ic = interface_class() @@ -251,7 +272,6 @@ def _set_interface(self, filename, interface_class): return interface_class def interface(self, filename): - def decorator(interface_class): self._add_interface(filename, interface_class()) return interface_class @@ -264,16 +284,10 @@ def get_listen_fd(): return None if "LISTEN_PID" not in os.environ: return None - try: - if int(os.environ["LISTEN_PID"]) != os.getpid(): - return None - except: + if int(os.environ.get("LISTEN_PID", -1)) != os.getpid(): return None - try: - fds = int(os.environ["LISTEN_FDS"]) - except: - return None + fds = int(os.environ.get("LISTEN_FDS", -1)) if fds < 1: return None @@ -309,10 +323,11 @@ class RequestHandler(StreamRequestHandler): To use as an argument for the VarlinkServer constructor. Instantiate your own class and set the class variable service to your global :class:`Service` object. """ + service = None def handle(self): - message = b'' + message = b"" self.request.setblocking(True) while not self.rfile.closed: @@ -321,18 +336,18 @@ def handle(self): except BrokenPipeError: break - if c == b'': + if c == b"": break - if c != b'\0': + if c != b"\0": message += c continue for reply in self.service.handle(message, _server=self.server, _request=self.request): if reply is not None: - self.wfile.write(reply + b'\0') + self.wfile.write(reply + b"\0") - message = b'' + message = b"" class Server(BaseServer): @@ -357,7 +372,7 @@ class Server(BaseServer): >>> >>> server = varlink.ThreadingServer(sys.argv[1][10:], ServiceRequestHandler) >>> server.serve_forever() - """ + """ # noqa: E501 address_family = socket.AF_INET @@ -380,13 +395,13 @@ def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): elif server_address.startswith("unix:"): self.address_family = socket.AF_UNIX address = server_address[5:] - m = address.rfind(';mode=') + m = address.rfind(";mode=") if m != -1: - self.mode = address[m + 6:] + self.mode = address[m + 6 :] address = address[:m] - if address[0] == '@': - address = address.replace('@', '\0', 1) + if address[0] == "@": + address = address.replace("@", "\0", 1) self.mode = None else: self.remove_file = address @@ -397,20 +412,28 @@ def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): elif server_address.startswith("tcp:"): address = server_address[4:] - p = address.rfind(':') + p = address.rfind(":") if p != -1: - port = int(address[p + 1:]) + port = int(address[p + 1 :]) address = address[:p] else: - raise ConnectionError("Invalid address 'tcp:%s'" % address) - address = address.replace('[', '') - address = address.replace(']', '') + raise ConnectionError(f"Invalid address 'tcp:{address}'") + address = address.replace("[", "") + address = address.replace("]", "") try: - res = socket.getaddrinfo(address, port, proto=socket.IPPROTO_TCP, flags=socket.AI_NUMERICHOST) + res = socket.getaddrinfo( + address, port, proto=socket.IPPROTO_TCP, flags=socket.AI_NUMERICHOST + ) except TypeError: - res = socket.getaddrinfo(address, port, self.address_family, self.socket_type, socket.IPPROTO_TCP, - socket.AI_NUMERICHOST) + res = socket.getaddrinfo( + address, + port, + self.address_family, + self.socket_type, + socket.IPPROTO_TCP, + socket.AI_NUMERICHOST, + ) af, socktype, proto, canonname, sa = res[0] self.address_family = af @@ -419,7 +442,7 @@ def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): server_address = sa[0:2] else: - raise ConnectionError("Invalid address '%s'" % server_address) + raise ConnectionError(f"Invalid address '{server_address}'") BaseServer.__init__(self, server_address, RequestHandlerClass) @@ -427,7 +450,7 @@ def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): try: self.server_bind() self.server_activate() - except: + except Exception: # TODO: maybe just OSError? self.server_close() raise @@ -447,7 +470,7 @@ def server_bind(self): self.server_address = self.socket.getsockname() if self.server_address[0] == 0: - self.server_address = '@' + self.server_address[1:].decode('utf-8') + self.server_address = "@" + self.server_address[1:].decode("utf-8") if self.mode: os.fchmod(self.socket.fileno(), mode=int(self.mode, 8)) elif self.mode: @@ -470,7 +493,7 @@ def server_close(self): if self.remove_file: try: os.remove(self.remove_file) - except: + except OSError: pass self.socket.close() @@ -497,7 +520,7 @@ def shutdown_request(self, request): # the socket and waits for GC to perform the actual close. request.shutdown(socket.SHUT_RDWR) - except: + except Exception: # TODO: maybe just OSError? pass # some platforms may raise ENOTCONN here self.close_request(request) @@ -512,8 +535,11 @@ def __exit__(self, *args): self.server_close() -class ThreadingServer(ThreadingMixIn, Server): pass +class ThreadingServer(ThreadingMixIn, Server): + pass if hasattr(os, "fork"): - class ForkingServer(ForkingMixIn, Server): pass + + class ForkingServer(ForkingMixIn, Server): + pass diff --git a/varlink/tests/__init__.py b/varlink/tests/__init__.py index 2223c79..145e7dd 100644 --- a/varlink/tests/__init__.py +++ b/varlink/tests/__init__.py @@ -5,6 +5,7 @@ here = os.path.dirname(__file__) loader = unittest.defaultTestLoader + def suite(): suite = unittest.TestSuite() for fn in os.listdir(here): diff --git a/varlink/tests/test_basic_network.py b/varlink/tests/test_basic_network.py index 76eb7b9..fcabe3b 100755 --- a/varlink/tests/test_basic_network.py +++ b/varlink/tests/test_basic_network.py @@ -4,15 +4,14 @@ import unittest from sys import platform - import varlink service = varlink.Service( - vendor='Varlink', - product='Varlink Examples', - version='1', - url='http://varlink.org', - interface_dir=os.path.dirname(__file__) + vendor="Varlink", + product="Varlink Examples", + version="1", + url="http://varlink.org", + interface_dir=os.path.dirname(__file__), ) @@ -28,15 +27,14 @@ def do_run(self, address): server_thread.start() try: - with varlink.Client(address) as client, \ - client.open('org.varlink.service') as _connection: + with varlink.Client(address) as client, client.open("org.varlink.service") as _connection: info = _connection.GetInfo() - self.assertEqual(len(info['interfaces']), 1) - self.assertEqual(info['interfaces'][0], "org.varlink.service") + self.assertEqual(len(info["interfaces"]), 1) + self.assertEqual(info["interfaces"][0], "org.varlink.service") self.assertEqual(info, service.GetInfo()) - desc = _connection.GetInterfaceDescription(info['interfaces'][0]) + desc = _connection.GetInterfaceDescription(info["interfaces"][0]) self.assertEqual(desc, service.GetInterfaceDescription("org.varlink.service")) _connection.close() @@ -50,18 +48,13 @@ def test_tcp(self): def test_anon_unix(self): if platform.startswith("linux"): - self.do_run("unix:@org.varlink.service_anon_test" - + str(os.getpid()) - + threading.current_thread().name - ) + self.do_run(f"unix:@org.varlink.service_anon_test{os.getpid()}{threading.current_thread().name}") def test_unix(self): if hasattr(socket, "AF_UNIX"): - self.do_run("unix:org.varlink.service_anon_test_" - + str(os.getpid()) - + threading.current_thread().name - ) + self.do_run(f"unix:org.varlink.service_anon_test_{os.getpid()}{threading.current_thread().name}") def test_wrong_url(self): - self.assertRaises(ConnectionError, self.do_run, - "uenix:org.varlink.service_wrong_url_test_%d" % os.getpid()) + self.assertRaises( + ConnectionError, self.do_run, f"uenix:org.varlink.service_wrong_url_test_{os.getpid()}" + ) diff --git a/varlink/tests/test_certification.py b/varlink/tests/test_certification.py index 3c43a66..8fc59fd 100755 --- a/varlink/tests/test_certification.py +++ b/varlink/tests/test_certification.py @@ -8,6 +8,7 @@ import shlex import socket import sys +import textwrap import threading import time import unittest @@ -15,12 +16,12 @@ import varlink - ######## CLIENT ############# + def run_client(client): - print('Connecting to %s\n' % client) - with client.open('org.varlink.certification') as con: + print(f"Connecting to {client}\n") + with client.open("org.varlink.certification") as con: ret = con.Start() client_id = ret["client_id"] print("client_id:", client_id) @@ -56,11 +57,11 @@ def run_client(client): ######## SERVER ############# service = varlink.Service( - vendor='Varlink', - product='Varlink Examples', - version='1', - url='http://varlink.org', - interface_dir=os.path.dirname(__file__) + vendor="Varlink", + product="Varlink Examples", + version="1", + url="http://varlink.org", + interface_dir=os.path.dirname(__file__), ) @@ -75,19 +76,22 @@ def sorted_json(dct): class CertificationError(varlink.VarlinkError): - def __init__(self, wants, got): - varlink.VarlinkError.__init__(self, - {'error': 'org.varlink.certification.CertificationError', - 'parameters': {'wants': wants, 'got': got}}) + varlink.VarlinkError.__init__( + self, + { + "error": "org.varlink.certification.CertificationError", + "parameters": {"wants": wants, "got": got}, + }, + ) -@service.interface('org.varlink.certification') +@service.interface("org.varlink.certification") class CertService: next_method = {} def new_client_id(self, _server): - client_id = codecs.getencoder('hex')(os.urandom(16))[0].decode("ascii") + client_id = codecs.getencoder("hex")(os.urandom(16))[0].decode("ascii") if not hasattr(_server, "next_method"): _server.next_method = {} if not hasattr(_server, "lifetimes"): @@ -117,42 +121,58 @@ def check_lifetimes(self, _server): def assert_raw(self, client_id, _server, _raw, _message, wants): if wants != _message: del _server.next_method[client_id] - raise CertificationError(wants, json.loads(_raw.decode('utf-8'))) + raise CertificationError(wants, json.loads(_raw.decode("utf-8"))) def assert_cmp(self, client_id, _server, _raw, wants, _bool): if not _bool: del _server.next_method[client_id] - raise CertificationError(wants, json.loads(_raw.decode('utf-8'))) + raise CertificationError(wants, json.loads(_raw.decode("utf-8"))) def assert_method(self, client_id, _server, from_method, next_method): if not hasattr(_server, "next_method") or client_id not in _server.next_method: - raise CertificationError({"method": "org.varlink.certification.Start+++"}, - {"method": "org.varlink.certification." + from_method}) + raise CertificationError( + {"method": "org.varlink.certification.Start+++"}, + {"method": "org.varlink.certification." + from_method}, + ) self.check_lifetimes(_server) if from_method != _server.next_method[client_id]: - raise CertificationError("Call to method org.varlink.certification." + _server.next_method[client_id], - "Call to method org.varlink.certification." + from_method) + raise CertificationError( + "Call to method org.varlink.certification." + _server.next_method[client_id], + "Call to method org.varlink.certification." + from_method, + ) _server.next_method[client_id] = next_method def Start(self, _server=None, _raw=None, _message=None, _oneway=False): client_id = self.new_client_id(_server) - if 'parameters' in _message and not _message['parameters']: - del _message['parameters'] + if "parameters" in _message and not _message["parameters"]: + del _message["parameters"] self.assert_method(client_id, _server, "Start", "Test01") - self.assert_raw(client_id, _server, _raw, _message, { - "method": "org.varlink.certification.Start", - }) + self.assert_raw( + client_id, + _server, + _raw, + _message, + { + "method": "org.varlink.certification.Start", + }, + ) return {"client_id": client_id} # () -> (bool: bool) def Test01(self, client_id, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test01", "Test02") - self.assert_raw(client_id, _server, _raw, _message, { - "method": "org.varlink.certification.Test01", - "parameters": {"client_id": client_id} - }) + self.assert_raw( + client_id, + _server, + _raw, + _message, + { + "method": "org.varlink.certification.Test01", + "parameters": {"client_id": client_id}, + }, + ) return {"bool": True} # (bool: bool) -> (int: int) @@ -160,9 +180,9 @@ def Test02(self, client_id, _bool, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test02", "Test03") wants = { "method": "org.varlink.certification.Test02", - "parameters": {"client_id": client_id, "bool": True} + "parameters": {"client_id": client_id, "bool": True}, } - self.assert_cmp(client_id, _server, _raw, wants, _bool == True) + self.assert_cmp(client_id, _server, _raw, wants, _bool == True) # noqa: E712 self.assert_raw(client_id, _server, _raw, _message, wants) return {"int": 1} @@ -171,7 +191,7 @@ def Test03(self, client_id, _int, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test03", "Test04") wants = { "method": "org.varlink.certification.Test03", - "parameters": {"client_id": client_id, "int": 1} + "parameters": {"client_id": client_id, "int": 1}, } self.assert_cmp(client_id, _server, _raw, wants, _int == 1) self.assert_raw(client_id, _server, _raw, _message, wants) @@ -182,7 +202,7 @@ def Test04(self, client_id, _float, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test04", "Test05") wants = { "method": "org.varlink.certification.Test04", - "parameters": {"client_id": client_id, "float": 1.0} + "parameters": {"client_id": client_id, "float": 1.0}, } self.assert_cmp(client_id, _server, _raw, wants, _float == 1.0) self.assert_raw(client_id, _server, _raw, _message, wants) @@ -193,7 +213,7 @@ def Test05(self, client_id, _string, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test05", "Test06") wants = { "method": "org.varlink.certification.Test05", - "parameters": {"client_id": client_id, "string": "ping"} + "parameters": {"client_id": client_id, "string": "ping"}, } self.assert_cmp(client_id, _server, _raw, wants, _string == "ping") self.assert_raw(client_id, _server, _raw, _message, wants) @@ -210,12 +230,12 @@ def Test06(self, client_id, _bool, _int, _float, _string, _server=None, _raw=Non "bool": False, "int": 2, "float": math.pi, - "string": "a lot of string" - } + "string": "a lot of string", + }, } self.assert_raw(client_id, _server, _raw, _message, wants) self.assert_cmp(client_id, _server, _raw, wants, _int == 2) - self.assert_cmp(client_id, _server, _raw, wants, _bool == False) + self.assert_cmp(client_id, _server, _raw, wants, _bool == False) # noqa: E712 self.assert_cmp(client_id, _server, _raw, wants, _float == math.pi) self.assert_cmp(client_id, _server, _raw, wants, _string == "a lot of string") @@ -228,12 +248,12 @@ def Test07(self, client_id, _dict, _server=None, _raw=None, _message=None): "method": "org.varlink.certification.Test07", "parameters": { "client_id": client_id, - "struct": {"bool": False, "int": 2, "float": math.pi, "string": "a lot of string"} - } + "struct": {"bool": False, "int": 2, "float": math.pi, "string": "a lot of string"}, + }, } self.assert_raw(client_id, _server, _raw, _message, wants) self.assert_cmp(client_id, _server, _raw, wants, _dict["int"] == 2) - self.assert_cmp(client_id, _server, _raw, wants, _dict["bool"] == False) + self.assert_cmp(client_id, _server, _raw, wants, _dict["bool"] == False) # noqa: E712 self.assert_cmp(client_id, _server, _raw, wants, _dict["float"] == math.pi) self.assert_cmp(client_id, _server, _raw, wants, _dict["string"] == "a lot of string") return {"map": {"foo": "Foo", "bar": "Bar"}} @@ -241,11 +261,16 @@ def Test07(self, client_id, _dict, _server=None, _raw=None, _message=None): # (map: [string]string) -> (set: [string]()) def Test08(self, client_id, _map, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test08", "Test09") - self.assert_raw(client_id, _server, _raw, _message, - { - "method": "org.varlink.certification.Test08", - "parameters": {"client_id": client_id, "map": {"foo": "Foo", "bar": "Bar"}} - }) + self.assert_raw( + client_id, + _server, + _raw, + _message, + { + "method": "org.varlink.certification.Test08", + "parameters": {"client_id": client_id, "map": {"foo": "Foo", "bar": "Bar"}}, + }, + ) return {"set": {"one", "two", "three"}} # (set: [string]()) -> (mytype: MyType) @@ -253,10 +278,7 @@ def Test09(self, client_id, _set, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "Test09", "Test10") wants = { "method": "org.varlink.certification.Test09", - "parameters": { - "client_id": client_id, - "set": {"one": {}, "three": {}, "two": {}} - } + "parameters": {"client_id": client_id, "set": {"one": {}, "three": {}, "two": {}}}, } self.assert_raw(client_id, _server, _raw, _message, wants) self.assert_cmp(client_id, _server, _raw, wants, isinstance(_set, set)) @@ -267,8 +289,10 @@ def Test09(self, client_id, _set, _server=None, _raw=None, _message=None): return { "client_id": client_id, "mytype": { - "object": {"method": "org.varlink.certification.Test09", - "parameters": {"map": {"foo": "Foo", "bar": "Bar"}}}, + "object": { + "method": "org.varlink.certification.Test09", + "parameters": {"map": {"foo": "Foo", "bar": "Bar"}}, + }, "enum": "two", "struct": {"first": 1, "second": "2"}, "array": ["one", "two", "three"], @@ -281,11 +305,11 @@ def Test09(self, client_id, _set, _server=None, _raw=None, _message=None): None, {"foo": "foo", "bar": "bar"}, None, - {"one": "foo", "two": "bar"} + {"one": "foo", "two": "bar"}, ], - "anon": {"foo": True, "bar": False} - } - } + "anon": {"foo": True, "bar": False}, + }, + }, } # method Test10(mytype: MyType) -> (string: string) @@ -298,8 +322,10 @@ def Test10(self, client_id, mytype, _server=None, _raw=None, _message=None): "parameters": { "client_id": client_id, "mytype": { - "object": {"method": "org.varlink.certification.Test09", - "parameters": {"map": {"foo": "Foo", "bar": "Bar"}}}, + "object": { + "method": "org.varlink.certification.Test09", + "parameters": {"map": {"foo": "Foo", "bar": "Bar"}}, + }, "enum": "two", "struct": {"first": 1, "second": "2"}, "array": ["one", "two", "three"], @@ -310,12 +336,12 @@ def Test10(self, client_id, mytype, _server=None, _raw=None, _message=None): None, {"foo": "foo", "bar": "bar"}, None, - {"one": "foo", "two": "bar"} + {"one": "foo", "two": "bar"}, ], - "anon": {"foo": True, "bar": False} - } - } - } + "anon": {"foo": True, "bar": False}, + }, + }, + }, } if "nullable" in mytype: @@ -329,7 +355,7 @@ def Test10(self, client_id, mytype, _server=None, _raw=None, _message=None): self.assert_cmp(client_id, _server, _raw, wants, mytype == wants["parameters"]["mytype"]) for i in range(1, 11): - yield {"string": "Reply number %d" % i, '_continues': i != 10} + yield {"string": f"Reply number {i}", "_continues": i != 10} # method Test11(last_more_replies: []string) -> () def Test11(self, client_id, last_more_replies, _server=None, _raw=None, _message=None, _oneway=False): @@ -340,26 +366,36 @@ def Test11(self, client_id, last_more_replies, _server=None, _raw=None, _message "parameters": { "client_id": client_id, "last_more_replies": [ - "Reply number 1", "Reply number 2", "Reply number 3", "Reply number 4", - "Reply number 5", "Reply number 6", "Reply number 7", "Reply number 8", - "Reply number 9", "Reply number 10" - ] - } + "Reply number 1", + "Reply number 2", + "Reply number 3", + "Reply number 4", + "Reply number 5", + "Reply number 6", + "Reply number 7", + "Reply number 8", + "Reply number 9", + "Reply number 10", + ], + }, } self.assert_cmp(client_id, _server, _raw, wants, _oneway) for i in range(0, 10): - self.assert_cmp(client_id, _server, _raw, wants, last_more_replies[i] == "Reply number %d" % (i + 1)) + self.assert_cmp(client_id, _server, _raw, wants, last_more_replies[i] == f"Reply number {i + 1}") # method End() -> () def End(self, client_id, _server=None, _raw=None, _message=None): self.assert_method(client_id, _server, "End", "Start") - self.assert_raw(client_id, _server, _raw, _message, { - "method": "org.varlink.certification.End", - "parameters": {"client_id": client_id} - }) + self.assert_raw( + client_id, + _server, + _raw, + _message, + {"method": "org.varlink.certification.End", "parameters": {"client_id": client_id}}, + ) del _server.next_method[client_id] return {"all_ok": True} @@ -376,18 +412,29 @@ def run_server(address): ######## MAIN ############# -def usage(): - print('Usage: %s [[--client] --varlink=]' % sys.argv[0], file=sys.stderr) - print('\tSelf Exec: $ %s' % sys.argv[0], file=sys.stderr) - print('\tServer : $ %s --varlink=' % sys.argv[0], file=sys.stderr) - print('\tClient : $ %s --client --varlink=' % sys.argv[0], file=sys.stderr) - print('\tClient : $ %s --client --bridge=' % sys.argv[0], file=sys.stderr) - print('\tClient : $ %s --client --activate=' % sys.argv[0], file=sys.stderr) - -if __name__ == '__main__': +def usage(): + arg0 = sys.argv[0] + print( + textwrap.dedent( + f"""\ + Usage: {arg0} [[--client] --varlink=] + \tSelf Exec: $ {arg0} + \tServer : $ {arg0} --varlink= + \tClient : $ {arg0} --client --varlink= + \tClient : $ {arg0} --client --bridge= + \tClient : $ {arg0} --client --activate= + """ + ), + file=sys.stderr, + ) + + +if __name__ == "__main__": try: - opts, args = getopt.getopt(sys.argv[1:], "b:A:", ["help", "client", "varlink=", "bridge=", "activate="]) + opts, args = getopt.getopt( + sys.argv[1:], "b:A:", ["help", "client", "varlink=", "bridge=", "activate="] + ) except getopt.GetoptError: usage() sys.exit(2) @@ -422,7 +469,7 @@ def usage(): if not address and not client_mode: if not hasattr(socket, "AF_UNIX"): - print("varlink activate: not supported on platform %s" % platform, file=sys.stderr) + print(f"varlink activate: not supported on platform {platform}", file=sys.stderr) usage() sys.exit(2) @@ -440,13 +487,14 @@ def usage(): ######## UNITTEST ############# + class TestService(unittest.TestCase): @classmethod def setUpClass(cls): if hasattr(socket, "AF_UNIX"): - cls.address = "unix:org.varlink.certification_" \ - + str(os.getpid()) \ - + threading.current_thread().name + cls.address = ( + "unix:org.varlink.certification_" + str(os.getpid()) + threading.current_thread().name + ) else: cls.address = "tcp:127.0.0.1:23456" @@ -459,8 +507,7 @@ def test_client(self): run_client(varlink.Client.new_with_address(self.address)) def test_01(self): - with varlink.Client(self.address) as client, \ - client.open('org.varlink.certification') as con: + with varlink.Client(self.address) as client, client.open("org.varlink.certification") as con: ret = con.Start() client_id = ret["client_id"] ret = con.Test01(client_id) diff --git a/varlink/tests/test_mocks.py b/varlink/tests/test_mocks.py index e39cc28..6bdac1d 100644 --- a/varlink/tests/test_mocks.py +++ b/varlink/tests/test_mocks.py @@ -1,7 +1,7 @@ import unittest -from varlink import mock -import varlink +import varlink +from varlink import mock types = """ type MyPersonalType ( @@ -11,18 +11,17 @@ """ -class Service(): - +class Service: def Test1(self, param1: int) -> str: """return test: MyPersonalType""" return { "test": { "foo": "bim", - "bar": "boom" - } + "bar": "boom", + }, } - def Test2(self, param1: str="test") -> None: + def Test2(self, param1: str = "test") -> None: pass def Test3(self, param1: str) -> str: @@ -31,25 +30,18 @@ def Test3(self, param1: str) -> str: class TestMockMechanisms(unittest.TestCase): - - @mock.mockedservice( - fake_service=Service, - fake_types=types, - name='org.service.com', - address='unix:@foo' - ) + @mock.mockedservice(fake_service=Service, fake_types=types, name="org.service.com", address="unix:@foo") def test_init(self): with varlink.Client("unix:@foo") as client: - connection = client.open('org.service.com') + connection = client.open("org.service.com") self.assertEqual(connection.Test1(param1=1)["test"]["bar"], "boom") self.assertEqual(connection.Test3(param1="foo")["test"], "foo") class TestMockUtilities(unittest.TestCase): - def test_cast_type(self): - self.assertEqual(mock.cast_type(""), 'int') - self.assertEqual(mock.cast_type(""), 'string') + self.assertEqual(mock.cast_type(""), "int") + self.assertEqual(mock.cast_type(""), "string") def test_get_ignored(self): expected_ignore = dir(mock.MockedService) @@ -59,19 +51,12 @@ def test_get_ignored(self): def test_get_attributs(self): service = Service() attributs = mock.get_interface_attributs(service, mock.get_ignored()) - expected_result = { - "callables": ["Test1", "Test2", "Test3"], - "others": [] - } + expected_result = {"callables": ["Test1", "Test2", "Test3"], "others": []} self.assertEqual(attributs, expected_result) def test_generate_callable_interface(self): service = Service() - generated_itf = mock.generate_callable_interface(service, 'Test1') - self.assertEqual( - generated_itf, - "method Test1(param1: int) -> (test: MyPersonalType)") - generated_itf = mock.generate_callable_interface(service, 'Test3') - self.assertEqual( - generated_itf, - "method Test3(param1: string) -> (test: string)") + generated_itf = mock.generate_callable_interface(service, "Test1") + self.assertEqual(generated_itf, "method Test1(param1: int) -> (test: MyPersonalType)") + generated_itf = mock.generate_callable_interface(service, "Test3") + self.assertEqual(generated_itf, "method Test3(param1: string) -> (test: string)") diff --git a/varlink/tests/test_orgexamplemore.py b/varlink/tests/test_orgexamplemore.py index 834c6bb..51ed30a 100755 --- a/varlink/tests/test_orgexamplemore.py +++ b/varlink/tests/test_orgexamplemore.py @@ -29,26 +29,26 @@ import varlink - ######## CLIENT ############# + def run_client(client): - print('Connecting to %s\n' % client) + print(f"Connecting to {client}\n") try: - with \ - client.open('org.example.more', namespaced=True) as con1, \ - client.open('org.example.more', namespaced=True) as con2: - + with ( + client.open("org.example.more", namespaced=True) as con1, + client.open("org.example.more", namespaced=True) as con2, + ): for m in con1.TestMore(10, _more=True): - if hasattr(m.state, 'start') and m.state.start is not None: + if hasattr(m.state, "start") and m.state.start is not None: if m.state.start: print("--- Start ---", file=sys.stderr) - if hasattr(m.state, 'end') and m.state.end is not None: + if hasattr(m.state, "end") and m.state.end is not None: if m.state.end: print("--- End ---", file=sys.stderr) - if hasattr(m.state, 'progress') and m.state.progress is not None: + if hasattr(m.state, "progress") and m.state.progress is not None: print("Progress:", m.state.progress, file=sys.stderr) if m.state.progress > 50: ret = con2.Ping("Test") @@ -67,11 +67,11 @@ def run_client(client): ######## SERVER ############# service = varlink.Service( - vendor='Varlink', - product='Varlink Examples', - version='1', - url='http://varlink.org', - interface_dir=os.path.dirname(__file__) + vendor="Varlink", + product="Varlink Examples", + version="1", + url="http://varlink.org", + interface_dir=os.path.dirname(__file__), ) @@ -80,38 +80,37 @@ class ServiceRequestHandler(varlink.RequestHandler): class ActionFailed(varlink.VarlinkError): - def __init__(self, reason): - varlink.VarlinkError.__init__(self, - {'error': 'org.example.more.ActionFailed', - 'parameters': {'field': reason}}) + varlink.VarlinkError.__init__( + self, {"error": "org.example.more.ActionFailed", "parameters": {"field": reason}} + ) -@service.interface('org.example.more') +@service.interface("org.example.more") class Example: sleep_duration = 1 def TestMore(self, n, _more=True, _server=None): try: if not _more: - yield varlink.InvalidParameter('more') + yield varlink.InvalidParameter("more") - yield {'state': {'start': True}, '_continues': True} + yield {"state": {"start": True}, "_continues": True} for i in range(0, n): - yield {'state': {'progress': int(i * 100 / n)}, '_continues': True} + yield {"state": {"progress": int(i * 100 / n)}, "_continues": True} time.sleep(self.sleep_duration) - yield {'state': {'progress': 100}, '_continues': True} + yield {"state": {"progress": 100}, "_continues": True} - yield {'state': {'end': True}, '_continues': False} + yield {"state": {"end": True}, "_continues": False} except Exception as error: print("ERROR", error, file=sys.stderr) if _server: _server.shutdown() def Ping(self, ping): - return {'pong': ping} + return {"pong": ping} def StopServing(self, reason=None, _request=None, _server=None): print("Server ends.") @@ -127,13 +126,14 @@ def StopServing(self, reason=None, _request=None, _server=None): def TestMap(self, map): i = 1 ret = {} - for (key, val) in map.items(): + for key, val in map.items(): ret[key] = {"i": i, "val": val} i += 1 - return {'map': ret} + return {"map": ret} def TestObject(self, object): import json + return {"object": json.loads(json.dumps(object))} @@ -148,43 +148,31 @@ def run_server(address): ######## MAIN ############# + def epilog(): - return textwrap.dedent(""" + arg0 = sys.argv[0] + return textwrap.dedent( + f""" Examples: - \tSelf Exec: $ {0} - \tServer : $ {0} --varlink= - \tClient : $ {0} --client --varlink= - \tClient : $ {0} --client --bridge= - \tClient : $ {0} --client --activate= - """.format(sys.argv[0])) + \tSelf Exec: $ {arg0} + \tServer : $ {arg0} --varlink= + \tClient : $ {arg0} --client --varlink= + \tClient : $ {arg0} --client --bridge= + \tClient : $ {arg0} --client --activate= + """ + ) -if __name__ == '__main__': +if __name__ == "__main__": parser = argparse.ArgumentParser( - description='Varlink org.example.more test case', + description="Varlink org.example.more test case", epilog=epilog(), - formatter_class=argparse.RawTextHelpFormatter - ) - parser.add_argument( - '--varlink', - type=str, - help='The varlink address' - ) - parser.add_argument( - '-b', '--bridge', - type=str, - help='bridge command' - ) - parser.add_argument( - '-A', '--activate', - type=str, - help='activation command' - ) - parser.add_argument( - '--client', - action='store_true', - help='launch the client mode' + formatter_class=argparse.RawTextHelpFormatter, ) + parser.add_argument("--varlink", type=str, help="The varlink address") + parser.add_argument("-b", "--bridge", type=str, help="bridge command") + parser.add_argument("-A", "--activate", type=str, help="activation command") + parser.add_argument("--client", action="store_true", help="launch the client mode") args = parser.parse_args() address = args.varlink @@ -204,14 +192,12 @@ def epilog(): if not address and not client_mode: if not hasattr(socket, "AF_UNIX"): - print("varlink activate: not supported on platform %s" % platform, - file=sys.stderr) + print(f"varlink activate: not supported on platform {platform}", file=sys.stderr) parser.print_help() sys.exit(2) client_mode = True - with varlink.Client.new_with_activate( - [__file__, "--varlink=$VARLINK_ADDRESS"]) as client: + with varlink.Client.new_with_activate([__file__, "--varlink=$VARLINK_ADDRESS"]) as client: run_client(client) elif client_mode: with client: @@ -224,6 +210,7 @@ def epilog(): ######## UNITTEST ############# + class TestService(unittest.TestCase): def test_service(self): address = "tcp:127.0.0.1:23451" @@ -238,25 +225,25 @@ def test_service(self): run_client(client) - with \ - client.open('org.example.more', namespaced=True) as con1, \ - client.open('org.example.more', namespaced=True) as con2: - + with ( + client.open("org.example.more", namespaced=True) as con1, + client.open("org.example.more", namespaced=True) as con2, + ): self.assertEqual(con1.Ping("Test").pong, "Test") it = con1.TestMore(10, _more=True) m = next(it) - self.assertTrue(hasattr(m.state, 'start')) - self.assertFalse(hasattr(m.state, 'end')) - self.assertFalse(hasattr(m.state, 'progress')) + self.assertTrue(hasattr(m.state, "start")) + self.assertFalse(hasattr(m.state, "end")) + self.assertFalse(hasattr(m.state, "progress")) self.assertIsNotNone(m.state.start) for i in range(0, 110, 10): m = next(it) - self.assertTrue(hasattr(m.state, 'progress')) - self.assertFalse(hasattr(m.state, 'start')) - self.assertFalse(hasattr(m.state, 'end')) + self.assertTrue(hasattr(m.state, "progress")) + self.assertFalse(hasattr(m.state, "start")) + self.assertFalse(hasattr(m.state, "end")) self.assertIsNotNone(m.state.progress) self.assertEqual(i, m.state.progress) @@ -265,9 +252,9 @@ def test_service(self): self.assertEqual("Test", ret.pong) m = next(it) - self.assertTrue(hasattr(m.state, 'end')) - self.assertFalse(hasattr(m.state, 'start')) - self.assertFalse(hasattr(m.state, 'progress')) + self.assertTrue(hasattr(m.state, "end")) + self.assertFalse(hasattr(m.state, "start")) + self.assertFalse(hasattr(m.state, "progress")) self.assertIsNotNone(m.state.end) self.assertRaises(StopIteration, next, it) diff --git a/varlink/tests/test_scanner.py b/varlink/tests/test_scanner.py index da5f2da..cb4e95e 100755 --- a/varlink/tests/test_scanner.py +++ b/varlink/tests/test_scanner.py @@ -83,10 +83,18 @@ def test_complex(self): def test_interfacename(self): self.assertRaises(SyntaxError, varlink.Interface, "interface .a.b.c\nmethod F()->()") - self.assertRaises(SyntaxError, varlink.Interface, "interface com.-example.leadinghyphen\nmethod F()->()") - self.assertRaises(SyntaxError, varlink.Interface, "interface com.example-.danglinghyphen-\nmethod F()->()") - self.assertRaises(SyntaxError, varlink.Interface, "interface co9.example.number-toplevel\nmethod F()->()") - self.assertRaises(SyntaxError, varlink.Interface, "interface 1om.example.number-toplevel\nmethod F()->()") + self.assertRaises( + SyntaxError, varlink.Interface, "interface com.-example.leadinghyphen\nmethod F()->()" + ) + self.assertRaises( + SyntaxError, varlink.Interface, "interface com.example-.danglinghyphen-\nmethod F()->()" + ) + self.assertRaises( + SyntaxError, varlink.Interface, "interface co9.example.number-toplevel\nmethod F()->()" + ) + self.assertRaises( + SyntaxError, varlink.Interface, "interface 1om.example.number-toplevel\nmethod F()->()" + ) self.assertRaises(SyntaxError, varlink.Interface, "interface ab\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface .a.b.c\nmethod F()->()") self.assertRaises(SyntaxError, varlink.Interface, "interface a.b.c.\nmethod F()->()") @@ -107,5 +115,9 @@ def test_interfacename(self): self.assertIsNotNone(varlink.Interface("interface org.varlink.service\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface com.example.0example\nmethod F()->()").name) self.assertIsNotNone(varlink.Interface("interface com.example.example-dash\nmethod F()->()").name) - self.assertIsNotNone(varlink.Interface("interface xn--lgbbat1ad8j.example.algeria\nmethod F()->()").name) - self.assertIsNotNone(varlink.Interface("interface xn--c1yn36f.xn--c1yn36f.xn--c1yn36f\nmethod F()->()").name) + self.assertIsNotNone( + varlink.Interface("interface xn--lgbbat1ad8j.example.algeria\nmethod F()->()").name + ) + self.assertIsNotNone( + varlink.Interface("interface xn--c1yn36f.xn--c1yn36f.xn--c1yn36f\nmethod F()->()").name + )