diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ab1b99b..c64db23d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Internal: * remove `psutil` library - #531 (@emcek) * use new API of `Pillow` Image object get_flattened_data() - #532 (@emcek) + * Use suppress from contextlib - #535 (@emcek) ## 3.7.1 * Update About dialog diff --git a/src/dcspy/migration.py b/src/dcspy/migration.py index c4e2be4e2..d3763784e 100644 --- a/src/dcspy/migration.py +++ b/src/dcspy/migration.py @@ -2,6 +2,7 @@ import re from collections.abc import Callable, Iterator +from contextlib import suppress from logging import getLogger from os import makedirs from pathlib import Path @@ -178,11 +179,9 @@ def _remove_key(cfg: DcspyConfigYaml, key: str) -> None: :param cfg: Configuration dictionary :param key: key name """ - try: + with suppress(KeyError): del cfg[key] LOG.debug(f'Remove key: {key}') - except KeyError: - pass def _rename_key_keep_value(cfg: DcspyConfigYaml, old_name: str, new_name: str, default_value: ConfigValue) -> None: @@ -195,10 +194,8 @@ def _rename_key_keep_value(cfg: DcspyConfigYaml, old_name: str, new_name: str, d :param default_value: Use if a value for an old key does not exist """ value = cfg.get(old_name, default_value) - try: + with suppress(KeyError): del cfg[old_name] - except KeyError: - pass cfg[new_name] = value LOG.debug(f'Rename key {old_name} -> {new_name} with: {value}') @@ -227,11 +224,9 @@ def _copy_file(filename: str, to_path: Path, force: bool = False) -> None: :param force: Force to overwrite an existing file """ if not Path(to_path / filename).is_file() or force: - try: + with suppress(SameFileError): copy(src=DEFAULT_YAML_FILE.parent / filename, dst=to_path) LOG.debug(f'Copy file: {filename} to {to_path}') - except SameFileError: - pass def replace_line_in_file(filename: str, dir_path: Path, pattern: re.Pattern, new_text: str) -> None: @@ -244,12 +239,10 @@ def replace_line_in_file(filename: str, dir_path: Path, pattern: re.Pattern, new :param new_text: The text to replace the line matching the pattern with. """ yaml_filename = dir_path / filename - try: + with suppress(FileNotFoundError): with open(yaml_filename) as yaml_file: file_content = yaml_file.read() LOG.debug(yaml_filename) updated_content = re.sub(pattern, new_text, file_content) with open(yaml_filename, 'w') as yaml_file: yaml_file.write(updated_content) - except FileNotFoundError: - pass diff --git a/src/dcspy/qt_gui.py b/src/dcspy/qt_gui.py index 613c88ae7..3044af76c 100644 --- a/src/dcspy/qt_gui.py +++ b/src/dcspy/qt_gui.py @@ -5,6 +5,7 @@ import traceback from argparse import Namespace from collections.abc import Callable +from contextlib import suppress from functools import partial from importlib import import_module from logging import DEBUG, INFO, Formatter, Handler, LogRecord, getLogger @@ -336,10 +337,8 @@ def _set_ded_font_and_font_sliders(self) -> None: for name in ['large', 'medium', 'small']: hs: QSlider = getattr(self, f'hs_{name}_font') - try: + with suppress(RuntimeError): hs.valueChanged.disconnect() - except RuntimeError: - pass hs.setMinimum(minimum) hs.setMaximum(maximum) hs.valueChanged.connect(partial(self._set_label_and_hs_value, name=name)) @@ -763,7 +762,7 @@ def _checked_iface_rb_for_identifier(self, key_name: str) -> None: :param key_name: G-Key, LCD or Mouse button as string """ - try: + with suppress(KeyError, AttributeError): widget_iface = self.input_reqs[self.current_plane][key_name].widget_iface if widget_iface == 'rb_custom': self.le_custom.setText(self.input_reqs[self.current_plane][key_name].request.split(f'{RequestType.CUSTOM.value} ')[1]) @@ -773,8 +772,6 @@ def _checked_iface_rb_for_identifier(self, key_name: str) -> None: self.le_custom.setText('') self.hs_set_state.setValue(0) getattr(self, widget_iface).setChecked(True) - except (KeyError, AttributeError): - pass def _hs_set_state_moved(self, value: int) -> None: """ diff --git a/src/dcspy/sdk/key_sdk.py b/src/dcspy/sdk/key_sdk.py index 307f5e61d..5f9f9cbe6 100644 --- a/src/dcspy/sdk/key_sdk.py +++ b/src/dcspy/sdk/key_sdk.py @@ -1,6 +1,7 @@ from __future__ import annotations from collections.abc import Callable +from contextlib import suppress from ctypes import CDLL, CFUNCTYPE, POINTER, Structure, c_bool, c_uint, c_void_p, c_wchar_p, pointer from logging import getLogger from typing import ClassVar @@ -70,20 +71,17 @@ def logi_gkey_init(self) -> bool: It must be called before your application can see G-key/button events. :return: If the function succeeds, it returns True. Otherwise, False. """ - try: + with suppress(AttributeError): self.key_dll.LogiGkeyInit.restype = c_bool self.key_dll.LogiGkeyInit.argtypes = [POINTER(LogiGkeyCBContext)] return self.key_dll.LogiGkeyInit(self.gkey_context_ptr) - except AttributeError: - return False + return False def logi_gkey_shutdown(self) -> None: """Unload the corresponding DLL and frees up any allocated resources.""" - try: + with suppress(AttributeError): self.key_dll.LogiGkeyShutdown() - except AttributeError: - pass def logi_gkey_is_keyboard_gkey_pressed(self, g_key: int, mode: int) -> bool: """ @@ -93,10 +91,9 @@ def logi_gkey_is_keyboard_gkey_pressed(self, g_key: int, mode: int) -> bool: :param mode: Number of the mode currently selected, example 1, 2 or 3 :return: True if the specified G-key for the specified Mode is currently being pressed, False otherwise """ - try: + with suppress(AttributeError): return self.key_dll.LogiGkeyIsKeyboardGkeyPressed(g_key, mode) - except AttributeError: - return False + return False def logi_gkey_is_keyboard_gkey_string(self, g_key: int, mode: int) -> str: """ @@ -106,10 +103,9 @@ def logi_gkey_is_keyboard_gkey_string(self, g_key: int, mode: int) -> str: :param mode: Number of the mode currently selected (1, 2 or 3) :return: Friendly string for specified G-key and Mode number, example 'G5/M1' """ - try: + with suppress(AttributeError): return self.key_dll.LogiGkeyGetKeyboardGkeyString(g_key, mode) - except AttributeError: - return '' + return '' def logi_gkey_is_mouse_pressed(self, button_number: int) -> bool: """ @@ -118,10 +114,9 @@ def logi_gkey_is_mouse_pressed(self, button_number: int) -> bool: :param button_number: Number of the buttons to check, example between 6 and 20 for G600 :return: True if the specified button is currently being pressed, False otherwise """ - try: + with suppress(AttributeError): return self.key_dll.LogiGkeyIsMousePressed(button_number) - except AttributeError: - return False + return False def logi_gkey_is_mouse_string(self, button_number: int) -> str: """ @@ -130,7 +125,6 @@ def logi_gkey_is_mouse_string(self, button_number: int) -> str: :param button_number: Number of the button to check, example between 6 and 20 for G600 :return: Friendly string for specified button number, example 'Mouse Btn 8' """ - try: + with suppress(AttributeError): return self.key_dll.LogiGkeyGetMouseString(button_number) - except AttributeError: - return '' + return '' diff --git a/src/dcspy/sdk/lcd_sdk.py b/src/dcspy/sdk/lcd_sdk.py index 5b0427404..874d43b80 100644 --- a/src/dcspy/sdk/lcd_sdk.py +++ b/src/dcspy/sdk/lcd_sdk.py @@ -1,3 +1,4 @@ +from contextlib import suppress from logging import getLogger from _cffi_backend import Lib @@ -36,10 +37,9 @@ def logi_lcd_init(self, name: str, lcd_type: LcdType) -> bool: :param lcd_type: LCD type :return: A result of execution """ - try: + with suppress(AttributeError): return self.lcd_dll.LogiLcdInit(FFI().new('wchar_t[]', name), lcd_type.value) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_lcd_is_connected(self, lcd_type: LcdType) -> bool: """ @@ -48,10 +48,9 @@ def logi_lcd_is_connected(self, lcd_type: LcdType) -> bool: :param lcd_type: LCD type :return: A result of execution """ - try: + with suppress(AttributeError): return self.lcd_dll.LogiLcdIsConnected(lcd_type.value) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_lcd_is_button_pressed(self, button: LcdButton) -> bool: """ @@ -60,24 +59,19 @@ def logi_lcd_is_button_pressed(self, button: LcdButton) -> bool: :param button: Defines the button to check on :return: True if a button is being pressed, False otherwise """ - try: + with suppress(AttributeError): return self.lcd_dll.LogiLcdIsButtonPressed(button.value) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_lcd_update(self) -> None: """Update the LCD.""" - try: + with suppress(AttributeError): self.lcd_dll.LogiLcdUpdate() # type: ignore[attr-defined] - except AttributeError: - pass def logi_lcd_shutdown(self) -> None: """Kill the applet and frees memory used by the SDK.""" - try: + with suppress(AttributeError): self.lcd_dll.LogiLcdShutdown() # type: ignore[attr-defined] - except AttributeError: - pass def logi_lcd_mono_set_background(self, pixels: list[int]) -> bool: """ @@ -92,10 +86,9 @@ def logi_lcd_mono_set_background(self, pixels: list[int]) -> bool: :param pixels: List of 6880 (160x43) pixels as integer :return: A result of execution """ - try: + with suppress(AttributeError, CDefError): # we need catch error since BYTE[] is a Windows specific return self.lcd_dll.LogiLcdMonoSetBackground(FFI().new('BYTE[]', pixels)) # type: ignore[attr-defined] - except (AttributeError, CDefError): # we need catch error since BYTE[] is a Windows specific - return False + return False def logi_lcd_mono_set_text(self, line_no: int, text: str) -> bool: """ @@ -105,10 +98,9 @@ def logi_lcd_mono_set_text(self, line_no: int, text: str) -> bool: :param text: The text to display :return: A result of execution """ - try: + with suppress(AttributeError): return self.lcd_dll.LogiLcdMonoSetText(line_no, FFI().new('wchar_t[]', text)) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_lcd_color_set_background(self, pixels: list[tuple[int, int, int, int]]) -> bool: """ @@ -121,10 +113,9 @@ def logi_lcd_color_set_background(self, pixels: list[tuple[int, int, int, int]]) :return: A result of execution """ img_bytes = [byte for pixel in pixels for byte in pixel] - try: + with suppress(AttributeError, CDefError): # we need catch error since BYTE[] is a Windows specific return self.lcd_dll.LogiLcdColorSetBackground(FFI().new('BYTE[]', img_bytes)) # type: ignore[attr-defined] - except (AttributeError, CDefError): # we need catch error since BYTE[] is Windows specific - return False + return False def logi_lcd_color_set_title(self, text: str, rgb: tuple[int, int, int] = (255, 255, 255)) -> bool: """ @@ -137,10 +128,9 @@ def logi_lcd_color_set_title(self, text: str, rgb: tuple[int, int, int] = (255, :param rgb: a tuple with integer values between 0 and 255 as red, green, blue :return: A result of execution """ - try: + with suppress(AttributeError): return self.lcd_dll.LogiLcdColorSetTitle(FFI().new('wchar_t[]', text), *rgb) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_lcd_color_set_text(self, line_no: int, text: str, rgb: tuple[int, int, int] = (255, 255, 255)) -> bool: """ @@ -152,10 +142,9 @@ def logi_lcd_color_set_text(self, line_no: int, text: str, rgb: tuple[int, int, :param rgb: tuple with integer values between 0 and 255 (interpreted as red, green or blue) :return: A result of execution """ - try: + with suppress(AttributeError): return self.lcd_dll.LogiLcdColorSetText(line_no, FFI().new('wchar_t[]', text), *rgb) # type: ignore[attr-defined] - except AttributeError: - return False + return False def update_text(self, txt: list[tuple[str, Color]]) -> None: """ diff --git a/src/dcspy/sdk/led_sdk.py b/src/dcspy/sdk/led_sdk.py index 4305c47b8..e5660127a 100644 --- a/src/dcspy/sdk/led_sdk.py +++ b/src/dcspy/sdk/led_sdk.py @@ -1,3 +1,4 @@ +from contextlib import suppress from logging import getLogger from threading import Event from time import sleep @@ -20,10 +21,9 @@ def logi_led_init() -> bool: currently going on the connected devices. :return: A result of execution """ - try: + with suppress(AttributeError): return LED_DLL.LogiLedInit() # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_led_init_with_name(name: str) -> bool: @@ -37,10 +37,9 @@ def logi_led_init_with_name(name: str) -> bool: :param name: The referred name for this integration to show u as :return: A result of execution """ - try: + with suppress(AttributeError): return LED_DLL.LogiLedInitWithName(FFI().new('wchar_t[]', name)) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_led_set_target_device(target_device: LedConstants) -> bool: @@ -53,10 +52,9 @@ def logi_led_set_target_device(target_device: LedConstants) -> bool: LOGI_DEVICETYPE_MONOCHROME, LOGI_DEVICETYPE_RGB, LOGI_DEVICETYPE_ALL :return: A result of execution """ - try: + with suppress(AttributeError): return LED_DLL.LogiLedSetTargetDevice(target_device.value) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_led_save_current_lighting() -> bool: @@ -67,10 +65,9 @@ def logi_led_save_current_lighting() -> bool: function just before starting the warning effect. :return: A result of execution """ - try: + with suppress(AttributeError): return LED_DLL.LogiLedSaveCurrentLighting() # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_led_restore_lighting() -> bool: @@ -82,10 +79,9 @@ def logi_led_restore_lighting() -> bool: after the warning effect is finished. :return: A result of execution """ - try: + with suppress(AttributeError): return LED_DLL.LogiLedRestoreLighting() # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_led_set_lighting(rgb: tuple[int, int, int]) -> bool: @@ -100,10 +96,9 @@ def logi_led_set_lighting(rgb: tuple[int, int, int]) -> bool: :param rgb: Tuple with integer range 0 to 100 as an amount of red, green, blue :return: A result of execution """ - try: + with suppress(AttributeError): return LED_DLL.LogiLedSetLighting(*rgb) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_led_flash_lighting(rgb: tuple[int, int, int], duration: int, interval: int) -> bool: @@ -117,10 +112,9 @@ def logi_led_flash_lighting(rgb: tuple[int, int, int], duration: int, interval: :param interval: Duration of the flashing interval in millisecond :return: A result of execution """ - try: + with suppress(AttributeError): return LED_DLL.LogiLedFlashLighting(*rgb, duration, interval) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_led_pulse_lighting(rgb: tuple[int, int, int], duration: int, interval: int) -> bool: @@ -134,10 +128,9 @@ def logi_led_pulse_lighting(rgb: tuple[int, int, int], duration: int, interval: :param interval: Duration of the flashing interval in millisecond. :return: A result of execution. """ - try: + with suppress(AttributeError): return LED_DLL.LogiLedPulseLighting(*rgb, duration, interval) # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_led_stop_effects() -> bool: @@ -147,18 +140,15 @@ def logi_led_stop_effects() -> bool: Started from logi_led_flash_lighting() or logi_led_pulse_lighting(). :return: A result of execution """ - try: + with suppress(AttributeError): return LED_DLL.LogiLedStopEffects() # type: ignore[attr-defined] - except AttributeError: - return False + return False def logi_led_shutdown() -> None: """Restore the last saved lighting and frees memory used by the SDK.""" - try: + with suppress(AttributeError): LED_DLL.LogiLedShutdown() # type: ignore[attr-defined] - except AttributeError: - pass def start_led_pulse(rgb: tuple[int, int, int], duration: int, interval: int, event: Event) -> None: diff --git a/src/dcspy/utils.py b/src/dcspy/utils.py index 2bc540c43..6a6ded9c5 100644 --- a/src/dcspy/utils.py +++ b/src/dcspy/utils.py @@ -5,6 +5,7 @@ import sys import zipfile from collections.abc import Callable, Generator, Sequence +from contextlib import suppress from datetime import datetime from functools import lru_cache from glob import glob @@ -27,10 +28,8 @@ from dcspy.models import (CONFIG_YAML, CTRL_LIST_SEPARATOR, DEFAULT_YAML_FILE, AnyButton, BiosValue, Color, ControlDepiction, ControlKeyData, DcsBiosPlaneData, DcspyConfigYaml, Gkey, LcdButton, LcdMode, MouseButton, Release, RequestModel, __version__) -try: +with suppress(ImportError): import git -except ImportError: - pass LOG = getLogger(__name__) @@ -309,11 +308,10 @@ def is_git_sha(repo: git.Repo, ref: str) -> bool: """ import gitdb # type: ignore[import-untyped] - try: + with suppress(gitdb.exc.BadName): _ = repo.commit(ref).hexsha return True - except gitdb.exc.BadName: - return False + return False def check_dcs_bios_entry(lua_dst_data: str, lua_dst_path: Path, temp_dir: Path) -> str: @@ -385,11 +383,9 @@ def is_git_object(repo_dir: Path, git_obj: str) -> bool: all_refs = get_all_git_refs(repo_dir=repo_dir) if git_obj in all_refs: result = True - try: + with suppress(gitdb.exc.BadName, TypeError, ValueError): git.Repo(repo_dir).commit(git_obj) result = True - except (gitdb.exc.BadName, TypeError, ValueError): - pass return result diff --git a/tests/helpers.py b/tests/helpers.py index 8b03f3557..43c9977d3 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,4 +1,5 @@ from collections.abc import Sequence +from contextlib import suppress from pathlib import Path from socket import AF_INET, SOCK_DGRAM, socket from time import sleep @@ -61,10 +62,8 @@ def assert_bytes(test_bytes: bytes, ref_bytes: bytes) -> tuple[float, int]: :return: Tuple with float of percentage and difference in size """ percents = [] - try: + with suppress(IndexError): percents = [1 for i, b in enumerate(ref_bytes) if b != test_bytes[i]] - except IndexError: - pass return float(f'{sum(percents) / len(ref_bytes) * 100:.2f}'), len(ref_bytes) - len(test_bytes)