From 283cf442be0137efa4220d61aa4049631d155948 Mon Sep 17 00:00:00 2001 From: Rob <5183487+Rexeh@users.noreply.github.com> Date: Sat, 25 Nov 2023 18:36:32 +0000 Subject: [PATCH] Integrated mypy and further clean up of build scripts --- .gitignore | 1 + build.py | 80 ------------------------- joystick_diagrams/adaptors/dcs_world.py | 58 +++++++++--------- joystick_diagrams/classes/input.py | 26 +++++--- joystick_diagrams/exceptions.py | 12 ++-- joystick_diagrams/manager.py | 8 +-- joystick_diagrams/version.py | 3 +- makefile | 7 ++- pyproject.toml | 26 ++++++-- setup.py | 41 +++++++++++++ tests/ui/test_main_ui.py | 1 - 11 files changed, 124 insertions(+), 139 deletions(-) delete mode 100644 build.py create mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 25714fb..76a9ff8 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ temp/ /notebooks /logs/* /src/joystick_diagrams/logs* +*.egg-info/ # IDE *.code-workspace diff --git a/build.py b/build.py deleted file mode 100644 index 9c78207..0000000 --- a/build.py +++ /dev/null @@ -1,80 +0,0 @@ -import pathlib -import sys -from setuptools import find_packages -from cx_Freeze import setup, Executable -import joystick_diagrams.version as ver - -VER = ver.get_current_version() -HERE = pathlib.Path(__file__).parent.resolve() -BASE = None -TARGET_NAME: str = "joystick_diagrams" -LONG_DESC = (HERE / "readme.md").read_text(encoding="utf-8") - -try: - from cx_Freeze.hooks import get_qt_plugins_paths -except ImportError: - include_files = [] -else: - # Inclusion of extra plugins (new in cx_Freeze 6.8b2) - # cx_Freeze imports automatically the following plugins depending of the - # use of some modules: - # imageformats - QtGui - # platforms - QtGui - # mediaservice - QtMultimedia - # printsupport - QtPrintSupport - # - # So, "platforms" is used here for demonstration purposes. - include_files = get_qt_plugins_paths("PyQt5", "QtGui") - -if sys.platform == "win32": - BASE = "Win32GUI" - TARGET_NAME = "joystick_diagrams.exe" - -extra_includes = ["./images", "./templates", "./config.cfg", "./readme.md"] -include_files.extend(extra_includes) - -build_options = { - "include_files": include_files, - "excludes": ["tkinter", "test", "distutils", "ssl", "asyncio", "concurrent", "pyqt5"], - "optimize": 2, -} - -setup( - name="Joystick Diagrams", - version=VER, - description="Automatically create diagrams for your throttles, joysticks and custom HID devices", - long_description=LONG_DESC, - long_description_content_type="text/markdown", - url="https://github.com/Rexeh/joystick-diagrams", - author="Robert Cox", - keywords="joystick, HID, diagrams, joystick gremlin, dcs world", - packages=find_packages(), - python_requires=">=3.11, <4", - install_requires=[ - "cx-freeze", - "pyqt5", - "ply", - "pytest", - "pytest-qt", - "pytest-cov", - "black", - "pre-commit", - "coveralls", - ], - project_urls={ - "Documentation": "https://joystick-diagrams.com/", - "Bug Reports": "https://github.com/Rexeh/joystick-diagrams/issues", - "Funding": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WLLDYGQM5Z39W&source=url", - "Source": "https://github.com/Rexeh/joystick-diagrams/src", - }, - options={"build_exe": build_options}, - executables=[ - Executable( - "src/joystick_diagrams/main.py", - base=BASE, - target_name=TARGET_NAME, - icon=pathlib.Path("images", "logo.ico"), - copyright="Robert Cox - joystick-diagrams.com", - ) - ], -) diff --git a/joystick_diagrams/adaptors/dcs_world.py b/joystick_diagrams/adaptors/dcs_world.py index 78aeb17..d707a64 100644 --- a/joystick_diagrams/adaptors/dcs_world.py +++ b/joystick_diagrams/adaptors/dcs_world.py @@ -1,11 +1,9 @@ """DCS World Lua Config Parser for use with Joystick Diagrams""" -from ast import Str import os import re from pathlib import Path import logging -from ply import lex, yacc - +from ply import lex, yacc # type: ignore import joystick_diagrams.adaptors.dcs_world_lex # pylint: disable=unused-import import joystick_diagrams.adaptors.dcs_world_yacc # pylint: disable=unused-import import joystick_diagrams.adaptors.joystick_diagram_interface as jdi @@ -38,14 +36,14 @@ def __validate_base_directory(self) -> list: else: raise FileNotFoundError("DCS: No Config Folder found in DCS Folder.") - def __validate_profiles(self) -> list: + def __validate_profiles(self) -> list[str]: """ Validate Profiles Routine """ if len(self.base_directory) == 0: raise FileExistsError("DCS: No profiles exist in Input directory!") - valid_items = list() + valid_items = [] for item in self.base_directory: valid = self.__validate_profile(item) if valid: @@ -55,7 +53,7 @@ def __validate_profiles(self) -> list: return valid_items - def __validate_profile(self, item): + def __validate_profile(self, item: str) -> list | bool: """ Validate Inidividual Profile Return Valid Profile @@ -68,7 +66,7 @@ def __validate_profile(self, item): return False - def get_validated_profiles(self): + def get_validated_profiles(self) -> list[str]: """Expose Valid Profiles only to UI""" if self.remove_easy_modes: return list( @@ -85,20 +83,21 @@ def convert_button_format(self, button) -> str: match len(split): case 2: - if len(split) == 2: - if split[1][0:3] == "BTN": - return split[1].replace("BTN", "BUTTON_") - elif split[1].isalpha(): - return f"AXIS_{split[1]}" - elif split[1][0:6] == "SLIDER": - return f"AXIS_SLIDER_{split[1][6:]}" - else: - return split[1] - ## Add default case / better handling + if split[1][0:3] == "BTN": + return f"{split[1].replace('BTN', 'BUTTON_')}" + elif split[1].isalpha(): + return f"AXIS_{split[1]}" + elif split[1][0:6] == "SLIDER": + return f"AXIS_SLIDER_{split[1][6:]}" + else: + return f"{split[1]}" case 4: return f"{split[1].replace('BTN', 'POV')}_{split[2][3]}_{split[3]}" + case _: + _logger.warning(f"Button format not found for {split}") + return f"{button}" - def process_profiles(self, profile_list: list = None) -> dict: + def process_profiles(self, profile_list: list | None = None) -> dict: if isinstance(profile_list, list) and len(profile_list) > 0: self.profiles_to_process = profile_list else: @@ -119,6 +118,7 @@ def process_profiles(self, profile_list: list = None) -> dict: _logger.info("Skipping as Folder") else: try: + _logger.debug(f"Obtaining file data for {joystick_file}") file_data = ( Path(os.path.join(self.fq_path, joystick_file)) .read_text(encoding="utf-8") @@ -126,12 +126,12 @@ def process_profiles(self, profile_list: list = None) -> dict: .replace("return diff", "") ) - except FileNotFoundError: - raise FileExistsError( - "DCS: File {} no longer found - It has been moved/deleted from directory".format( - joystick_file - ) + except FileNotFoundError as err: + _logger.error( + f"DCS: File {joystick_file} no longer found - It has been moved/deleted from directory. {err}" ) + raise + else: parsed_config = self.parse_config(file_data) ##Better handling - decompose @@ -143,10 +143,11 @@ def process_profiles(self, profile_list: list = None) -> dict: self.update_joystick_dictionary(joystick_device, profile, False, button_map) return self.joystick_dictionary - def parse_config(self, file: Str): + def parse_config(self, file: str): try: return self.parse_file(file) except Exception as error: + _logger.error("There was a parsing issue with the text data, this could mean an unhandled character.") _logger.error(error) return None @@ -179,7 +180,7 @@ def create_joystick_map(self, data) -> dict: write_val = False return button_array - def parse_file(self, file: Str) -> dict: + def parse_file(self, file: str) -> dict: # pylint: disable=unused-variable tokens = ( "LCURLY", @@ -271,14 +272,11 @@ def p_error(t): # pylint: disable=invalid-name # Build the lexer lexer = lex.lex( - debug=False, - optimize=1, - lextab="dcs_world_lex", - reflags=re.UNICODE | re.VERBOSE, + debug=False, optimize=1, lextab="dcs_world_lex", reflags=re.UNICODE | re.VERBOSE, errorlog=_logger ) # Build the parser - parser = yacc.yacc(debug=False, optimize=1, tabmodule="dcs_world_yacc") + parser = yacc.yacc(debug=False, optimize=1, tabmodule="dcs_world_yacc", errorlog=_logger) # Parse the data try: diff --git a/joystick_diagrams/classes/input.py b/joystick_diagrams/classes/input.py index 8a9f816..8713a60 100644 --- a/joystick_diagrams/classes/input.py +++ b/joystick_diagrams/classes/input.py @@ -13,9 +13,6 @@ import logging _logger = logging.getLogger(__name__) -_devices = {} - -print(__name__) @dataclass @@ -39,12 +36,16 @@ def validate(identifier): class Input: - def __init__(self, identifier: str, style: InputTypes, command: Command, modifiers: list[Modifier] = None) -> None: + # Rework input + # Better naming of the entities + # Change input to non conflciting class + + def __init__(self, identifier: str, style: InputTypes, command: Command, modifiers: list[Modifier] = []) -> None: style.validate(identifier) self.identifier = identifier self.style = style self.command = command - self.modifiers = modifiers = [] if modifiers is None else modifiers + self.modifiers = modifiers def add_modifier(self, modifier: set, command: Command): """ @@ -75,7 +76,7 @@ class LogicalDevice: def __init__(self, guid: str, name: str): self.guid = guid.strip().lower() self.name = name.strip().lower() - self.inputs = [] + self.inputs: list[Input] = [] def create_input(self, input_id: str, style: InputTypes, command: Command) -> None: existing = self.__check_existing_input(input_id, style) @@ -94,7 +95,7 @@ def _get_input(self, input_id: str) -> Input | None: return x return None - def get_device_inputs(self) -> dict: + def get_device_inputs(self) -> list[Input]: return self.inputs def add_modifier_to_input(self, input_id, modifier: set, command: Command) -> None: @@ -109,7 +110,8 @@ def add_modifier_to_input(self, input_id, modifier: set, command: Command) -> No self.create_input(input_id, InputTypes.BUTTON, "Not Used") obj = self._get_input(input_id) _logger.debug(f"Modifier input created now: {obj}") - obj.add_modifier(modifier, command) + else: + obj.add_modifier(modifier, command) def __check_existing_input(self, input_id: str, style: InputTypes) -> Input | None: """ @@ -149,8 +151,14 @@ def add_device(guid: str, name: str) -> LogicalDevice: def add_input_modifier(guid: str, input_id: str, modifier: set, command: Command) -> None: - _get_device(guid).add_modifier_to_input(input_id, modifier, command) + try: + _get_device(guid).add_modifier_to_input(input_id, modifier, command) + except: + pass def add_inputs(guid: str, **kwargs) -> None: _devices[guid].create_input(**kwargs) + + +_devices = {} diff --git a/joystick_diagrams/exceptions.py b/joystick_diagrams/exceptions.py index 55cefe2..f5708ba 100644 --- a/joystick_diagrams/exceptions.py +++ b/joystick_diagrams/exceptions.py @@ -11,12 +11,12 @@ def __str__(self): # ----------------------------------------------------------------------------- -class directory_not_valid(JoystickDiagramsException): - def __init__(self, value): +class DirectoryNotValid(JoystickDiagramsException): + def __init__(self, value=""): super().__init__(value) -class file_not_valid(JoystickDiagramsException): +class FileNotValid(JoystickDiagramsException): def __init__(self, value=""): super().__init__(value) @@ -24,7 +24,7 @@ def __str__(self): return repr("File was invalid: " + self.value) -class file_type_invalid(JoystickDiagramsException): +class FileTypeInvalid(JoystickDiagramsException): def __init__(self, value="Default"): super().__init__(value) @@ -34,7 +34,7 @@ def __init__(self, value="Default"): # ----------------------------------------------------------------------------- -class plugin_not_valid(JoystickDiagramsException): +class PluginNotValid(JoystickDiagramsException): def __init__(self, value="", error=""): super().__init__(value) self.error = error @@ -43,7 +43,7 @@ def __str__(self): return repr(f"Plugin {self.value} loaded was invalid: {self.error}") -class no_plugins_exist(JoystickDiagramsException): +class NoPluginsExist(JoystickDiagramsException): def __init__(self, value=""): super().__init__(value) diff --git a/joystick_diagrams/manager.py b/joystick_diagrams/manager.py index e5a19d1..3cbcb3a 100644 --- a/joystick_diagrams/manager.py +++ b/joystick_diagrams/manager.py @@ -28,10 +28,10 @@ def __init__(self): try: # Try initiate the plugin self._plugins.append(self.load_plugin(plugin)) - except exp.plugin_not_valid as e: + except exp.PluginNotValid as e: _logger.error(e) else: - raise exp.no_plugins_exist() + raise exp.NoPluginsExist() def load_plugin(self, module_path: str) -> ModuleType: """Attempt to load the plugin""" @@ -39,9 +39,9 @@ def load_plugin(self, module_path: str) -> ModuleType: # Todo make nicer the joining of relative pathing... return import_module("." + module_path + ".main", "joystick_diagrams.plugins").CustomPlugin() except TypeError as e: - raise exp.plugin_not_valid(error=e) from e + raise exp.PluginNotValid(error=e) from e except ModuleNotFoundError as e: - raise exp.plugin_not_valid(value=module_path, error=e) from e + raise exp.PluginNotValid(value=module_path, error=e) from e def find_plugins(self, directory="plugins") -> list: """ diff --git a/joystick_diagrams/version.py b/joystick_diagrams/version.py index b843edb..0a595d4 100644 --- a/joystick_diagrams/version.py +++ b/joystick_diagrams/version.py @@ -3,10 +3,9 @@ Author: Robert Cox """ -import requests from dataclasses import dataclass -VERSION = "1.4.1" +VERSION = "1.5" VERSION_SERVER = "http://localhost:5000/api/v1/version/" diff --git a/makefile b/makefile index df6971d..fbb1f03 100644 --- a/makefile +++ b/makefile @@ -12,7 +12,10 @@ fmt: lint: @echo "Linting source code" - @poetry run pylint --rcfile pyproject.toml src/ tests/ + @poetry run pylint --rcfile pyproject.toml joystick_diagrams/ tests/ build-exe: - @poetry run python build.py build \ No newline at end of file + @echo "Making standard portable package" + @poetry run setup.py build + @echo "Making MSI Build" + @poetry run setup.py build bdist_msi diff --git a/pyproject.toml b/pyproject.toml index 854ce5f..a11dc1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [tool.poetry] name = "joystick_diagrams" -version = "1.5" description = "" authors = ["Robert Cox"] +version = "1.5" [tool.poetry.dependencies] python = "^3.11" @@ -19,14 +19,26 @@ pre-commit = "^2.15.0" coveralls = "^3.3.0" pylint = "^3.0.0" cx-Freeze = "^6.15.0" - -[tool.poetry.scripts] -build-exe = "build:setup" +mypy = "^1.7.1" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" +[project] +name = "Joystick_Diagrams" +description = "Automatically create diagrams for your throttles, joysticks and custom HID devices" +dynamic = ["version", "readme"] + +[tool.setuptools.dynamic] +version = {attr = "joystick_diagrams.version.VERSION"} +readme = {file = "readme.md"} + +[tool.distutils.build_exe] +include_files = ["./images", "./templates", "./config.cfg", "./readme.md"] +excludes = ["tkinter", "test", "distutils", "ssl", "asyncio", "concurrent", "pyqt5"] +optimize = 2 + [tool.coverage.paths] source = ["joystick_diagrams/", "*/site-packages/"] @@ -79,4 +91,8 @@ variable-rgx = "[a-z_][a-z0-9_]{0,30}$" extension-pkg-whitelist = ["PyQt5"] # ignoring missing module and missing class docstring errors disable = ["C0114", "C0115","W1201","W1202","logging-fstring-interpolation","missing-function-docstring"] -output-format = "colorized" \ No newline at end of file +output-format = "colorized" + +[tool.mypy] +warn_return_any = "False" +warn_unused_configs = "True" \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9affea2 --- /dev/null +++ b/setup.py @@ -0,0 +1,41 @@ +import pathlib +import sys +from setuptools import find_packages +from cx_Freeze import setup, Executable + + +HERE = pathlib.Path(__file__).parent.resolve() +TARGET_NAME: str = "joystick_diagrams" +LONG_DESC = (HERE / "readme.md").read_text(encoding="utf-8") +BASE = "Win32GUI" if sys.platform == "win32" else None +TARGET_NAME = "joystick_diagrams.exe" + + +def main(): + setup( + url="https://github.com/Rexeh/joystick-diagrams", + author="Robert Cox", + keywords="joystick, HID, diagrams, joystick gremlin, dcs world", + packages=find_packages(), + python_requires=">=3.11, <4", + install_requires=["cx-freeze", "pyqt5", "ply"], + project_urls={ + "Documentation": "https://joystick-diagrams.com/", + "Bug Reports": "https://github.com/Rexeh/joystick-diagrams/issues", + "Funding": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WLLDYGQM5Z39W&source=url", + "Source": "https://github.com/Rexeh/joystick-diagrams/src", + }, + executables=[ + Executable( + "joystick_diagrams/__main__.py", + base=BASE, + target_name=TARGET_NAME, + icon=pathlib.Path("images", "logo.ico"), + copyright="Robert Cox - joystick-diagrams.com", + ) + ], + ) + + +if __name__ == "__main__": + main() diff --git a/tests/ui/test_main_ui.py b/tests/ui/test_main_ui.py index 49f3277..689217a 100644 --- a/tests/ui/test_main_ui.py +++ b/tests/ui/test_main_ui.py @@ -1,5 +1,4 @@ from PyQt5 import QtCore -import pytest import joystick_diagrams.__main__ as ui import joystick_diagrams.version as version