diff --git a/.flake8 b/.flake8 index 01e8f922..93d41a1a 100644 --- a/.flake8 +++ b/.flake8 @@ -4,12 +4,18 @@ max-line-length=120 ; Auto generated exclude=src/gen/, typings/cv2-stubs/__init__.pyi ignore= - W503, ; Linebreak before binary operator - E124, ; Closing bracket may not match multi-line method invocation style (enforced by add-trailing-comma) - E402, ; Allow imports at the bottom of file - Y026, ; Not using typing_extensions - SIM105, ; contextlib.suppress is roughly 3x slower than try/except - CCE001, ; False positives for attribute docstrings + ; Linebreak before binary operator + W503, + ; Closing bracket may not match multi-line method invocation style (enforced by add-trailing-comma) + E124, + ; Allow imports at the bottom of file + E402, + ; Not using typing_extensions + Y026, + ; contextlib.suppress is roughly 3x slower than try/except + SIM105, + ; False positives for attribute docstrings + CCE001, per-file-ignores= ; Quotes ; Allow ... on same line as class diff --git a/.github/workflows/lint-and-build.yml b/.github/workflows/lint-and-build.yml index ed3c1c5f..60316e46 100644 --- a/.github/workflows/lint-and-build.yml +++ b/.github/workflows/lint-and-build.yml @@ -2,6 +2,12 @@ name: Lint and build on: workflow_dispatch: # Allows manual builds + inputs: + excludeBuildNumber: + description: "Exclude build number" + required: true + default: false + type: boolean push: branches: - main @@ -26,23 +32,49 @@ on: env: GITHUB_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + GITHUB_EXCLUDE_BUILD_NUMBER: ${{ inputs.excludeBuildNumber }} jobs: - add-trailing-comma: + isort: runs-on: windows-latest steps: - name: Checkout ${{ github.repository }}/${{ github.ref }} uses: actions/checkout@v3 - - name: Set up Python 3.10 + - name: Set up Python 3.11 uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.11" cache: "pip" cache-dependency-path: "scripts/requirements*.txt" - run: scripts/install.ps1 shell: pwsh + - name: Analysing the code with isort + run: isort src/ typings/ --check-only + add-trailing-comma: + runs-on: windows-latest + steps: + - name: Checkout ${{ github.repository }}/${{ github.ref }} + uses: actions/checkout@v3 + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: "3.11" + - run: pip install add-trailing-comma - name: Analysing the code with add-trailing-comma run: add-trailing-comma $(git ls-files '**.py*') --py36-plus + Bandit: + # Bandit only matters on the version deployed. Platform checks are ignored + runs-on: windows-latest + steps: + - name: Checkout ${{ github.repository }}/${{ github.ref }} + uses: actions/checkout@v3 + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: "3.11" + - run: pip install bandit + - name: Analysing the code with Bandit + run: bandit src/ -n 1 --severity-level medium --recursive Pyright: runs-on: windows-latest strategy: @@ -106,22 +138,6 @@ jobs: shell: pwsh - name: Analysing the code with Flake8 run: flake8 src/ typings/ - Bandit: - # Bandit only matters on the version deployed. Platform checks are ignored - runs-on: windows-latest - steps: - - name: Checkout ${{ github.repository }}/${{ github.ref }} - uses: actions/checkout@v3 - - name: Set up Python 3.11 - uses: actions/setup-python@v4 - with: - python-version: "3.11" - cache: "pip" - cache-dependency-path: "scripts/requirements*.txt" - - run: scripts/install.ps1 - shell: pwsh - - name: Analysing the code with Bandit - run: bandit src/ -n 1 --severity-level medium --recursive Build: runs-on: windows-latest strategy: @@ -137,6 +153,7 @@ jobs: with: python-version: ${{ matrix.python-version }} cache: "pip" + cache-dependency-path: "scripts/requirements.txt" - run: scripts/install.ps1 shell: pwsh - run: scripts/build.ps1 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index d2d596de..a5cf6e6f 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -21,19 +21,18 @@ // Must disable in this workspace // // https://github.com/microsoft/vscode/issues/40239 // // - // We use autopep8 - "ms-python.black-formatter", // VSCode has implemented an optimized version "coenraads.bracket-pair-colorizer", "coenraads.bracket-pair-colorizer-2", // Obsoleted by Pylance "ms-pyright.pyright", - "ms-python.black-formatter", // Not configurable per workspace, tends to conflict with other linters "sonarsource.sonarlint-vscode", // // Don't recommend to autoinstall // // + // We use autopep8 + "ms-python.black-formatter", // This is a Git project "johnstoncode.svn-scm", // Prefer using VSCode itself as a text editor diff --git a/.vscode/settings.json b/.vscode/settings.json index d6d1feeb..807a07f0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,19 @@ 72 ] }, + "trailing-spaces.includeEmptyLines": true, + "trailing-spaces.trimOnSave": true, + "trailing-spaces.syntaxIgnore": [ + "markdown" + ], + "[markdown]": { + "files.trimTrailingWhitespace": false, + }, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "editor.comments.insertSpace": true, + "editor.insertSpaces": true, "editor.detectIndentation": false, "editor.tabSize": 2, "editor.formatOnSave": true, @@ -17,12 +30,6 @@ "source.fixAll.convertImportFormat": true, "source.organizeImports": true, }, - "files.insertFinalNewline": true, - "trailing-spaces.includeEmptyLines": true, - "trailing-spaces.trimOnSave": true, - "trailing-spaces.syntaxIgnore": [ - "markdown" - ], "emeraldwalk.runonsave": { "commands": [ { @@ -36,9 +43,7 @@ ] }, "files.associations": { - "*.json": "json", - "extensions.json": "jsonc", - "settings.json": "jsonc", + ".flake8": "properties", "*.qrc": "xml", "*.ui": "xml" }, @@ -50,11 +55,10 @@ "**/.DS_Store": true, "**/Thumbs.db": true, "build": true, - ".mypy_cache": true, + "**/.mypy_cache": true, "**/__pycache__": true, }, "search.exclude": { - "**/bower_components": true, "**/*.code-search": true, "*.lock": true, }, @@ -70,8 +74,12 @@ 120, // Our hard rule ], }, - "python.formatting.provider": "autopep8", + // Important to follow the config in pyrightconfig.json + "python.analysis.useLibraryCodeForTypes": false, "python.analysis.diagnosticMode": "workspace", + "python.formatting.provider": "autopep8", + "isort.check": true, + "isort.importStrategy": "fromEnvironment", "python.linting.enabled": true, // Use the new Pylint extension instead "python.linting.pylintEnabled": false, diff --git a/README.md b/README.md index 6f21235c..3c788ecb 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ Refer to the [build instructions](build%20instructions.md) if you'd like to buil #### Capture Device -Select the Video Capture Device that you wanna use if selecting the `Video Capture Device` Capture Method. Will show `[occupied]` if a device is detected but can't be started. +Select the Video Capture Device that you wanna use if selecting the `Video Capture Device` Capture Method. #### Show Live Similarity diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..27f9c438 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,21 @@ +; We don't run mypy in the CI. This is just to help anyone who would like to use it manually. +; Namely, the mypy_primer tool. +[mypy] +strict=true +; Implicit return types ! +disallow_untyped_calls=false +disallow_untyped_defs=false +disallow_incomplete_defs=false + +; Of course my stubs are going to be incomplete. Otherwise they'd be on typeshed! +; Mypy becomes really whack with its errors inside these stubs though +mypy_path=typings,src +; exclude doesn't work with strict=true Why? +exclude=.*(typings|gen)/.* + +[mypy-gen.*,cv2.*,] +; strict=false ; Doesn't work in overrides +follow_imports=skip +implicit_reexport=true +strict_optional=false +disable_error_code=attr-defined, misc, name-defined diff --git a/pyproject.toml b/pyproject.toml index 5b21dbf3..495b3a6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -105,13 +105,11 @@ max-branches = 15 # https://github.com/PyCQA/pep8-naming/issues/164 # OR doesn't fit CaptureMethodMeta valid-classmethod-first-arg = "self" -# https://pylint.pycqa.org/en/latest/user_guide/options.html#naming-styles -module-naming-style = "any" disable = [ # No need to mention the fixmes "fixme", "missing-docstring", - # Already taken care of by Flake8 and isort + # Already taken care of by isort "ungrouped-imports", "unused-import", "wrong-import-order", @@ -122,8 +120,6 @@ disable = [ "unused-argument", # Only reports a single instance. Pyright does a better job anyway "cyclic-import", - # Doesn't work with local imports - "import-error", # Similar lines in 2 files, doesn't really work "R0801", ] diff --git a/scripts/compile_resources.ps1 b/scripts/compile_resources.ps1 index a1a4ba2f..1b660e6d 100644 --- a/scripts/compile_resources.ps1 +++ b/scripts/compile_resources.ps1 @@ -10,11 +10,11 @@ pyside6-rcc './res/resources.qrc' -o './src/gen/resources_rc.py' Write-Host 'Generated code from .ui files' $build_vars_path = "$PSScriptRoot/../src/gen/build_vars.py" -$BUILD_NUMBER = Get-Date -Format yyMMddHHmm +$BUILD_NUMBER = If ($Env:GITHUB_EXCLUDE_BUILD_NUMBER -eq $true) { '' } Else { Get-Date -Format yyMMddHHmm } $GITHUB_REPOSITORY = $Env:GITHUB_HEAD_REPOSITORY If (-not $GITHUB_REPOSITORY) { $repo_url = git config --get remote.origin.url - $GITHUB_REPOSITORY = $repo_url.substring(19, $repo_url.length - 19 - 4) + $GITHUB_REPOSITORY = $repo_url.substring(19, $repo_url.length - 19) -replace '\.git', '' } If (-not $GITHUB_REPOSITORY) { $GITHUB_REPOSITORY = 'Toufool/Auto-Split' diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 669eeb4d..a7ed1534 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -6,11 +6,12 @@ If ($IsWindows) { } # Installing Python dependencies -$dev = If ($env:GITHUB_JOB -eq 'Build') { '' } Else { '-dev' } +$dev = If ($Env:GITHUB_JOB -eq 'Build') { '' } Else { '-dev' } # Ensures installation tools are up to date. This also aliases pip to pip3 on MacOS. python3 -m pip install wheel pip setuptools --upgrade pip install -r "$PSScriptRoot/requirements$dev.txt" --upgrade -if (Get-Command 'npm' -ErrorAction SilentlyContinue) { +# Don't install pyright on CI. We use an action +if (-not $Env:CI -and (Get-Command 'npm' -ErrorAction SilentlyContinue)) { npm i --global pyright@latest } diff --git a/scripts/lint.ps1 b/scripts/lint.ps1 index be1a0452..69ba1820 100644 --- a/scripts/lint.ps1 +++ b/scripts/lint.ps1 @@ -41,7 +41,6 @@ else { Write-Host "`nRunning Bandit..." bandit src/ -f custom --silent --recursive -# $exitCodes += $LastExitCode # Returns 1 on low if ($LastExitCode -gt 0) { Write-Host "`Bandit warning ($LastExitCode)" -ForegroundColor Yellow } @@ -49,7 +48,6 @@ else { Write-Host "`Bandit passed" -ForegroundColor Green } - if ($exitCodes -gt 0) { Write-Host "`nLinting failed ($exitCodes)" -ForegroundColor Red } diff --git a/scripts/requirements-dev.txt b/scripts/requirements-dev.txt index 28bd6fae..0b0f0d8c 100644 --- a/scripts/requirements-dev.txt +++ b/scripts/requirements-dev.txt @@ -8,18 +8,16 @@ # # Linters bandit -flake8>=5 # flake8-pyi deprecation warnings +flake8>=6 # Validates configuration flake8-builtins flake8-bugbear flake8-class-attributes-order -flake8-comprehensions>=3.8 # flake8 5 support +flake8-comprehensions>=3.8 # flake8 v5 support flake8-datetimez -flake8-isort>=4.2,<=5.0 # flake8 5 support ; Breaking issue (https://github.com/gforcada/flake8-isort/issues/128) -flake8-pyi>=22.10.0 # Fixes for negative numbers -flake8-quotes +flake8-pyi>=22.11.0 # flake8 v6 support flake8-simplify pep8-naming -pylint>=2.14,<3.0.0 # New checks # 3.0 still in pre-release +pylint>=2.14,<3.0.0 # New checks # 2.16 and 3.0 still in pre-release # Formatters add-trailing-comma>=2.3.0 # Added support for with statement autopep8>=2.0.0 # New checks @@ -32,6 +30,11 @@ qt6-applications # Types types-d3dshot types-keyboard +types-Pillow +types-psutil +types-PyAutoGUI types-pyinstaller types-pywin32 +types-requests +types-toml typing-extensions diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 14bd6b0e..7d11f5e6 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -12,20 +12,21 @@ # Dependencies: certifi ImageHash>=4.3.1 # Contains type information + setup as package not module -git+https://github.com/Avasam/keyboard.git@fix-563#egg=keyboard # Fix install on linux-ci https://github.com/boppreh/keyboard/pull/568 +git+https://github.com/boppreh/keyboard.git#egg=keyboard # Fix install on macos and linux-ci https://github.com/boppreh/keyboard/pull/568 numpy>=1.23.2 # Python 3.11 wheels opencv-python-headless>=4.6 # Breaking changes importing cv2.cv2 packaging Pillow>=9.2 # gnome-screeshot checks psutil PyAutoGUI ---extra-index-url https://www.riverbankcomputing.com/pypi/simple/ --pre -PyQt6>6.4.0 # Python 3.11 support +# 6.4.1 fixes the rare Illegal Operation issue from RTADan, but the dev wheels are currently broken :/ +# 2.0.0-beta.2 contains the fixes if anyone needs it. +PyQt6>=6.4.0 # Python 3.11 support requests toml # # Build and compile resources -PyInstaller>=5.5 # Python 3.11 support +pyinstaller>=5.5 # Python 3.11 support pyinstaller-hooks-contrib>=2022.9 # opencv-python 4.6 support. Changes for pywintypes and comtypes PySide6>=6.4.0.1 # Python 3.11 support # diff --git a/src/AutoSplitImage.py b/src/AutoSplitImage.py index 8988b81c..7adc0b24 100644 --- a/src/AutoSplitImage.py +++ b/src/AutoSplitImage.py @@ -2,6 +2,7 @@ import os from enum import Enum +from math import sqrt from typing import TYPE_CHECKING import cv2 @@ -19,8 +20,8 @@ COMPARISON_RESIZE_HEIGHT = 240 COMPARISON_RESIZE = (COMPARISON_RESIZE_WIDTH, COMPARISON_RESIZE_HEIGHT) COMPARISON_RESIZE_AREA = COMPARISON_RESIZE_WIDTH * COMPARISON_RESIZE_HEIGHT -MASK_LOWER_BOUND = np.array([1], dtype="uint8") -MASK_UPPER_BOUND = np.array([MAXBYTE], dtype="uint8") +MASK_LOWER_BOUND = np.array([0, 0, 0, 1], dtype="uint8") +MASK_UPPER_BOUND = np.array([MAXBYTE, MAXBYTE, MAXBYTE, MAXBYTE], dtype="uint8") START_KEYWORD = "start_auto_splitter" RESET_KEYWORD = "reset" @@ -115,8 +116,7 @@ def __read_image_bytes(self, path: str): # the number of nonzero elements in the alpha channel of the split image. # This may result in images bigger than COMPARISON_RESIZE if there's plenty of transparency. # Which wouldn't incur any performance loss in methods where masked regions are ignored. - alpha_channel = image[:, :, 3] - scale = min(1, (COMPARISON_RESIZE_AREA / cv2.countNonZero(alpha_channel)) ** 0.5) + scale = min(1, sqrt(COMPARISON_RESIZE_AREA / cv2.countNonZero(image[:, :, 3]))) image = cv2.resize( image, @@ -127,7 +127,7 @@ def __read_image_bytes(self, path: str): ) # Mask based on adaptively resized, nearest neighbor interpolated split image - self.mask = cv2.inRange(alpha_channel, MASK_LOWER_BOUND, MASK_UPPER_BOUND) + self.mask = cv2.inRange(image, MASK_LOWER_BOUND, MASK_UPPER_BOUND) else: image = cv2.resize(image, COMPARISON_RESIZE, interpolation=cv2.INTER_NEAREST) # Add Alpha channel if missing diff --git a/src/utils.py b/src/utils.py index e2596682..6214f9c0 100644 --- a/src/utils.py +++ b/src/utils.py @@ -81,7 +81,7 @@ def get_window_bounds(hwnd: int) -> tuple[int, int, int, int]: return window_left_bounds, window_top_bounds, window_width, window_height -def open_file(file_path: str): +def open_file(file_path: str | bytes | os.PathLike[str] | os.PathLike[bytes]): os.startfile(file_path) # nosec B606 @@ -173,7 +173,6 @@ def title(self): MAXBYTE = 255 # Shared strings -# Set AUTOSPLIT_BUILD_NUMBER to an empty string to generate a clean version number -# AUTOSPLIT_BUILD_NUMBER = "" # pyright: ignore[reportConstantRedefinition] # noqa: F811 +# Check `excludeBuildNumber` during workflow dispatch build generate a clean version number AUTOSPLIT_VERSION = "2.0.0-beta.1" + (f"-{AUTOSPLIT_BUILD_NUMBER}" if AUTOSPLIT_BUILD_NUMBER else "") GITHUB_REPOSITORY = AUTOSPLIT_GITHUB_REPOSITORY diff --git a/typings/cv2/gapi/streaming.pyi b/typings/cv2/gapi/streaming.pyi new file mode 100644 index 00000000..c330960f --- /dev/null +++ b/typings/cv2/gapi/streaming.pyi @@ -0,0 +1,15 @@ +from cv2.cv2 import GMat, GOpaqueT, gapi_streaming_queue_capacity + +SYNC_POLICY_DONT_SYNC: int +SYNC_POLICY_DROP: int +sync_policy_dont_sync: int +sync_policy_drop: int + +queue_capacity = gapi_streaming_queue_capacity + + +def desync(g: GMat) -> GMat: ... +def seqNo(arg1: GMat) -> GOpaqueT: ... +def seq_id(arg1: GMat) -> GOpaqueT: ... +def size(src: GMat) -> GOpaqueT: ... +def timestamp(arg1: GMat) -> GOpaqueT: ...