diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 08ea785..b8eeba9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,3 +28,8 @@ repos: hooks: - id: bandit args: ['-x', 'tests,benchmark', '--skip', 'B404'] + - repo: https://github.com/asottile/pyupgrade + rev: v3.18.0 + hooks: + - id: pyupgrade + args: [--py3-only] diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py index 7340166..b60bb3d 100644 --- a/benchmark/benchmark.py +++ b/benchmark/benchmark.py @@ -70,19 +70,19 @@ runners = [ def get_time_string(sec): if sec > 0.9: - return "{0:7.3f}s ".format(sec) + return "{:7.3f}s ".format(sec) elif sec > 0.0009: - return "{0:7.3f}ms".format(sec * 1000.0) + return "{:7.3f}ms".format(sec * 1000.0) else: - return "{0:7.3f}us".format(sec * 1000000.0) + return "{:7.3f}us".format(sec * 1000000.0) def run_bench(cmd, runners): strcmd = ' '.join(cmd) max_name_len = max(len(name) for name, _ in runners) + len(strcmd) - 3 - print("Running '{0}':".format(strcmd)) + print("Running '{}':".format(strcmd)) print("{0:^{1}} :".format("method", max_name_len), - "".join(map("{0:^10}".format, ["min", "avg", "max", "dev"]))) + "".join(map("{:^10}".format, ["min", "avg", "max", "dev"]))) for name, runner in runners: results = timeit.repeat(run_one(runner, cmd), repeat=num_iterations, number=1) diff --git a/doc/source/conf.py b/doc/source/conf.py index 88d0788..5611261 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2020 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/oslo_rootwrap/client.py b/oslo_rootwrap/client.py index a607be3..2eab9a5 100644 --- a/oslo_rootwrap/client.py +++ b/oslo_rootwrap/client.py @@ -45,7 +45,7 @@ LOG = logging.getLogger(__name__) SHUTDOWN_RETRIES = 3 -class Client(object): +class Client: def __init__(self, rootwrap_daemon_cmd): self._start_command = rootwrap_daemon_cmd self._initialized = False @@ -114,7 +114,7 @@ class Client(object): try: manager.rootwrap().shutdown() break - except (EOFError, IOError): + except (EOFError, OSError): break # assume it is dead already except RuntimeError: time.sleep(0.2) @@ -167,7 +167,7 @@ class Client(object): proxy = self._restart(proxy) try: res = self._run_one_command(proxy, cmd, stdin) - except (EOFError, IOError): + except (EOFError, OSError): retry = True # res can be None if we received final None sent by dying # server thread instead of response to our diff --git a/oslo_rootwrap/cmd.py b/oslo_rootwrap/cmd.py index b28a200..53a9f1b 100644 --- a/oslo_rootwrap/cmd.py +++ b/oslo_rootwrap/cmd.py @@ -51,7 +51,7 @@ SIGNAL_BASE = 128 def _exit_error(execname, message, errorcode, log=True): - print("%s: %s" % (execname, message), file=sys.stderr) + print("{}: {}".format(execname, message), file=sys.stderr) if log: logging.error(message) sys.exit(errorcode) @@ -81,7 +81,7 @@ def main(run_daemon=False): rawconfig.read(configfile) config = wrapper.RootwrapConfig(rawconfig) except ValueError as exc: - msg = "Incorrect value in %s: %s" % (configfile, exc.args[0]) + msg = "Incorrect value in {}: {}".format(configfile, exc.args[0]) _exit_error(execname, msg, RC_BADCONFIG, log=False) except configparser.Error: _exit_error(execname, "Incorrect configuration file: %s" % configfile, diff --git a/oslo_rootwrap/daemon.py b/oslo_rootwrap/daemon.py index 533ae6d..abb8dbd 100644 --- a/oslo_rootwrap/daemon.py +++ b/oslo_rootwrap/daemon.py @@ -38,7 +38,7 @@ LOG = logging.getLogger(__name__) managers.listener_client['jsonrpc'] = jsonrpc.JsonListener, jsonrpc.JsonClient -class RootwrapClass(object): +class RootwrapClass: def __init__(self, config, filters): self.config = config self.filters = filters @@ -115,8 +115,8 @@ def get_manager_class(config=None, filters=None): class RootwrapManager(managers.BaseManager): def __init__(self, address=None, authkey=None): # Force jsonrpc because neither pickle nor xmlrpclib is secure - super(RootwrapManager, self).__init__(address, authkey, - serializer='jsonrpc') + super().__init__(address, authkey, + serializer='jsonrpc') if config is not None: partial_class = functools.partial(RootwrapClass, config, filters) diff --git a/oslo_rootwrap/filters.py b/oslo_rootwrap/filters.py index 8d7bf0e..541ce1c 100644 --- a/oslo_rootwrap/filters.py +++ b/oslo_rootwrap/filters.py @@ -46,7 +46,7 @@ def realpath(path): return '' -class CommandFilter(object): +class CommandFilter: """Command filter only checking that the 1st argument matches exec_path.""" def __init__(self, exec_path, run_as, *args): @@ -138,7 +138,7 @@ class PathFilter(CommandFilter): arguments = userargs[1:] equal_args_num = len(self.args) == len(arguments) - exec_is_valid = super(PathFilter, self).match(userargs) + exec_is_valid = super().match(userargs) args_equal_or_pass = all( arg == 'pass' or arg == value for arg, value in zip(self.args, arguments) @@ -163,8 +163,8 @@ class PathFilter(CommandFilter): args = [realpath(value) if os.path.isabs(arg) else value for arg, value in zip(self.args, arguments)] - return super(PathFilter, self).get_command([command] + args, - exec_dirs) + return super().get_command([command] + args, + exec_dirs) class KillFilter(CommandFilter): @@ -180,7 +180,7 @@ class KillFilter(CommandFilter): """ def __init__(self, *args): - super(KillFilter, self).__init__("/bin/kill", *args) + super().__init__("/bin/kill", *args) @staticmethod def _program_path(command): @@ -209,7 +209,7 @@ class KillFilter(CommandFilter): try: command = os.readlink("/proc/%d/exe" % int(pid)) - except (ValueError, EnvironmentError): + except (ValueError, OSError): # Incorrect PID return None @@ -241,7 +241,7 @@ class KillFilter(CommandFilter): # as that will allow killing a process where the exe # has been removed from the system rather than updated. return command - except EnvironmentError: + except OSError: return None def match(self, userargs): @@ -282,7 +282,7 @@ class ReadFileFilter(CommandFilter): def __init__(self, file_path, *args): self.file_path = file_path - super(ReadFileFilter, self).__init__("/bin/cat", "root", *args) + super().__init__("/bin/cat", "root", *args) def match(self, userargs): return (userargs == ['cat', self.file_path]) @@ -319,7 +319,7 @@ class EnvFilter(CommandFilter): return envs def __init__(self, exec_path, run_as, *args): - super(EnvFilter, self).__init__(exec_path, run_as, *args) + super().__init__(exec_path, run_as, *args) env_list = self._extract_env(self.args) # Set exec_path to X when args are in the form of @@ -342,7 +342,7 @@ class EnvFilter(CommandFilter): user_command = userargs[len(user_envs):len(user_envs) + 1] # match first non-env argument with CommandFilter - return (super(EnvFilter, self).match(user_command) and + return (super().match(user_command) and len(filter_envs) and user_envs == filter_envs) def exec_args(self, userargs): diff --git a/oslo_rootwrap/jsonrpc.py b/oslo_rootwrap/jsonrpc.py index 195febb..8de2531 100644 --- a/oslo_rootwrap/jsonrpc.py +++ b/oslo_rootwrap/jsonrpc.py @@ -40,7 +40,7 @@ class RpcJSONEncoder(json.JSONEncoder): # Other errors will fail to pass JSON encoding and will be visible on # client side else: - return super(RpcJSONEncoder, self).default(o) + return super().default(o) # Parse whatever RpcJSONEncoder supplied us with @@ -57,7 +57,7 @@ def rpc_object_hook(obj): return obj -class JsonListener(object): +class JsonListener: def __init__(self, address, backlog=1): self.address = address self._socket = socket.socket(socket.AF_UNIX) @@ -65,7 +65,7 @@ class JsonListener(object): self._socket.setblocking(True) self._socket.bind(address) self._socket.listen(backlog) - except socket.error: + except OSError: self._socket.close() raise self.closed = False @@ -75,7 +75,7 @@ class JsonListener(object): while True: try: s, _ = self._socket.accept() - except socket.error as e: + except OSError as e: if e.errno in (errno.EINVAL, errno.EBADF): raise EOFError elif e.errno != errno.EINTR: @@ -109,7 +109,7 @@ if hasattr(managers.Server, 'accepter'): managers.Server.accepter = silent_accepter -class JsonConnection(object): +class JsonConnection: def __init__(self, sock): sock.setblocking(True) self._socket = sock @@ -190,7 +190,7 @@ class JsonClient(JsonConnection): sock = socket.socket(socket.AF_UNIX) sock.setblocking(True) sock.connect(address) - super(JsonClient, self).__init__(sock) + super().__init__(sock) if authkey is not None: connection.answer_challenge(self, authkey) connection.deliver_challenge(self, authkey) diff --git a/oslo_rootwrap/tests/run_daemon.py b/oslo_rootwrap/tests/run_daemon.py index a39e4cf..5a9e2c0 100644 --- a/oslo_rootwrap/tests/run_daemon.py +++ b/oslo_rootwrap/tests/run_daemon.py @@ -39,7 +39,7 @@ def forwarding_popen(f, old_popen=subprocess.Popen): return popen -class nonclosing(object): +class nonclosing: def __init__(self, f): self._f = f diff --git a/oslo_rootwrap/tests/test_functional.py b/oslo_rootwrap/tests/test_functional.py index f8755a8..7d5157a 100644 --- a/oslo_rootwrap/tests/test_functional.py +++ b/oslo_rootwrap/tests/test_functional.py @@ -41,9 +41,9 @@ from oslo_rootwrap import subprocess from oslo_rootwrap.tests import run_daemon -class _FunctionalBase(object): +class _FunctionalBase: def setUp(self): - super(_FunctionalBase, self).setUp() + super().setUp() tmpdir = self.useFixture(fixtures.TempDir()).path self.config_file = os.path.join(tmpdir, 'rootwrap.conf') self.later_cmd = os.path.join(tmpdir, 'later_install_cmd') @@ -52,9 +52,9 @@ class _FunctionalBase(object): os.mkdir(filters_dir) with open(self.config_file, 'w') as f: f.write("""[DEFAULT] -filters_path=%s +filters_path={} daemon_timeout=10 -exec_dirs=/bin""" % (filters_dir,)) +exec_dirs=/bin""".format(filters_dir)) with open(filters_file, 'w') as f: f.write("""[Filters] echo: CommandFilter, /bin/echo, root @@ -120,7 +120,7 @@ later_install_cmd: CommandFilter, %s, root class RootwrapTest(_FunctionalBase, testtools.TestCase): def setUp(self): - super(RootwrapTest, self).setUp() + super().setUp() self.cmd = [ sys.executable, '-c', 'from oslo_rootwrap import cmd; cmd.main()', @@ -157,7 +157,7 @@ class RootwrapDaemonTest(_FunctionalBase, testtools.TestCase): def setUp(self): self.assert_unpatched() - super(RootwrapDaemonTest, self).setUp() + super().setUp() # Collect daemon logs daemon_log = io.BytesIO() diff --git a/oslo_rootwrap/tests/test_rootwrap.py b/oslo_rootwrap/tests/test_rootwrap.py index d5c8b73..ed1b123 100644 --- a/oslo_rootwrap/tests/test_rootwrap.py +++ b/oslo_rootwrap/tests/test_rootwrap.py @@ -66,7 +66,7 @@ class RootwrapTestCase(testtools.TestCase): _ip = '/bin/ip' def setUp(self): - super(RootwrapTestCase, self).setUp() + super().setUp() self.filters = [ filters.RegExpFilter("/bin/ls", "root", 'ls', '/[a-z]+'), filters.CommandFilter("/usr/bin/foo_bar_not_exist", "root"), @@ -528,7 +528,7 @@ class RootwrapTestCase(testtools.TestCase): class PathFilterTestCase(testtools.TestCase): def setUp(self): - super(PathFilterTestCase, self).setUp() + super().setUp() self.tmp_root_dir = tempfile.mkdtemp() tmpdir = fixtures.TempDir(self.tmp_root_dir) diff --git a/oslo_rootwrap/wrapper.py b/oslo_rootwrap/wrapper.py index b0ce0f5..bdde1f6 100644 --- a/oslo_rootwrap/wrapper.py +++ b/oslo_rootwrap/wrapper.py @@ -38,7 +38,7 @@ class FilterMatchNotExecutable(Exception): self.match = match -class RootwrapConfig(object): +class RootwrapConfig: def __init__(self, config): # filters_path @@ -106,7 +106,7 @@ def setup_syslog(execname, facility, level): try: handler = logging.handlers.SysLogHandler(address='/dev/log', facility=facility) - except IOError: + except OSError: logging.warning("Unable to setup syslog, maybe /dev/log socket needs " "to be restarted. Ignoring syslog configuration " "options.") @@ -218,7 +218,7 @@ def start_subprocess(filter_list, userargs, exec_dirs=[], log=False, **kwargs): command = filtermatch.get_command(userargs, exec_dirs) if log: - logging.info("(%s > %s) Executing %s (filter match = %s)" % ( + logging.info("({} > {}) Executing {} (filter match = {})".format( _getlogin(), pwd.getpwuid(os.getuid())[0], command, filtermatch.name)) diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index 3366c7a..7348921 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2020 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License");