diff --git a/docs/source/rcon.rst b/docs/source/rcon.rst index 0e23737..754d1fa 100644 --- a/docs/source/rcon.rst +++ b/docs/source/rcon.rst @@ -20,10 +20,18 @@ rcon.console module :undoc-members: :show-inheritance: -rcon.exceptions module ----------------------- +rcon.errorhandler module +------------------------ -.. automodule:: rcon.exceptions +.. automodule:: rcon.errorhandler + :members: + :undoc-members: + :show-inheritance: + +rcon.gui module +--------------- + +.. automodule:: rcon.gui :members: :undoc-members: :show-inheritance: @@ -52,6 +60,14 @@ rcon.rconshell module :undoc-members: :show-inheritance: +rcon.readline module +-------------------- + +.. automodule:: rcon.readline + :members: + :undoc-members: + :show-inheritance: + Module contents --------------- diff --git a/rcon/__init__.py b/rcon/__init__.py index cef7e96..43949cb 100644 --- a/rcon/__init__.py +++ b/rcon/__init__.py @@ -1,16 +1,7 @@ """RCON client library.""" from rcon.console import rconcmd -from rcon.exceptions import InvalidConfig -from rcon.exceptions import RequestIdMismatch -from rcon.exceptions import WrongPassword from rcon.proto import Client -__all__ = [ - 'InvalidConfig', - 'RequestIdMismatch', - 'WrongPassword', - 'Client', - 'rconcmd' -] +__all__ = ['Client', 'rconcmd'] diff --git a/rcon/config.py b/rcon/config.py index 2ef8eac..4e93b8b 100644 --- a/rcon/config.py +++ b/rcon/config.py @@ -7,8 +7,6 @@ from os import getenv, name from pathlib import Path from typing import Dict, Iterator, NamedTuple, Tuple -from rcon.exceptions import InvalidConfig - __all__ = ['CONFIG_FILE', 'LOG_FORMAT', 'Config', 'servers'] @@ -39,12 +37,9 @@ class Config(NamedTuple): try: host, port = string.split(':') except ValueError: - raise InvalidConfig(f'Invalid socket: {string}.') from None + raise ValueError(f'Invalid socket: {string}.') from None - try: - port = int(port) - except ValueError: - raise InvalidConfig(f'Not an integer: {port}.') from None + port = int(port) try: passwd, host = host.rsplit('@', maxsplit=1) diff --git a/rcon/console.py b/rcon/console.py index 77080b9..39a5487 100644 --- a/rcon/console.py +++ b/rcon/console.py @@ -3,7 +3,6 @@ from getpass import getpass from rcon.config import Config -from rcon.exceptions import RequestIdMismatch, WrongPassword from rcon.proto import Client @@ -83,8 +82,8 @@ def login(client: Client, passwd: str) -> str: while True: try: client.login(passwd) - except WrongPassword: - print('Invalid password.') + except RuntimeError as error: + print(error) passwd = read_passwd() continue @@ -113,7 +112,7 @@ def process_input(client: Client, passwd: str, prompt: str) -> bool: try: result = client.run(command, *args) - except RequestIdMismatch: + except RuntimeError: print(MSG_SESSION_TIMEOUT) try: diff --git a/rcon/errorhandler.py b/rcon/errorhandler.py index 09bb806..0acc04a 100644 --- a/rcon/errorhandler.py +++ b/rcon/errorhandler.py @@ -1,32 +1,35 @@ """Common errors handler.""" from logging import Logger +from socket import timeout from sys import exit # pylint: disable=W0622 -from typing import Iterable, Tuple __all__ = ['ErrorHandler'] -ErrorMap = Iterable[Tuple[Exception, str, int]] - - class ErrorHandler: """Handles common errors and exits.""" - __slots__ = ('errors', 'logger') + __slots__ = ('logger',) - def __init__(self, errors: ErrorMap, logger: Logger): + def __init__(self, logger: Logger): """Sets the logger.""" - self.errors = errors self.logger = logger def __enter__(self): return self - def __exit__(self, typ, value, _): + def __exit__(self, _, value, __): """Checks for connection errors and exits respectively.""" - for exception, message, returncode in self.errors: - if typ is exception or isinstance(value, exception): - self.logger.error(message) - exit(returncode) + if isinstance(value, ConnectionRefusedError): + self.logger.error('Connection refused.') + exit(3) + + if isinstance(value, (TimeoutError, timeout)): + self.logger.error('Connection timed out.') + exit(4) + + if isinstance(value, RuntimeError): + self.logger.error(str(value)) + exit(5) diff --git a/rcon/exceptions.py b/rcon/exceptions.py deleted file mode 100644 index 11c6f6a..0000000 --- a/rcon/exceptions.py +++ /dev/null @@ -1,22 +0,0 @@ -"""RCON exceptions.""" - - -__all__ = ['InvalidConfig', 'RequestIdMismatch', 'WrongPassword'] - - -class InvalidConfig(ValueError): - """Indicates invalid credentials.""" - - -class RequestIdMismatch(Exception): - """Indicates that the sent and received request IDs do not match.""" - - def __init__(self, sent: int, received: int): - """Sets the sent and received request IDs.""" - super().__init__(sent, received) - self.sent = sent - self.received = received - - -class WrongPassword(Exception): - """Indicates a wrong RCON password.""" diff --git a/rcon/gui.py b/rcon/gui.py index 3666aeb..0d2269e 100644 --- a/rcon/gui.py +++ b/rcon/gui.py @@ -14,7 +14,6 @@ require_version('Gtk', '3.0') from gi.repository import Gtk from rcon.config import LOG_FORMAT -from rcon.exceptions import RequestIdMismatch, WrongPassword from rcon.proto import Client @@ -203,15 +202,13 @@ class GUI(Gtk.Window): # pylint: disable=R0902 try: result = self.run_rcon() except ValueError as error: - self.show_error(' '.join(error.args)) + self.show_error(str(error)) except ConnectionRefusedError: self.show_error('Connection refused.') except (TimeoutError, timeout): self.show_error('Connection timed out.') - except RequestIdMismatch: - self.show_error('Request ID mismatch.') - except WrongPassword: - self.show_error('Invalid password.') + except RuntimeError as error: + self.show_error(str(error)) else: self.result_text = result diff --git a/rcon/proto.py b/rcon/proto.py index 53321bf..d92d820 100644 --- a/rcon/proto.py +++ b/rcon/proto.py @@ -7,9 +7,6 @@ from random import randint from socket import SOCK_STREAM, socket from typing import IO, NamedTuple, Optional -from rcon.exceptions import RequestIdMismatch -from rcon.exceptions import WrongPassword - __all__ = [ 'LittleEndianSignedInt32', @@ -172,36 +169,27 @@ class Client: file.write(bytes(packet)) with self._socket.makefile('rb') as file: - response = Packet.read(file) - - if response.id == packet.id: - return response - - raise RequestIdMismatch(packet.id, response.id) + return Packet.read(file) - def login(self, passwd: str) -> Packet: + def login(self, passwd: str) -> bool: """Performs a login.""" - packet = Packet.make_login(passwd) + response = self.communicate(Packet.make_login(passwd)) - try: - return self.communicate(packet) - except RequestIdMismatch as mismatch: - if mismatch.received == -1: - raise WrongPassword() from None + if response.id == -1: + raise RuntimeError('Wrong password.') - raise + return True def run(self, command: str, *arguments: str, raw: bool = False) -> str: """Runs a command.""" - packet = Packet.make_command(command, *arguments) + request = Packet.make_command(command, *arguments) + response = self.communicate(request) - try: - response = self.communicate(packet) - except RequestIdMismatch: - if self.passwd is not None: # Re-authenticate and retry command. - self.login(self.passwd) - return self.run(command, *arguments) + if response.id != request.id: + if self.passwd is not None: + if self.login(self.passwd): + return self.run(command, *arguments) - raise + raise RuntimeError('Request ID mismatch.') return response if raw else response.payload diff --git a/rcon/rconclt.py b/rcon/rconclt.py index e73331e..9df3b54 100644 --- a/rcon/rconclt.py +++ b/rcon/rconclt.py @@ -4,26 +4,17 @@ from argparse import ArgumentParser, Namespace from getpass import getpass from logging import DEBUG, INFO, basicConfig, getLogger from pathlib import Path -from socket import timeout from sys import exit # pylint: disable=W0622 from typing import Tuple from rcon.errorhandler import ErrorHandler -from rcon.exceptions import InvalidConfig from rcon.config import CONFIG_FILE, LOG_FORMAT, Config, servers -from rcon.exceptions import RequestIdMismatch, WrongPassword from rcon.proto import Client __all__ = ['get_credentials', 'main'] -ERRORS = ( - (ConnectionRefusedError, 'Connection refused.', 3), - ((TimeoutError, timeout), 'Connection timeout.', 4), - (RequestIdMismatch, 'Unexpected request ID mismatch.', 5), - (WrongPassword, 'Wrong password.', 6) -) LOGGER = getLogger('rconclt') @@ -49,7 +40,7 @@ def get_credentials(args: Namespace) -> Tuple[str, int, str]: try: host, port, passwd = Config.from_string(args.server) - except InvalidConfig: + except ValueError: try: host, port, passwd = servers(args.config)[args.server] except KeyError: @@ -74,7 +65,7 @@ def main() -> None: basicConfig(format=LOG_FORMAT, level=DEBUG if args.debug else INFO) host, port, passwd = get_credentials(args) - with ErrorHandler(ERRORS, LOGGER): + with ErrorHandler(LOGGER): with Client(host, port, timeout=args.timeout) as client: client.login(passwd) text = client.run(args.command, *args.argument) diff --git a/rcon/rconshell.py b/rcon/rconshell.py index 767d0a3..6af16eb 100644 --- a/rcon/rconshell.py +++ b/rcon/rconshell.py @@ -3,10 +3,8 @@ from argparse import ArgumentParser, Namespace from logging import INFO, basicConfig, getLogger from pathlib import Path -from socket import timeout from rcon.errorhandler import ErrorHandler -from rcon.exceptions import RequestIdMismatch from rcon.rconclt import get_credentials from rcon.readline import CommandHistory from rcon.config import CONFIG_FILE, LOG_FORMAT @@ -15,11 +13,7 @@ from rcon.console import PROMPT, rconcmd __all__ = ['get_args', 'main'] -ERRORS = ( - (ConnectionRefusedError, 'Connection refused.', 3), - ((TimeoutError, timeout), 'Connection timeout.', 4), - (RequestIdMismatch, 'Unexpected request ID mismatch.', 5) -) + LOGGER = getLogger('rconshell') @@ -46,6 +40,6 @@ def main() -> None: else: host = port = passwd = None - with ErrorHandler(ERRORS, LOGGER): + with ErrorHandler(LOGGER): with CommandHistory(LOGGER): rconcmd(host, port, passwd, prompt=args.prompt)