From cac358197a7a3c10b8138b6c41cec5855c59f089 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 7 Sep 2022 21:01:24 -0400 Subject: [PATCH 1/5] Updated configs --- .github/workflows/lint-and-build.yml | 8 ++++---- .sonarcloud.properties | 1 + .vscode/extensions.json | 5 +++-- .vscode/launch.json | 9 +++++++++ .vscode/settings.json | 3 +++ pyproject.toml | 19 +++++++++++++++++-- scripts/lint.ps1 | 12 ++++++++---- scripts/requirements-dev.txt | 6 +++++- scripts/requirements.txt | 6 +++--- 9 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 .sonarcloud.properties diff --git a/.github/workflows/lint-and-build.yml b/.github/workflows/lint-and-build.yml index 62d39ad2..0fe853ba 100644 --- a/.github/workflows/lint-and-build.yml +++ b/.github/workflows/lint-and-build.yml @@ -45,7 +45,7 @@ jobs: - run: scripts/install.ps1 shell: pwsh - name: Analysing the code with Pyright - run: pyright --warnings + run: pyright src/ --warnings Pylint: runs-on: windows-latest strategy: @@ -64,7 +64,7 @@ jobs: - run: scripts/install.ps1 shell: pwsh - name: Analysing the code with Pylint - run: pylint --reports=y --output-format=colorized src/ + run: pylint src/ --reports=y --output-format=colorized Flake8: runs-on: windows-latest strategy: @@ -83,7 +83,7 @@ jobs: - run: scripts/install.ps1 shell: pwsh - name: Analysing the code with Flake8 - run: flake8 + run: flake8 src/ typings/ Bandit: runs-on: windows-latest steps: @@ -98,7 +98,7 @@ jobs: - run: scripts/install.ps1 shell: pwsh - name: Analysing the code with Bandit - run: bandit -n 1 --severity-level medium --recursive src + run: bandit src/ -n 1 --severity-level medium --recursive Build: runs-on: windows-latest strategy: diff --git a/.sonarcloud.properties b/.sonarcloud.properties new file mode 100644 index 00000000..79132012 --- /dev/null +++ b/.sonarcloud.properties @@ -0,0 +1 @@ +sonar.python.version=3.9, 3.10 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index e6b47305..33859d80 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -9,9 +9,10 @@ "ms-python.vscode-pylance", "ms-vscode.powershell", "pkief.material-icon-theme", + "redhat.vscode-xml", "redhat.vscode-yaml", "shardulm94.trailing-spaces", - "sonarsource.sonarlint-vscode" + "sonarsource.sonarlint-vscode", ], "unwantedRecommendations": [ // Must disable in this workspace // @@ -36,5 +37,5 @@ "johnstoncode.svn-scm", // Prefer using VSCode itself as a text editor "vscodevim.vim", - ] + ], } diff --git a/.vscode/launch.json b/.vscode/launch.json index db52528d..957b8d50 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,15 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Python: AutoSplit (debug non-user code)", + "type": "python", + "request": "launch", + "preLaunchTask": "Compile resources", + "program": "src/AutoSplit.py", + "console": "integratedTerminal", + "justMyCode": false + }, { "name": "Python: AutoSplit", "type": "python", diff --git a/.vscode/settings.json b/.vscode/settings.json index 601ce64c..e240084e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -80,6 +80,7 @@ "E": "Warning", "E9": "Error", // Runtime "W": "Warning", + "W6": "Error", // Deprecation warning // Pyflakes "F": "Warning", // PEP8 Naming convention @@ -106,4 +107,6 @@ "powershell.codeFormatting.useCorrectCasing": true, "powershell.codeFormatting.whitespaceBetweenParameters": true, "powershell.integratedConsole.showOnStartup": false, + "xml.codeLens.enabled": true, + "xml.format.spaceBeforeEmptyCloseTag": false, } diff --git a/pyproject.toml b/pyproject.toml index b198a748..93dafc06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,20 @@ max_line_length = 120 recursive = true aggressive = 3 +# https://mypy.readthedocs.io/en/stable/config_file.html +[tool.mypy] +mypy_path = "$MYPY_CONFIG_FILE_DIR/typings" +show_column_numbers=true +show_error_codes=true +# Not all of our imports are types +ignore_missing_imports=true +disable_error_code=[ + # False positives with PyQt .connect. pylint(no-member) works instead + "union-attr", "attr-defined", + # Doesn't see our QTest overload + "call-arg", "arg-type" + ] + # https://github.com/microsoft/pyright/blob/main/docs/configuration.md#sample-pyprojecttoml-file [tool.pyright] typeCheckingMode = "strict" @@ -20,8 +34,6 @@ reportCallInDefaultInitializer="error" reportMissingSuperCall="none" # False positives on base classes reportPropertyTypeMismatch="error" reportUninitializedInstanceVariable="error" -reportUnnecessaryTypeIgnoreComment="error" -reportUnusedCallResult="none" # Exclude from scanning when running pyright exclude = [ # Auto generated, produces unecessary `# type: ignore` @@ -32,6 +44,9 @@ ignore = [ # We expect stub files to be incomplete or contain useless statements "**/*.pyi", ] +reportUnusedCallResult="none" +# We need the comment to flag false positives in mypy +reportUnnecessaryTypeIgnoreComment="none" # Type stubs may not be completable reportMissingTypeStubs = "warning" # False positives with TYPE_CHECKING diff --git a/scripts/lint.ps1 b/scripts/lint.ps1 index f5931602..cd0ece24 100644 --- a/scripts/lint.ps1 +++ b/scripts/lint.ps1 @@ -2,9 +2,13 @@ $originalDirectory = $pwd Set-Location "$PSScriptRoot/.." $exitCodes = 0 +Write-Host "`nRunning autofixes..." +isort src/ typings/ +autopep8 $(git ls-files '**.py*') --in-place + Write-Host "`nRunning Pyright..." $Env:PYRIGHT_PYTHON_FORCE_VERSION = 'latest' -pyright --warnings +pyright src/ --warnings $exitCodes += $LastExitCode if ($LastExitCode -gt 0) { Write-Host "`Pyright failed ($LastExitCode)" -ForegroundColor Red @@ -14,7 +18,7 @@ else { } Write-Host "`nRunning Pylint..." -pylint --output-format=colorized src/ +pylint src/ --output-format=colorized $exitCodes += $LastExitCode if ($LastExitCode -gt 0) { Write-Host "`Pylint failed ($LastExitCode)" -ForegroundColor Red @@ -24,7 +28,7 @@ else { } Write-Host "`nRunning Flake8..." -flake8 +flake8 src/ typings/ $exitCodes += $LastExitCode if ($LastExitCode -gt 0) { Write-Host "`Flake8 failed ($LastExitCode)" -ForegroundColor Red @@ -34,7 +38,7 @@ else { } Write-Host "`nRunning Bandit..." -bandit -f custom --silent --recursive src +bandit src/ -f custom --silent --recursive # $exitCodes += $LastExitCode # Returns 1 on low if ($LastExitCode -gt 0) { Write-Host "`Bandit warning ($LastExitCode)" -ForegroundColor Yellow diff --git a/scripts/requirements-dev.txt b/scripts/requirements-dev.txt index 99f7134e..679c21f7 100644 --- a/scripts/requirements-dev.txt +++ b/scripts/requirements-dev.txt @@ -19,7 +19,7 @@ flake8-pyi>=22.8.1 # flake8 5 support flake8-quotes flake8-simplify pep8-naming -pylint>=2.13.9 # Respect ignore configuration options with --recursive=y +pylint>=2.13.9,<3.0.0 # Respect ignore configuration options with --recursive=y # 3.0 still in pre-release pyright # # Run `./scripts/designer.ps1` to quickly open the bundled PyQt Designer. @@ -27,4 +27,8 @@ pyright qt6-applications # Types pywin32-stubs>=1.0.7 # Added error types +types-keyboard +types-pyautogui +types-requests +types-toml typing-extensions diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 00d56180..3ecf1bbe 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -13,10 +13,10 @@ numpy>=1.23 # Updated types opencv-python-headless>=4.5.4,<4.6 # https://github.com/pyinstaller/pyinstaller/issues/6889 PyQt6>=6.2.1 # Python 3.10 support -git+https://github.com/Avasam/imagehash.git@patch-2#egg=ImageHash # Contains type information + setup as package not module +git+https://github.com/JohannesBuchner/imagehash.git#egg=ImageHash # Contains type information + setup as package not module keyboard packaging -Pillow +Pillow>=7.2.0 # https://github.com/SerpentAI/D3DShot/issues/44 PyAutoGUI requests certifi @@ -26,7 +26,7 @@ git+https://github.com/Avasam/python_grabber.git@complete-types#egg=pygrabber # # Windows-only pywin32>=301 winsdk>=v1.0.0b4 -git+https://github.com/ranchen421/D3DShot.git#egg=D3DShot # https://github.com/SerpentAI/D3DShot/issues/44 +D3DShot # # Build and compile resources PyInstaller From 8f76cad1ca039b4411d76d99e391d2107cc876db Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 7 Sep 2022 21:01:41 -0400 Subject: [PATCH 2/5] Updated typings --- typings/PyInstaller/__init__.pyi | 11 + typings/PyInstaller/__main__.pyi | 13 + typings/PyInstaller/building/build_main.pyi | 32 ++ typings/PyInstaller/building/datastruct.pyi | 36 +++ typings/PyInstaller/compat.pyi | 91 ++++++ typings/PyInstaller/depend/imphookapi.pyi | 16 + typings/PyInstaller/isolated/__init__.pyi | 1 + typings/PyInstaller/isolated/_parent.pyi | 17 ++ typings/PyInstaller/utils/__init__.pyi | 0 typings/PyInstaller/utils/hooks/__init__.pyi | 173 +++++------ typings/PyInstaller/utils/hooks/conda.pyi | 57 ++++ typings/PyInstaller/utils/hooks/win32.pyi | 4 + typings/keyboard/__init__.pyi | 199 ------------- typings/keyboard/_canonical_names.pyi | 7 - typings/keyboard/_darwinkeyboard.pyi | 57 ---- typings/keyboard/_darwinmouse.pyi | 36 --- typings/keyboard/_generic.pyi | 24 -- typings/keyboard/_keyboard_event.pyi | 29 -- typings/keyboard/_mouse_event.pyi | 45 --- typings/keyboard/_nixcommon.pyi | 52 ---- typings/keyboard/_nixkeyboard.pyi | 40 --- typings/keyboard/_nixmouse.pyi | 55 ---- typings/keyboard/_winkeyboard.pyi | 238 --------------- typings/keyboard/_winmouse.pyi | 118 -------- typings/keyboard/mouse.pyi | 80 ----- typings/pyautogui/__init__.pyi | 298 ------------------- typings/pyautogui/_pyautogui_osx.pyi | 2 - typings/pyautogui/_pyautogui_win.pyi | 39 --- typings/pyautogui/_pyautogui_x11.pyi | 2 - typings/pyi_splash/__init__.pyi | 14 + 30 files changed, 368 insertions(+), 1418 deletions(-) create mode 100644 typings/PyInstaller/__init__.pyi create mode 100644 typings/PyInstaller/__main__.pyi create mode 100644 typings/PyInstaller/building/build_main.pyi create mode 100644 typings/PyInstaller/building/datastruct.pyi create mode 100644 typings/PyInstaller/compat.pyi create mode 100644 typings/PyInstaller/depend/imphookapi.pyi create mode 100644 typings/PyInstaller/isolated/__init__.pyi create mode 100644 typings/PyInstaller/isolated/_parent.pyi create mode 100644 typings/PyInstaller/utils/__init__.pyi create mode 100644 typings/PyInstaller/utils/hooks/conda.pyi create mode 100644 typings/PyInstaller/utils/hooks/win32.pyi delete mode 100644 typings/keyboard/__init__.pyi delete mode 100644 typings/keyboard/_canonical_names.pyi delete mode 100644 typings/keyboard/_darwinkeyboard.pyi delete mode 100644 typings/keyboard/_darwinmouse.pyi delete mode 100644 typings/keyboard/_generic.pyi delete mode 100644 typings/keyboard/_keyboard_event.pyi delete mode 100644 typings/keyboard/_mouse_event.pyi delete mode 100644 typings/keyboard/_nixcommon.pyi delete mode 100644 typings/keyboard/_nixkeyboard.pyi delete mode 100644 typings/keyboard/_nixmouse.pyi delete mode 100644 typings/keyboard/_winkeyboard.pyi delete mode 100644 typings/keyboard/_winmouse.pyi delete mode 100644 typings/keyboard/mouse.pyi delete mode 100644 typings/pyautogui/__init__.pyi delete mode 100644 typings/pyautogui/_pyautogui_osx.pyi delete mode 100644 typings/pyautogui/_pyautogui_win.pyi delete mode 100644 typings/pyautogui/_pyautogui_x11.pyi create mode 100644 typings/pyi_splash/__init__.pyi diff --git a/typings/PyInstaller/__init__.pyi b/typings/PyInstaller/__init__.pyi new file mode 100644 index 00000000..df51e4d4 --- /dev/null +++ b/typings/PyInstaller/__init__.pyi @@ -0,0 +1,11 @@ +from typing_extensions import LiteralString + +from PyInstaller import compat as compat + +__all__ = ("HOMEPATH", "PLATFORM", "__version__", "DEFAULT_DISTPATH", "DEFAULT_SPECPATH", "DEFAULT_WORKPATH") +__version__: str = ... +HOMEPATH: str = ... +DEFAULT_SPECPATH: str = ... +DEFAULT_DISTPATH: str = ... +DEFAULT_WORKPATH: str = ... +PLATFORM: LiteralString = ... diff --git a/typings/PyInstaller/__main__.pyi b/typings/PyInstaller/__main__.pyi new file mode 100644 index 00000000..d1f8064d --- /dev/null +++ b/typings/PyInstaller/__main__.pyi @@ -0,0 +1,13 @@ +# https://pyinstaller.org/en/stable/usage.html#running-pyinstaller-from-python-code +from collections.abc import Iterable + +from _typeshed import SupportsKeysAndGetItem +from typing_extensions import TypeAlias + +# Used to update PyInstaller.config.CONF +_PyIConfig: TypeAlias = ( + SupportsKeysAndGetItem[str, bool | str | list[str] | None] | Iterable[tuple[str, bool | str | list[str] | None]] +) + + +def run(pyi_args: Iterable[str] | None = ..., pyi_config: _PyIConfig | None = ...) -> None: ... diff --git a/typings/PyInstaller/building/build_main.pyi b/typings/PyInstaller/building/build_main.pyi new file mode 100644 index 00000000..1d3f44d5 --- /dev/null +++ b/typings/PyInstaller/building/build_main.pyi @@ -0,0 +1,32 @@ +# Referenced in: https://pyinstaller.org/en/stable/hooks.html?highlight=get_hook_config#PyInstaller.utils.hooks.get_hook_config +# Not to be imported during runtime, but is the type reference for hooks and analysis configuration + +from collections.abc import Iterable + +from _typeshed import StrOrBytesPath +from PyInstaller.building.datastruct import Target # type: ignore[import] + +# + + +class Analysis(Target): + # https://pyinstaller.org/en/stable/hooks-config.html#hook-configuration-options + hooksconfig: dict[str, dict[str, object]] + + def __init__( + self, + scripts: Iterable[StrOrBytesPath], + pathex=..., + binaries=..., + datas=..., + hiddenimports=..., + hookspath=..., + hooksconfig: dict[str, dict[str, object]] | None = ..., + excludes=..., + runtime_hooks=..., + cipher=..., + win_no_prefer_redirects: bool = ..., + win_private_assemblies: bool = ..., + noarchive: bool = ..., + module_collection_mode=..., + ) -> None: ... diff --git a/typings/PyInstaller/building/datastruct.pyi b/typings/PyInstaller/building/datastruct.pyi new file mode 100644 index 00000000..922bfa4a --- /dev/null +++ b/typings/PyInstaller/building/datastruct.pyi @@ -0,0 +1,36 @@ +# https://pyinstaller.org/en/stable/advanced-topics.html#the-toc-and-tree-classes +from collections.abc import Iterable, Sequence +from typing import ClassVar + +from typing_extensions import Literal, LiteralString, TypeAlias + +_TypeCode: TypeAlias = Literal["DATA", "BINARY", "EXTENSION", "OPTION"] +_TOCTuple: TypeAlias = tuple[str, str | None, _TypeCode | None] + + +class TOC(list[_TOCTuple]): + filenames: set[str] + def __init__(self, initlist: Iterable[_TOCTuple] | None = ...) -> None: ... + + +class Target: + invcnum: ClassVar[int] + tocfilename: LiteralString + tocbasename: LiteralString + dependencies: TOC + + +class Tree(Target, TOC): + root: str | None + prefix: str | None + excludes: list[str] | None + typecode: _TypeCode + + def __init__( + self, + root: str | None = ..., + prefix: str | None = ..., + excludes: Sequence[str] | None = ..., + typecode: _TypeCode = ...) -> None: ... + + def assemble(self) -> None: ... diff --git a/typings/PyInstaller/compat.pyi b/typings/PyInstaller/compat.pyi new file mode 100644 index 00000000..0d512b77 --- /dev/null +++ b/typings/PyInstaller/compat.pyi @@ -0,0 +1,91 @@ +from collections.abc import Iterable +from importlib.abc import _Path +from types import ModuleType +from typing import AnyStr, overload + +from _typeshed import GenericPath, StrOrBytesPath +from typing_extensions import Literal, TypeAlias + +_OpenFile: TypeAlias = StrOrBytesPath | int + +is_64bits: bool +is_py35: bool +is_py36: bool +is_py37: bool +is_py38: bool +is_py39: bool +is_py310: bool +is_win: bool +is_win_10: bool +is_win_wine: bool +is_cygwin: bool +is_darwin: bool +is_linux: bool +is_solar: bool +is_aix: bool +is_freebsd: bool +is_openbsd: bool +is_hpux: bool +is_unix: bool +is_musl: bool +is_macos_11_compat: tuple[int, ...] | bool | None +is_macos_11_native: tuple[int, ...] | bool | None +is_macos_11: tuple[int, ...] | bool | None +PYDYLIB_NAMES: set[str] +base_prefix: str +is_venv: bool +is_conda: bool +is_pure_conda: bool +python_executable: str +is_ms_app_store: bool +BYTECODE_MAGIC: bytes +EXTENSION_SUFFIXES: list[str] +ALL_SUFFIXES: list[str] + +architecture: Literal["64bit", "n32bit", "32bit"] +system: Literal["Cygwin", "Linux", "Darwin", "Java", "Windows"] +machine: Literal["sw_64", "loongarch64", "arm", "intel", "ppc", "mips", "riscv", "s390x", "unknown"] | None + + +def is_wine_dll(filename: _OpenFile) -> bool: ... +@overload +def getenv(name: str, default: str = ...) -> str: ... +@overload +def getenv(name: str, default: None = ...) -> str | None: ... +def setenv(name: str, value: str) -> None: ... +def unsetenv(name: str) -> None: ... + + +def exec_command(*cmdargs: str, encoding: str | None = ..., raise_enoent: bool + | None = ..., **kwargs: int | bool | Iterable[int] | None) -> str: ... + + +def exec_command_rc(*cmdargs: str, **kwargs: float | bool | Iterable[int] | None) -> int: ... + + +def exec_command_stdout( + *command_args: str, encoding: str | None = ..., **kwargs: float | str | bytes | bool | Iterable[int] | None +) -> str: ... + + +def exec_command_all( + *cmdargs: str, encoding: str | None = ..., **kwargs: int | bool | Iterable[int] | None +) -> tuple[int, str, str]: ... +def exec_python(*args: str, **kwargs: str | None) -> str: ... +def exec_python_rc(*args: str, **kwargs: str | None) -> int: ... +def expand_path(path: GenericPath[AnyStr]) -> AnyStr: ... +def getsitepackages(prefixes: Iterable[str] | None = ...) -> list[str]: ... +def importlib_load_source(name: str, pathname: _Path) -> ModuleType: ... + + +PY3_BASE_MODULES: set[str] +PURE_PYTHON_MODULE_TYPES: set[str] +SPECIAL_MODULE_TYPES: set[str] +BINARY_MODULE_TYPES: set[str] +VALID_MODULE_TYPES: set[str] +BAD_MODULE_TYPES: set[str] +ALL_MODULE_TYPES: set[str] +MODULE_TYPES_TO_TOC_DICT: dict[str, str] + + +def check_requirements() -> None: ... diff --git a/typings/PyInstaller/depend/imphookapi.pyi b/typings/PyInstaller/depend/imphookapi.pyi new file mode 100644 index 00000000..b2a06998 --- /dev/null +++ b/typings/PyInstaller/depend/imphookapi.pyi @@ -0,0 +1,16 @@ +# https://pyinstaller.org/en/stable/hooks-config.html `hook_api` is a PostGraphAPI + +from collections.abc import Iterable + +from _typeshed import StrOrBytesPath +from PyInstaller.building.build_main import Analysis # type: ignore[import] + +# + + +class PostGraphAPI: + @property + def __path__(self) -> tuple[str, ...] | None: ... + @property + def analysis(self) -> Analysis: ... + def add_datas(self, list_of_tuples: Iterable[tuple[StrOrBytesPath, StrOrBytesPath]]) -> None: ... diff --git a/typings/PyInstaller/isolated/__init__.pyi b/typings/PyInstaller/isolated/__init__.pyi new file mode 100644 index 00000000..100495ee --- /dev/null +++ b/typings/PyInstaller/isolated/__init__.pyi @@ -0,0 +1 @@ +from PyInstaller.isolated._parent import Python as Python, call as call, decorate as decorate diff --git a/typings/PyInstaller/isolated/_parent.pyi b/typings/PyInstaller/isolated/_parent.pyi new file mode 100644 index 00000000..aa5ff6d8 --- /dev/null +++ b/typings/PyInstaller/isolated/_parent.pyi @@ -0,0 +1,17 @@ +from collections.abc import Callable +from functools import _AnyCallable +from typing import TypeVar + +from typing_extensions import ParamSpec + +_AC = TypeVar("_AC", bound=_AnyCallable) +_R = TypeVar("_R") +_P = ParamSpec("_P") + + +class Python: + def call(self, function: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... + + +def call(function: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: ... +def decorate(function: _AC) -> _AC: ... diff --git a/typings/PyInstaller/utils/__init__.pyi b/typings/PyInstaller/utils/__init__.pyi new file mode 100644 index 00000000..e69de29b diff --git a/typings/PyInstaller/utils/hooks/__init__.pyi b/typings/PyInstaller/utils/hooks/__init__.pyi index c89940ed..de5ec5ec 100644 --- a/typings/PyInstaller/utils/hooks/__init__.pyi +++ b/typings/PyInstaller/utils/hooks/__init__.pyi @@ -1,126 +1,105 @@ -""" # noqa: Y021 -This type stub file was generated by pyright. -""" -from collections.abc import Callable -from typing import Any, Literal +# https://pyinstaller.org/en/stable/hooks.html -logger = ... -PY_IGNORE_EXTENSIONS: set -hook_variables: dict +import os +from collections.abc import Callable, Iterable +from typing import Any, AnyStr +import pkg_resources +from _typeshed import AnyOrLiteralStr, StrOrBytesPath, SupportsKeysAndGetItem +from PyInstaller.depend.imphookapi import PostGraphAPI # type: ignore[import] +from PyInstaller.utils.hooks import conda as conda_support +from PyInstaller.utils.hooks.win32 import get_pywin32_module_file_attribute as get_pywin32_module_file_attribute +from typing_extensions import Literal, TypeAlias -def exec_statement(statement): - ... +from PyInstaller import isolated +_Environ: TypeAlias = SupportsKeysAndGetItem[str, str] | Iterable[tuple[str, str]] | os._Environ[str] -def exec_statement_rc(statement): - ... +PY_IGNORE_EXTENSIONS: set[str] +hook_variables: dict[str, str] -def exec_script(script_filename, *args, env=...): - ... +def exec_statement(statement: str) -> str | int: ... +def exec_statement_rc(statement: str) -> str | int: ... -def exec_script_rc(script_filename, *args, env=...): - ... +def exec_script(script_filename: os.PathLike[AnyStr] | AnyOrLiteralStr, + *args: str, env: _Environ | None = ...) -> str | int: ... -def eval_statement(statement) -> Any | Literal[""]: - ... +def exec_script_rc( + script_filename: os.PathLike[AnyStr] | AnyOrLiteralStr, *args: str, env: _Environ | None = ... +) -> str | int: ... +def eval_statement(statement: str) -> Any | Literal[""]: ... -def eval_script(scriptfilename, *args, env=...) -> Any | Literal[""]: - ... +def eval_script( + scriptfilename: os.PathLike[AnyStr] | AnyOrLiteralStr, *args: str, env: _Environ | None = ... +) -> Any | Literal[""]: ... +@isolated.decorate +def get_pyextension_imports(module_name: str) -> list[str]: ... +def get_homebrew_path(formula: str = ...) -> str | None: ... +def remove_prefix(string: str, prefix: str) -> str: ... +def remove_suffix(string: str, suffix: str) -> str: ... +def remove_file_extension(filename: str) -> str: ... +@isolated.decorate +def can_import_module(module_name: str) -> bool: ... +def get_module_attribute(module_name: str, attr_name: str) -> Any: ... +def get_module_file_attribute(package: str) -> str | None: ... -def get_pyextension_imports(modname) -> Any | list: - ... +def is_module_satisfies( + requirements: Iterable[str] | pkg_resources.Requirement, + version: str | pkg_resources.Distribution | None = ..., + version_attr: str = ..., +) -> bool: ... +def is_package(module_name: str) -> bool: ... +def get_all_package_paths(package: str) -> list[str]: ... +def package_base_path(package_path: str, package: str) -> str: ... +def get_package_paths(package: str) -> tuple[str, str]: ... -def get_homebrew_path(formula=...) -> str | None: - ... +def collect_submodules( + package: str, filter: Callable[[str], bool] = ..., on_error: Literal["ignore", "warn once", "warn", "raise"] = ... +) -> list[str]: ... +def is_module_or_submodule(name: str, mod_or_submod: str) -> bool: ... -def remove_prefix(string, prefix): - ... +PY_DYLIB_PATTERNS: list[str] -def remove_suffix(string, suffix): - ... +def collect_dynamic_libs(package: str, destdir: object | None = ...) -> list[tuple[str, str]]: ... -def remove_file_extension(filename): - ... +def collect_data_files( + package: str, + include_py_files: bool = ..., + subdir: StrOrBytesPath | None = ..., + excludes: Iterable[str] | None = ..., + includes: Iterable[str] | None = ..., +) -> list[tuple[str, str]]: ... -def can_import_module(module_name): - ... +def collect_system_data_files( + path: str, destdir: StrOrBytesPath | None = ..., include_py_files: bool = ... +) -> list[tuple[str, str]]: ... +def copy_metadata(package_name: str, recursive: bool = ...) -> list[tuple[str, str]]: ... +def get_installer(module: str) -> str | None: ... +def requirements_for_package(package_name: str) -> list[str]: ... -def get_module_attribute(module_name, attr_name): - ... +def collect_all( + package_name: str, + include_py_files: bool = ..., + filter_submodules: Callable[[str], bool] | None = ..., + exclude_datas: Iterable[str] | None = ..., + include_datas: Iterable[str] | None = ..., + on_error: Literal["ignore", "warn once", "warn", "raise"] = ..., +) -> tuple[list[tuple[str, str]], list[tuple[str, str]], list[str]]: ... +def collect_entry_point(name: str) -> tuple[tuple[str, str], list[str]]: ... +def get_hook_config(hook_api: PostGraphAPI, module_name: str, key: str) -> None: ... -def get_module_file_attribute(package): - ... - - -def is_module_satisfies(requirements, version=..., version_attr=...): - ... - - -def is_package(module_name) -> Literal[False]: - ... - - -def get_package_paths(package) -> tuple[str, str]: - ... - - -def collect_submodules(package: str, filter: Callable[[str], bool] | None = ...) -> list[str]: - ... - - -def is_module_or_submodule(name, mod_or_submod): - ... - - -PY_DYLIB_PATTERNS: str - - -def collect_dynamic_libs(package: str, destdir: str | None = ...) -> list[tuple[str, str]]: - ... - - -def collect_data_files(package: str, include_py_files: bool = ..., subdir: str = ..., - excludes: list[str] = ..., includes: list[str] = ...) -> list[tuple[str, str]]: - ... - - -def collect_system_data_files(path, destdir=..., include_py_files=...): - ... - - -def copy_metadata(package_name, recursive=...) -> list: - ... - - -def get_installer(module) -> str | None: - ... - - -def requirements_for_package(package_name) -> list: - ... - - -def collect_all(package_name, include_py_files=..., filter_submodules=..., - exclude_datas=..., include_datas=...) -> tuple[list, list, list]: - ... - - -def collect_entry_point(name: str) -> tuple[list, list]: - ... - - -def get_hook_config(hook_api, module_name, key) -> None: - ... +def include_or_exclude_file( + filename: str, include_list: Iterable[str] | None = ..., exclude_list: Iterable[str] | None = ... +) -> bool: ... diff --git a/typings/PyInstaller/utils/hooks/conda.pyi b/typings/PyInstaller/utils/hooks/conda.pyi new file mode 100644 index 00000000..657ed28d --- /dev/null +++ b/typings/PyInstaller/utils/hooks/conda.pyi @@ -0,0 +1,57 @@ +# https://pyinstaller.org/en/stable/hooks.html?highlight=conda_support#module-PyInstaller.utils.hooks.conda + +import sys +from collections.abc import Sequence +from pathlib import Path, PurePath +from typing import Any + +from _typeshed import StrOrBytesPath +from typing_extensions import TypeAlias, TypedDict + +if sys.version_info >= (3, 8): + from importlib.metadata import PackagePath as _PackagePath +else: + _PackagePath: TypeAlias = Any + +CONDA_ROOT: Path +CONDA_META_DIR: Path +PYTHONPATH_PREFIXES: list[Path] + + +class _RawDict(TypedDict): + name: str + version: str + files: list[StrOrBytesPath | PurePath] + depends: list[str] + + +class Distribution: + raw: _RawDict + name: str + version: str + files: list[PackagePath] + dependencies: list[str] + packages: list[str | None] + def __init__(self, json_path: str) -> None: ... + @classmethod + def from_name(cls, name: str) -> Distribution: ... + @classmethod + def from_package_name(cls, name: str) -> Distribution: ... + + +class PackagePath(_PackagePath): + def locate(self) -> Path: ... + + +def walk_dependency_tree(initial: str, excludes: Sequence[str] | None = ...) -> dict[str, Distribution]: ... +def requires(name: str, strip_versions: bool = ...) -> list[str]: ... +def files(name: str, dependencies: bool = ..., excludes: Sequence[str] | None = ...) -> list[PackagePath]: ... + + +def collect_dynamic_libs( + name: str, dest: str = ..., dependencies: bool = ..., excludes: Sequence[str] | None = ... +) -> list[tuple[str, str]]: ... + + +distributions: dict[str, Distribution] +distributions_by_package: dict[str | None, Distribution] diff --git a/typings/PyInstaller/utils/hooks/win32.pyi b/typings/PyInstaller/utils/hooks/win32.pyi new file mode 100644 index 00000000..cf597c88 --- /dev/null +++ b/typings/PyInstaller/utils/hooks/win32.pyi @@ -0,0 +1,4 @@ +__all__ = ("get_pywin32_module_file_attribute",) + + +def get_pywin32_module_file_attribute(module_name: str) -> str | int: ... diff --git a/typings/keyboard/__init__.pyi b/typings/keyboard/__init__.pyi deleted file mode 100644 index 6d9f5e54..00000000 --- a/typings/keyboard/__init__.pyi +++ /dev/null @@ -1,199 +0,0 @@ -from collections import Counter, defaultdict, deque -from collections.abc import Callable, Generator, Sequence -from queue import Queue -from threading import Event as _UninterruptibleEvent -from typing import Optional # noqa: Y037 # mypy issue - -from keyboard._canonical_names import all_modifiers as all_modifiers, sided_modifiers as sided_modifiers -from keyboard._generic import GenericListener as _GenericListener -from keyboard._keyboard_event import KEY_DOWN as KEY_DOWN, KEY_UP as KEY_UP, KeyboardEvent as KeyboardEvent -from typing_extensions import Literal, TypeAlias - -_Key: TypeAlias = int | str -_ScanCodeList: TypeAlias = list[int] | tuple[int, ...] -_ParseableHotkey: TypeAlias = _Key | list[int | _ScanCodeList] | tuple[int | _ScanCodeList, ...] -_Callback: TypeAlias = Callable[[KeyboardEvent], Optional[bool]] | Callable[[], Optional[bool]] -# Can't use ParamSpecArgs on `args`, only on `*args` -# _P = ParamSpec("_P") -_P: TypeAlias = tuple[object, ...] - -version: str - - -class _Event(_UninterruptibleEvent): - def wait(self) -> None: ... - - -def is_modifier(key: _Key | None) -> bool: ... - - -class _KeyboardListener(_GenericListener): - transition_table: dict[ - tuple[Literal["free"], Literal["up"], Literal["modifier"]] - | tuple[Literal["free"], Literal["down"], Literal["modifier"]] - | tuple[Literal["pending"], Literal["up"], Literal["modifier"]] - | tuple[Literal["pending"], Literal["down"], Literal["modifier"]] - | tuple[Literal["suppressed"], Literal["up"], Literal["modifier"]] - | tuple[Literal["suppressed"], Literal["down"], Literal["modifier"]] - | tuple[Literal["allowed"], Literal["up"], Literal["modifier"]] - | tuple[Literal["allowed"], Literal["down"], Literal["modifier"]] - | tuple[Literal["free"], Literal["up"], Literal["hotkey"]] - | tuple[Literal["free"], Literal["down"], Literal["hotkey"]] - | tuple[Literal["pending"], Literal["up"], Literal["hotkey"]] - | tuple[Literal["pending"], Literal["down"], Literal["hotkey"]] - | tuple[Literal["suppressed"], Literal["up"], Literal["hotkey"]] - | tuple[Literal["suppressed"], Literal["down"], Literal["hotkey"]] - | tuple[Literal["allowed"], Literal["up"], Literal["hotkey"]] - | tuple[Literal["allowed"], Literal["down"], Literal["hotkey"]] - | tuple[Literal["free"], Literal["up"], Literal["other"]] - | tuple[Literal["free"], Literal["down"], Literal["other"]] - | tuple[Literal["pending"], Literal["up"], Literal["other"]] - | tuple[Literal["pending"], Literal["down"], Literal["other"]] - | tuple[Literal["suppressed"], Literal["up"], Literal["other"]] - | tuple[Literal["suppressed"], Literal["down"], Literal["other"]] - | tuple[Literal["allowed"], Literal["up"], Literal["other"]] - | tuple[Literal["allowed"], Literal["down"], Literal["other"]], - tuple[Literal[False], Literal[True], Literal["free"]] - | tuple[Literal[False], Literal[False], Literal["pending"]] - | tuple[Literal[True], Literal[True], Literal["free"]] - | tuple[Literal[False], Literal[True], Literal["allowed"]] - | tuple[Literal[False], Literal[False], Literal["free"]] - | tuple[Literal[False], Literal[False], Literal["suppressed"]] - | tuple[Literal[False], None, Literal["free"]] - | tuple[Literal[False], None, Literal["suppressed"]] - | tuple[Literal[False], None, Literal["allowed"]] - | tuple[Literal[True], Literal[True], Literal["allowed"]] - | tuple[Literal[False], Literal[False], Literal["allowed"]], - ] - active_modifiers: set[int] - blocking_hooks: list[_Callback] - blocking_keys: defaultdict[int, list[_Callback]] - nonblocking_keys: defaultdict[int, list[_Callback]] - blocking_hotkeys: defaultdict[tuple[int, ...], list[_Callback]] - nonblocking_hotkeys: defaultdict[tuple[int, ...], list[_Callback]] - filtered_modifiers: Counter[int] - is_replaying: bool - modifier_states: dict[_Key, str] - def init(self) -> None: ... - def pre_process_event(self, event): ... - def direct_callback(self, event): ... - def listen(self) -> None: ... - - -def key_to_scan_codes(key: _ParseableHotkey, error_if_missing: bool = ...) -> tuple[int, ...]: ... -def parse_hotkey(hotkey: _ParseableHotkey) -> tuple[tuple[tuple[int, ...], ...], ...]: ... -def send(hotkey: _ParseableHotkey, do_press: bool = ..., do_release: bool = ...) -> None: ... - - -press_and_release = send - - -def press(hotkey: _ParseableHotkey) -> None: ... -def release(hotkey: _ParseableHotkey) -> None: ... - -# is_pressed cannot check multi-step hotkeys, so not using _ParseableHotkey - - -def is_pressed(hotkey: _Key | _ScanCodeList) -> bool: ... -def call_later(fn: Callable[..., None], args: _P = ..., delay: float = ...) -> None: ... -def hook(callback: _Callback, suppress: bool = ..., on_remove: Callable[[], None] = ...) -> Callable[[], None]: ... -def on_press(callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ... -def on_release(callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ... -def hook_key(key: _ParseableHotkey, callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ... -def on_press_key(key: _ParseableHotkey, callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ... -def on_release_key(key: _ParseableHotkey, callback: _Callback, suppress: bool = ...) -> Callable[[], None]: ... -def unhook(remove: _Callback) -> None: ... - - -unhook_key = unhook - - -def unhook_all() -> None: ... -def block_key(key: _ParseableHotkey) -> Callable[[], None]: ... - - -unblock_key = unhook_key - - -def remap_key(src: _ParseableHotkey, dst: _ParseableHotkey) -> Callable[[], None]: ... - - -unremap_key = unhook_key - - -def parse_hotkey_combinations(hotkey: _ParseableHotkey) -> tuple[tuple[tuple[int, ...], ...], ...]: ... - - -def add_hotkey( - hotkey: _ParseableHotkey, - callback: Callable[..., bool | None], - args: _P = ..., - suppress: bool = ..., - timeout: float = ..., - trigger_on_release: bool = ..., -) -> Callable[[], None]: ... - - -register_hotkey = add_hotkey - - -def remove_hotkey(hotkey_or_callback: _ParseableHotkey | _Callback) -> None: ... - - -unregister_hotkey = remove_hotkey -clear_hotkey = remove_hotkey - - -def unhook_all_hotkeys() -> None: ... - - -unregister_all_hotkeys = unhook_all_hotkeys -remove_all_hotkeys = unhook_all_hotkeys -clear_all_hotkeys = unhook_all_hotkeys - - -def remap_hotkey( - src: _ParseableHotkey, dst: _ParseableHotkey, suppress: bool = ..., trigger_on_release: bool = ... -) -> Callable[[], None]: ... - - -unremap_hotkey = remove_hotkey - - -def stash_state() -> list[int]: ... -def restore_state(scan_codes: Sequence[int]) -> None: ... -def restore_modifiers(scan_codes: Sequence[int]) -> None: ... -def write(text: str, delay: float = ..., restore_state_after: bool = ..., exact: bool | None = ...) -> None: ... -def wait(hotkey: _ParseableHotkey | None = ..., suppress: bool = ..., trigger_on_release: bool = ...) -> None: ... -def get_hotkey_name(names: Sequence[str] | None = ...) -> str: ... -def read_event(suppress: bool = ...) -> KeyboardEvent: ... -def read_key(suppress: bool = ...) -> _Key: ... -def read_hotkey(suppress: bool = ...) -> str: ... -def get_typed_strings(events: Sequence[KeyboardEvent], allow_backspace: bool = ...) -> Generator[str, None, None]: ... - - -def start_recording( - recorded_events_queue: Queue[KeyboardEvent] | None = ..., -) -> tuple[Queue[KeyboardEvent], Callable[[], None]]: ... -def stop_recording() -> list[deque[KeyboardEvent]]: ... -def record(until: str = ..., suppress: bool = ..., trigger_on_release: bool = ...) -> list[deque[KeyboardEvent]]: ... -def play(events: Sequence[KeyboardEvent], speed_factor: float = ...) -> None: ... - - -replay = play - - -def add_word_listener( - word: str, callback: _Callback, triggers: Sequence[str] = ..., match_suffix: bool = ..., timeout: float = ... -) -> Callable[[], None]: ... -def remove_word_listener(word_or_handler: str | _Callback) -> None: ... - - -def add_abbreviation( - source_text: str, replacement_text: str, match_suffix: bool = ..., timeout: float = ... -) -> Callable[[], None]: ... - - -register_word_listener = add_word_listener -register_abbreviation = add_abbreviation -remove_abbreviation = remove_word_listener diff --git a/typings/keyboard/_canonical_names.pyi b/typings/keyboard/_canonical_names.pyi deleted file mode 100644 index dee5d6db..00000000 --- a/typings/keyboard/_canonical_names.pyi +++ /dev/null @@ -1,7 +0,0 @@ -basestring = str -canonical_names: dict[str, str] -sided_modifiers: set[str] -all_modifiers: set[str] - - -def normalize_name(name: str) -> str: ... diff --git a/typings/keyboard/_darwinkeyboard.pyi b/typings/keyboard/_darwinkeyboard.pyi deleted file mode 100644 index b0760a1b..00000000 --- a/typings/keyboard/_darwinkeyboard.pyi +++ /dev/null @@ -1,57 +0,0 @@ -from collections.abc import Callable, Generator, Sequence -from ctypes import CDLL -from typing import TypeVar - -from _typeshed import Incomplete -from keyboard._canonical_names import normalize_name as normalize_name -from keyboard._keyboard_event import KEY_DOWN as KEY_DOWN, KEY_UP as KEY_UP, KeyboardEvent as KeyboardEvent -from typing_extensions import TypeAlias - -_Unused: TypeAlias = object - -# https://github.com/ronaldoussoren/pyobjc/milestone/3 -_CGEventTap: TypeAlias = Incomplete # Quartz.CGEventTap -_KCGEvent: TypeAlias = Incomplete # Quartz.kCGEvent -_T = TypeVar("_T") - -unichr = chr -Carbon: CDLL - - -class KeyMap: - non_layout_keys: dict[int, str] - layout_specific_keys: dict[int, tuple[str, str]] - def character_to_vk(self, character: str) -> tuple[int, list[str]]: ... - def vk_to_character(self, vk: int, modifiers: Sequence[str] = ...): ... - - -class KeyController: - key_map: KeyMap - current_modifiers: dict[str, bool] - media_keys: dict[str, int] - def press(self, key_code: int) -> None: ... - def release(self, key_code: int) -> None: ... - def map_char(self, character: str) -> tuple[int, list[str]]: ... - def map_scan_code(self, scan_code: int) -> int | str | None: ... - - -class KeyEventListener: - blocking: bool - callback: Callable[[KeyboardEvent], None] - listening: bool - tap: _CGEventTap - def __init__(self, callback: Callable[[KeyboardEvent], None], blocking: bool = ...) -> None: ... - def run(self) -> None: ... - def handler(self, proxy: _Unused, e_type: _KCGEvent, event: _T, refcon: _Unused) -> _T: ... - - -key_controller: KeyController - - -def init() -> None: ... -def press(scan_code: int) -> None: ... -def release(scan_code: int) -> None: ... -def map_name(name: str) -> Generator[tuple[int, list[str]], None, None]: ... -def name_from_scancode(scan_code: int) -> int | str | None: ... -def listen(callback: Callable[[KeyboardEvent], None]) -> None: ... -def type_unicode(character: str) -> None: ... diff --git a/typings/keyboard/_darwinmouse.pyi b/typings/keyboard/_darwinmouse.pyi deleted file mode 100644 index 61711c0f..00000000 --- a/typings/keyboard/_darwinmouse.pyi +++ /dev/null @@ -1,36 +0,0 @@ -from collections.abc import Callable -from queue import Queue -from typing import TypeVar - -from _typeshed import Incomplete -from keyboard._keyboard_event import KeyboardEvent -from keyboard._mouse_event import (DOWN as DOWN, LEFT as LEFT, MIDDLE as MIDDLE, RIGHT as RIGHT, UP as UP, X2 as X2, - ButtonEvent as ButtonEvent, MoveEvent as MoveEvent, WheelEvent as WheelEvent, X as X, - _MouseButton) -from typing_extensions import TypeAlias - -_Unused: TypeAlias = object - -# https://github.com/ronaldoussoren/pyobjc/milestone/3 -_CGEventTap: TypeAlias = Incomplete # Quartz.CGEventTap -_KCGEventKey: TypeAlias = Incomplete # Quartz.kCGEventKey -_T = TypeVar("_T") - - -class MouseEventListener: - blocking: bool - callback: Callable[[KeyboardEvent], None] - listening: bool - def __init__(self, callback: Callable[[KeyboardEvent], None], blocking: bool = ...) -> None: ... - tap: _CGEventTap - def run(self) -> None: ... - def handler(self, proxy: _Unused, e_type: _KCGEventKey, event: _T, refcon: _Unused) -> _T: ... - - -def init() -> None: ... -def listen(queue: Queue[KeyboardEvent]) -> None: ... -def press(button: _MouseButton = ...) -> None: ... -def release(button: _MouseButton = ...) -> None: ... -def wheel(delta: int = ...) -> None: ... -def move_to(x: int, y: int) -> None: ... -def get_position() -> tuple[int, int]: ... diff --git a/typings/keyboard/_generic.pyi b/typings/keyboard/_generic.pyi deleted file mode 100644 index 9c62225f..00000000 --- a/typings/keyboard/_generic.pyi +++ /dev/null @@ -1,24 +0,0 @@ -from collections.abc import Callable -from queue import Queue -from threading import Lock, Thread - -from keyboard._keyboard_event import KeyboardEvent -from keyboard._mouse_event import ButtonEvent, MoveEvent, WheelEvent -from typing_extensions import Literal, TypeAlias - -_Event: TypeAlias = KeyboardEvent | ButtonEvent | WheelEvent | MoveEvent - - -class GenericListener: - lock: Lock - handlers: list[Callable[[_Event], bool | None]] - listening: bool - queue: Queue[_Event] - listening_thread: Thread | None - processing_thread: Thread | None - def invoke_handlers(self, event: _Event) -> Literal[1] | None: ... - def start_if_necessary(self) -> None: ... - def pre_process_event(self, event: _Event) -> None: ... - def process(self) -> None: ... - def add_handler(self, handler: Callable[[_Event], bool | None]) -> None: ... - def remove_handler(self, handler: Callable[[_Event], bool | None]) -> None: ... diff --git a/typings/keyboard/_keyboard_event.pyi b/typings/keyboard/_keyboard_event.pyi deleted file mode 100644 index 1e0453fc..00000000 --- a/typings/keyboard/_keyboard_event.pyi +++ /dev/null @@ -1,29 +0,0 @@ -from keyboard._canonical_names import canonical_names as canonical_names, normalize_name as normalize_name -from typing_extensions import Literal - -basestring = str -KEY_DOWN: Literal["down"] -KEY_UP: Literal["up"] - - -class KeyboardEvent: - event_type: Literal["down", "up"] | None - scan_code: int - name: str | None - time: float | None - device: str | None - modifiers: tuple[str, ...] | None - is_keypad: bool | None - - def __init__( - self, - event_type: Literal["down", "up"] | None, - scan_code: int, - name: str | None = ..., - time: float | None = ..., - device: str | None = ..., - modifiers: tuple[str, ...] | None = ..., - is_keypad: bool | None = ..., - ) -> None: ... - def to_json(self, ensure_ascii: bool = ...) -> str: ... - def __eq__(self, other: object) -> bool: ... diff --git a/typings/keyboard/_mouse_event.pyi b/typings/keyboard/_mouse_event.pyi deleted file mode 100644 index 9d1b5f74..00000000 --- a/typings/keyboard/_mouse_event.pyi +++ /dev/null @@ -1,45 +0,0 @@ -import sys -from typing import NamedTuple - -from typing_extensions import Literal, TypeAlias - -LEFT: Literal["left"] -RIGHT: Literal["right"] -MIDDLE: Literal["middle"] -X: Literal["x"] -X2: Literal["x2"] - -UP: Literal["up"] -DOWN: Literal["down"] -DOUBLE: Literal["double"] -WHEEL: Literal["wheel"] - -VERTICAL: Literal["vertical"] -HORIZONTAL: Literal["horizontal"] - -if sys.platform == "linux" or sys.platform == "win32": - _MouseButton: TypeAlias = Literal["left", "right", "middle", "x", "x2"] -else: - _MouseButton: TypeAlias = Literal["left", "right", "middle"] - -if sys.platform == "win32": - _MouseEvent: TypeAlias = Literal["up", "down", "double", "wheel"] -else: - _MouseEvent: TypeAlias = Literal["up", "down"] - - -class ButtonEvent(NamedTuple): - event_type: _MouseEvent - button: _MouseButton - time: float - - -class WheelEvent(NamedTuple): - delta: int - time: float - - -class MoveEvent(NamedTuple): - x: int - y: int - time: float diff --git a/typings/keyboard/_nixcommon.pyi b/typings/keyboard/_nixcommon.pyi deleted file mode 100644 index 3c616d86..00000000 --- a/typings/keyboard/_nixcommon.pyi +++ /dev/null @@ -1,52 +0,0 @@ -from collections.abc import Generator, Sequence -from io import BufferedReader, BufferedWriter -from queue import Queue -from typing import NamedTuple - -from _typeshed import Incomplete -from typing_extensions import Literal - -event_bin_format: Literal["llHHI"] -EV_SYN: Literal[0] -EV_KEY: Literal[1] -EV_REL: Literal[2] -EV_ABS: Literal[3] -EV_MSC: Literal[4] - - -def make_uinput() -> BufferedWriter: ... - - -class EventDevice: - path: str - def __init__(self, path: str) -> None: ... - @property - def input_file(self) -> BufferedReader: ... - @property - def output_file(self) -> BufferedWriter: ... - def read_event(self) -> tuple[float, Incomplete, Incomplete, Incomplete, str]: ... - def write_event(self, type, code, value) -> None: ... - - -class AggregatedEventDevice: - event_queue: Queue[tuple[float, Incomplete, Incomplete, Incomplete, str]] - devices: Sequence[EventDevice] - output: EventDevice - def __init__(self, devices: Sequence[EventDevice], output: EventDevice | None = ...) -> None: ... - def read_event(self) -> EventDevice: ... - def write_event(self, type, code, value) -> None: ... - - -class DeviceDescription(NamedTuple): - event_file: str - is_mouse: bool - is_keyboard: bool - - -device_pattern: str - - -def list_devices_from_proc(type_name: str) -> Generator[EventDevice, None, None]: ... -def list_devices_from_by_id(name_suffix: str, by_id: bool = ...) -> Generator[EventDevice, None, None]: ... -def aggregate_devices(type_name: str) -> AggregatedEventDevice | EventDevice: ... -def ensure_root() -> None: ... diff --git a/typings/keyboard/_nixkeyboard.pyi b/typings/keyboard/_nixkeyboard.pyi deleted file mode 100644 index 226b101c..00000000 --- a/typings/keyboard/_nixkeyboard.pyi +++ /dev/null @@ -1,40 +0,0 @@ -from collections import defaultdict -from collections.abc import Callable, Generator - -from keyboard._canonical_names import all_modifiers as all_modifiers, normalize_name as normalize_name -from keyboard._keyboard_event import KEY_DOWN as KEY_DOWN, KEY_UP as KEY_UP, KeyboardEvent as KeyboardEvent -from keyboard._nixcommon import (EV_KEY as EV_KEY, AggregatedEventDevice, EventDevice, - aggregate_devices as aggregate_devices, ensure_root as ensure_root) - -# - - -def cleanup_key(name: str) -> tuple[str, bool]: ... -def cleanup_modifier(modifier: str) -> str: ... - - -to_name: defaultdict[tuple[int, tuple[str, ...]], list[str] | str] -from_name: defaultdict[str, list[tuple[int, tuple[str, ...]]]] -keypad_scan_codes: set[str] - - -def register_key(key_and_modifiers: tuple[int, tuple[str, ...]], name: str) -> None: ... -def build_tables() -> None: ... - - -device: AggregatedEventDevice | EventDevice | None - - -def build_device() -> None: ... -def init() -> None: ... - - -pressed_modifiers: set[str] - - -def listen(callback: Callable[[KeyboardEvent], None]) -> None: ... -def write_event(scan_code: int, is_down: bool | int) -> None: ... -def map_name(name: str) -> Generator[tuple[int, tuple[str, ...]], None, None]: ... -def press(scan_code: int) -> None: ... -def release(scan_code: int) -> None: ... -def type_unicode(character: str | bytes | bytearray) -> None: ... diff --git a/typings/keyboard/_nixmouse.pyi b/typings/keyboard/_nixmouse.pyi deleted file mode 100644 index d292cb28..00000000 --- a/typings/keyboard/_nixmouse.pyi +++ /dev/null @@ -1,55 +0,0 @@ -from ctypes import CDLL -from queue import Queue -from subprocess import check_output as check_output - -from _typeshed import Incomplete -from keyboard._mouse_event import (DOWN as DOWN, LEFT as LEFT, MIDDLE as MIDDLE, RIGHT as RIGHT, UP as UP, X2 as X2, - ButtonEvent as ButtonEvent, MoveEvent as MoveEvent, WheelEvent as WheelEvent, X as X, - _MouseButton) -from keyboard._nixcommon import (EV_ABS as EV_ABS, EV_KEY as EV_KEY, EV_MSC as EV_MSC, EV_REL as EV_REL, - EV_SYN as EV_SYN, AggregatedEventDevice, EventDevice, - aggregate_devices as aggregate_devices, ensure_root as ensure_root) -from typing_extensions import Literal - -x11: CDLL | None -display: Incomplete # x11.XOpenDisplay(None) -window: Incomplete # x11.XDefaultRootWindow(display) - - -def build_display() -> None: ... -def get_position() -> tuple[int, int]: ... -def move_to(x, y) -> None: ... - - -REL_X: Literal[0] -REL_Y: Literal[1] -REL_Z: Literal[2] -REL_HWHEEL: Literal[6] -REL_WHEEL: Literal[8] - -ABS_X: Literal[0] -ABS_Y: Literal[1] - -BTN_MOUSE: Literal[272] -BTN_LEFT: Literal[272] -BTN_RIGHT: Literal[273] -BTN_MIDDLE: Literal[274] -BTN_SIDE: Literal[275] -BTN_EXTRA: Literal[276] - -button_by_code: dict[int, str] -code_by_button: dict[str, int] -device: AggregatedEventDevice | EventDevice | None - - -def build_device() -> None: ... - - -init = build_device - - -def listen(queue: Queue[ButtonEvent | WheelEvent | MoveEvent]) -> None: ... -def press(button: _MouseButton = ...) -> None: ... -def release(button: _MouseButton = ...) -> None: ... -def move_relative(x: int, y: int) -> None: ... -def wheel(delta: int = ...) -> None: ... diff --git a/typings/keyboard/_winkeyboard.pyi b/typings/keyboard/_winkeyboard.pyi deleted file mode 100644 index 7d273869..00000000 --- a/typings/keyboard/_winkeyboard.pyi +++ /dev/null @@ -1,238 +0,0 @@ -import sys -from collections import defaultdict -from collections.abc import Callable, Generator, Sequence -from ctypes import (Array, Structure, Union, c_char as c_char, c_int, c_int32 as c_int32, c_long, c_short, c_uint, - c_uint8, c_uint32 as c_uint32, c_wchar) -from ctypes.wintypes import (BOOL, DWORD, HHOOK, HMODULE, LONG, LPARAM, LPCWSTR, LPDWORD, LPMSG as LPMSG, LPWSTR, WCHAR, - WORD, WPARAM) -from threading import Lock - -from _typeshed import Incomplete -from keyboard._canonical_names import normalize_name as normalize_name -from keyboard._keyboard_event import KEY_DOWN as KEY_DOWN, KEY_UP as KEY_UP, KeyboardEvent as KeyboardEvent -from typing_extensions import Literal, TypeAlias - -if sys.platform == "win32": - from ctypes import WinDLL - - kernel32: WinDLL - user32: WinDLL - -ULONG_PTR: TypeAlias = LPDWORD -GetModuleHandleW: Callable[[LPCWSTR | None], HMODULE] -VK_PACKET: Literal[231] -INPUT_MOUSE: Literal[0] -INPUT_KEYBOARD: Literal[1] -INPUT_HARDWARE: Literal[2] -KEYEVENTF_KEYUP: Literal[2] -KEYEVENTF_UNICODE: Literal[4] - - -class KBDLLHOOKSTRUCT(Structure): - vk_code: DWORD - scan_code: DWORD - flags: DWORD - time: c_int - dwExtraInfo: ULONG_PTR - - -class MOUSEINPUT(Structure): - dx: LONG - dy: LONG - mouseData: DWORD - dwFlags: DWORD - time: DWORD - dwExtraInfo: ULONG_PTR - - -class KEYBDINPUT(Structure): - wVk: WORD - wScan: WORD - dwFlags: DWORD - time: DWORD - dwExtraInfo: ULONG_PTR - - -class HARDWAREINPUT(Structure): - uMsg: DWORD - wParamL: WORD - wParamH: WORD - - -class _INPUTunion(Union): - mi: MOUSEINPUT - ki: KEYBDINPUT - hi: HARDWAREINPUT - - -class INPUT(Structure): - type: DWORD - union: _INPUTunion - - -_POINTER_KBDLLHOOKSTRUCT: TypeAlias = Incomplete # POINTER(KBDLLHOOKSTRUCT) -_LRESULT: TypeAlias = Incomplete -_POINTER_INPUT: TypeAlias = Incomplete # POINTER(INPUT) -_LowLevelKeyboardProc: TypeAlias = Callable[[Callable[[c_int, WPARAM, LPARAM], c_int]], _POINTER_KBDLLHOOKSTRUCT] -LowLevelKeyboardProc: _LowLevelKeyboardProc -SetWindowsHookEx: Callable[[c_int, _LowLevelKeyboardProc, c_int, c_int], HHOOK] -CallNextHookEx: Callable[[c_int, c_int, WPARAM, LPARAM], c_int] -UnhookWindowsHookEx: Callable[[HHOOK], BOOL] -GetMessage: Callable[[LPMSG, c_int, c_int, c_int], BOOL] -TranslateMessage: Callable[[LPMSG], BOOL] -DispatchMessage: Callable[[LPMSG], _LRESULT] -_KeyboardStateType: TypeAlias = Array[c_uint8] -keyboard_state_type: _KeyboardStateType -GetKeyboardState: Callable[[_KeyboardStateType], BOOL] -GetKeyNameText: Callable[[c_long, LPWSTR, c_int], c_int] -MapVirtualKey: Callable[[c_uint, c_uint], c_uint] -ToUnicode: Callable[[c_uint, c_uint, _KeyboardStateType, LPWSTR, c_int, c_uint], c_int] -SendInput: Callable[[c_uint, _POINTER_INPUT, c_int], c_uint] -MAPVK_VK_TO_CHAR: Literal[2] -MAPVK_VK_TO_VSC: Literal[0] -MAPVK_VSC_TO_VK: Literal[1] -MAPVK_VK_TO_VSC_EX: Literal[4] -MAPVK_VSC_TO_VK_EX: Literal[3] -VkKeyScan: Callable[[WCHAR], c_short] -LLKHF_INJECTED: Literal[16] - -WM_KEYDOWN: Literal[256] -WM_KEYUP: Literal[257] -WM_SYSKEYDOWN: Literal[260] -WM_SYSKEYUP: Literal[261] -keyboard_event_types: dict[int, str] -official_virtual_keys: dict[ - int, - tuple[Literal["control-break processing"], Literal[False]] - | tuple[Literal["backspace"], Literal[False]] - | tuple[Literal["tab"], Literal[False]] - | tuple[Literal["clear"], Literal[False]] - | tuple[Literal["enter"], Literal[False]] - | tuple[Literal["shift"], Literal[False]] - | tuple[Literal["ctrl"], Literal[False]] - | tuple[Literal["alt"], Literal[False]] - | tuple[Literal["pause"], Literal[False]] - | tuple[Literal["caps lock"], Literal[False]] - | tuple[Literal["ime kana mode"], Literal[False]] - | tuple[Literal["ime hanguel mode"], Literal[False]] - | tuple[Literal["ime hangul mode"], Literal[False]] - | tuple[Literal["ime junja mode"], Literal[False]] - | tuple[Literal["ime final mode"], Literal[False]] - | tuple[Literal["ime hanja mode"], Literal[False]] - | tuple[Literal["ime kanji mode"], Literal[False]] - | tuple[Literal["esc"], Literal[False]] - | tuple[Literal["ime convert"], Literal[False]] - | tuple[Literal["ime nonconvert"], Literal[False]] - | tuple[Literal["ime accept"], Literal[False]] - | tuple[Literal["ime mode change request"], Literal[False]] - | tuple[Literal["spacebar"], Literal[False]] - | tuple[Literal["page up"], Literal[False]] - | tuple[Literal["page down"], Literal[False]] - | tuple[Literal["end"], Literal[False]] - | tuple[Literal["home"], Literal[False]] - | tuple[Literal["left"], Literal[False]] - | tuple[Literal["up"], Literal[False]] - | tuple[Literal["right"], Literal[False]] - | tuple[Literal["down"], Literal[False]] - | tuple[Literal["select"], Literal[False]] - | tuple[Literal["print"], Literal[False]] - | tuple[Literal["execute"], Literal[False]] - | tuple[Literal["print screen"], Literal[False]] - | tuple[Literal["insert"], Literal[False]] - | tuple[Literal["delete"], Literal[False]] - | tuple[Literal["help"], Literal[False]] - | tuple[Literal["0"], Literal[False]] - | tuple[Literal["1"], Literal[False]] - | tuple[Literal["2"], Literal[False]] - | tuple[Literal["3"], Literal[False]] - | tuple[Literal["4"], Literal[False]] - | tuple[Literal["5"], Literal[False]] - | tuple[Literal["6"], Literal[False]] - | tuple[Literal["7"], Literal[False]] - | tuple[Literal["8"], Literal[False]] - | tuple[Literal["9"], Literal[False]] - | tuple[Literal["a"], Literal[False]] - | tuple[Literal["b"], Literal[False]] - | tuple[Literal["c"], Literal[False]] - | tuple[Literal["d"], Literal[False]] - | tuple[Literal["e"], Literal[False]] - | tuple[Literal["f"], Literal[False]] - | tuple[Literal["g"], Literal[False]] - | tuple[Literal["h"], Literal[False]] - | tuple[Literal["i"], Literal[False]] - | tuple[Literal["j"], Literal[False]] - | tuple[Literal["k"], Literal[False]] - | tuple[Literal["l"], Literal[False]] - | tuple[Literal["m"], Literal[False]] - | tuple[Literal["n"], Literal[False]] - | tuple[Literal["o"], Literal[False]] - | tuple[Literal["p"], Literal[False]], -] -tables_lock: Lock -to_name: defaultdict[tuple[int, int, int, str | int | tuple[str, ...]] | tuple[int, int, int, int, int], list[str]] -from_name: defaultdict[str, list[tuple[int, tuple[str | int | tuple[str, ...], ...]]]] -scan_code_to_vk: dict[int, int] -distinct_modifiers: list[ - tuple[()] - | tuple[Literal["shift"]] - | tuple[Literal["alt gr"]] - | tuple[Literal["num lock"]] - | tuple[Literal["shift"], Literal["num lock"]] - | tuple[Literal["caps lock"]] - | tuple[Literal["shift"], Literal["caps lock"]] - | tuple[Literal["alt gr"], Literal["num lock"]] -] -name_buffer: Array[c_wchar] -unicode_buffer: Array[c_wchar] -keyboard_state: _KeyboardStateType - - -def get_event_names( - scan_code: int, vk: int, is_extended: Literal[0, 1], modifiers: Sequence[str] -) -> Generator[str, None, None]: ... - - -init: Callable[[], None] -keypad_keys: list[ - tuple[Literal[126], Literal[194], Literal[0]] - | tuple[Literal[28], Literal[13], Literal[1]] - | tuple[Literal[53], Literal[111], Literal[1]] - | tuple[Literal[55], Literal[106], Literal[0]] - | tuple[Literal[69], Literal[144], Literal[1]] - | tuple[Literal[71], Literal[103], Literal[0]] - | tuple[Literal[71], Literal[36], Literal[0]] - | tuple[Literal[72], Literal[104], Literal[0]] - | tuple[Literal[72], Literal[38], Literal[0]] - | tuple[Literal[73], Literal[105], Literal[0]] - | tuple[Literal[73], Literal[33], Literal[0]] - | tuple[Literal[74], Literal[109], Literal[0]] - | tuple[Literal[75], Literal[100], Literal[0]] - | tuple[Literal[75], Literal[37], Literal[0]] - | tuple[Literal[76], Literal[101], Literal[0]] - | tuple[Literal[76], Literal[12], Literal[0]] - | tuple[Literal[77], Literal[102], Literal[0]] - | tuple[Literal[77], Literal[39], Literal[0]] - | tuple[Literal[78], Literal[107], Literal[0]] - | tuple[Literal[79], Literal[35], Literal[0]] - | tuple[Literal[79], Literal[97], Literal[0]] - | tuple[Literal[80], Literal[40], Literal[0]] - | tuple[Literal[80], Literal[98], Literal[0]] - | tuple[Literal[81], Literal[34], Literal[0]] - | tuple[Literal[81], Literal[99], Literal[0]] - | tuple[Literal[82], Literal[45], Literal[0]] - | tuple[Literal[82], Literal[96], Literal[0]] - | tuple[Literal[83], Literal[110], Literal[0]] - | tuple[Literal[83], Literal[46], Literal[0]] -] -shift_is_pressed: bool -altgr_is_pressed: bool -ignore_next_right_alt: bool -shift_vks: set[int] - - -def prepare_intercept(callback: Callable[[KeyboardEvent], bool]) -> None: ... -def listen(callback: Callable[[KeyboardEvent], bool]) -> None: ... -def map_name(name: str) -> Generator[tuple[int, str | int | tuple[str, ...]], None, None]: ... -def press(code: int) -> None: ... -def release(code: int) -> None: ... -def type_unicode(character: str) -> None: ... diff --git a/typings/keyboard/_winmouse.pyi b/typings/keyboard/_winmouse.pyi deleted file mode 100644 index d9b5fc7b..00000000 --- a/typings/keyboard/_winmouse.pyi +++ /dev/null @@ -1,118 +0,0 @@ -import sys -from collections.abc import Callable -from ctypes import (Structure, c_char as c_char, c_int, c_int32, c_long, c_short as c_short, c_uint as c_uint, - c_uint8 as c_uint8, c_uint32 as c_uint32) -from ctypes.wintypes import BOOL, DWORD, HHOOK, LPARAM, LPMSG as LPMSG, LPWSTR as LPWSTR, WCHAR as WCHAR, WPARAM -from queue import Queue - -from _typeshed import Incomplete -from keyboard._mouse_event import (DOUBLE as DOUBLE, DOWN as DOWN, HORIZONTAL as HORIZONTAL, LEFT as LEFT, - MIDDLE as MIDDLE, RIGHT as RIGHT, UP as UP, VERTICAL as VERTICAL, WHEEL as WHEEL, - X2 as X2, ButtonEvent as ButtonEvent, MoveEvent as MoveEvent, - WheelEvent as WheelEvent, X as X, _MouseButton) -from typing_extensions import Literal, TypeAlias - -if sys.platform == "win32": - from ctypes import WinDLL - - user32: WinDLL - - -class MSLLHOOKSTRUCT(Structure): - x: c_long - y: c_long - data: c_int32 - reserved: c_int32 - flags: DWORD - time: c_int - - -_POINTER_MSLLHOOKSTRUCT: TypeAlias = Incomplete # POINTER(MSLLHOOKSTRUCT) -_LRESULT: TypeAlias = Incomplete -LowLevelMouseProc: TypeAlias = Callable[[Callable[[c_int, WPARAM, LPARAM], c_int]], _POINTER_MSLLHOOKSTRUCT] -SetWindowsHookEx: Callable[[c_int, LowLevelMouseProc, c_int, c_int], HHOOK] -CallNextHookEx: Callable[[c_int, c_int, WPARAM, LPARAM], c_int] -UnhookWindowsHookEx: Callable[[HHOOK], BOOL] -GetMessage: Callable[[LPMSG, c_int, c_int, c_int], BOOL] -TranslateMessage: Callable[[LPMSG], BOOL] -DispatchMessage: Callable[[LPMSG], _LRESULT] -WM_MOUSEMOVE: Literal[512] -WM_LBUTTONDOWN: Literal[513] -WM_LBUTTONUP: Literal[514] -WM_LBUTTONDBLCLK: Literal[515] -WM_RBUTTONDOWN: Literal[516] -WM_RBUTTONUP: Literal[517] -WM_RBUTTONDBLCLK: Literal[518] -WM_MBUTTONDOWN: Literal[519] -WM_MBUTTONUP: Literal[520] -WM_MBUTTONDBLCLK: Literal[521] -WM_MOUSEWHEEL: Literal[522] -WM_XBUTTONDOWN: Literal[523] -WM_XBUTTONUP: Literal[524] -WM_XBUTTONDBLCLK: Literal[525] -WM_NCXBUTTONDOWN: Literal[171] -WM_NCXBUTTONUP: Literal[172] -WM_NCXBUTTONDBLCLK: Literal[173] -WM_MOUSEHWHEEL: Literal[526] -buttons_by_wm_code: dict[ - int, - tuple[Literal["down"], Literal["left"]] - | tuple[Literal["up"], Literal["left"]] - | tuple[Literal["double"], Literal["left"]] - | tuple[Literal["down"], Literal["right"]] - | tuple[Literal["up"], Literal["right"]] - | tuple[Literal["double"], Literal["right"]] - | tuple[Literal["down"], Literal["middle"]] - | tuple[Literal["up"], Literal["middle"]] - | tuple[Literal["double"], Literal["middle"]] - | tuple[Literal["down"], Literal["x"]] - | tuple[Literal["up"], Literal["x"]] - | tuple[Literal["double"], Literal["x"]], -] -MOUSEEVENTF_ABSOLUTE: Literal[32768] -MOUSEEVENTF_MOVE: Literal[1] -MOUSEEVENTF_WHEEL: Literal[2048] -MOUSEEVENTF_HWHEEL: Literal[4096] -MOUSEEVENTF_LEFTDOWN: Literal[2] -MOUSEEVENTF_LEFTUP: Literal[4] -MOUSEEVENTF_RIGHTDOWN: Literal[8] -MOUSEEVENTF_RIGHTUP: Literal[16] -MOUSEEVENTF_MIDDLEDOWN: Literal[32] -MOUSEEVENTF_MIDDLEUP: Literal[64] -MOUSEEVENTF_XDOWN: Literal[128] -MOUSEEVENTF_XUP: Literal[256] -simulated_mouse_codes: dict[ - tuple[Literal["wheel"], Literal["horizontal"]] - | tuple[Literal["wheel"], Literal["vertical"]] - | tuple[Literal["down"], Literal["left"]] - | tuple[Literal["up"], Literal["left"]] - | tuple[Literal["down"], Literal["right"]] - | tuple[Literal["up"], Literal["right"]] - | tuple[Literal["down"], Literal["middle"]] - | tuple[Literal["up"], Literal["middle"]] - | tuple[Literal["down"], Literal["x"]] - | tuple[Literal["up"], Literal["x"]], - int, -] -NULL: c_long -WHEEL_DELTA: Literal[120] -init: Callable[[], None] - - -def listen(queue: Queue[MoveEvent | WheelEvent | ButtonEvent]): - ... - - -def press(button: _MouseButton = ...) -> None: ... -def release(button: _MouseButton = ...) -> None: ... -def wheel(delta: int = ...) -> None: ... -def move_to(x: int | c_long, y: int | c_long) -> None: ... -def move_relative(x: int | c_long, y: int | c_long) -> None: ... - - -class POINT(Structure): - x: c_long - y: c_long - - -def get_position() -> tuple[c_long, c_long]: ... diff --git a/typings/keyboard/mouse.pyi b/typings/keyboard/mouse.pyi deleted file mode 100644 index 1de736be..00000000 --- a/typings/keyboard/mouse.pyi +++ /dev/null @@ -1,80 +0,0 @@ -import sys -from collections.abc import Callable, Sequence -from ctypes import c_long -from typing import TypeVar - -from keyboard._generic import GenericListener as _GenericListener -from keyboard._mouse_event import (DOUBLE as DOUBLE, DOWN as DOWN, LEFT as LEFT, MIDDLE as MIDDLE, RIGHT as RIGHT, - UP as UP, X2 as X2, ButtonEvent as ButtonEvent, MoveEvent as MoveEvent, - WheelEvent as WheelEvent, X as X, _MouseButton, _MouseEvent) -from typing_extensions import Literal, TypeAlias - -# Can't use ParamSpecArgs on `args`, only on `*args` -# _P = ParamSpec("_P") -_P: TypeAlias = tuple[object, ...] -_Callback: TypeAlias = Callable[[ButtonEvent | WheelEvent | MoveEvent], bool | None] - - -class _MouseListener(_GenericListener): - def init(self) -> None: ... - - def pre_process_event( - self, event: ButtonEvent | MoveEvent | WheelEvent - ) -> Literal[True]: ... - def listen(self) -> None: ... - - -def is_pressed(button: _MouseButton = ...): - ... - - -def press(button: _MouseButton = ...) -> None: ... -def release(button: _MouseButton = ...) -> None: ... -def click(button: _MouseButton = ...) -> None: ... -def double_click(button: _MouseButton = ...) -> None: ... -def right_click() -> None: ... -def wheel(delta: int = ...) -> None: ... -def move(x: int | c_long, y: int | c_long, absolute: bool = ..., duration: float = ...) -> None: ... -def drag(start_x: int, start_y: int, end_x: int, end_y: int, absolute: bool = ..., duration: float = ...) -> None: ... - - -def on_button( - callback: Callable[..., None], - args: _P = ..., - buttons: list[_MouseButton] | tuple[_MouseButton, ...] | _MouseButton = ..., - types: list[_MouseEvent] | tuple[_MouseEvent, ...] | _MouseEvent = ..., -) -> _Callback: ... -def on_click(callback: Callable[..., None], args: _P = ...) -> _Callback: ... -def on_double_click(callback: Callable[..., None], args: _P = ...) -> _Callback: ... -def on_right_click(callback: Callable[..., None], args: _P = ...) -> _Callback: ... -def on_middle_click(callback: Callable[..., None], args: _P = ...) -> _Callback: ... -def wait(button: _MouseButton = ..., target_types: tuple[_MouseEvent] = ...) -> None: ... - - -if sys.platform == "win32": - def get_position() -> tuple[c_long, c_long]: ... - -else: - def get_position() -> tuple[int, int]: ... - - -def hook(callback: _Callback) -> _Callback: ... -def unhook(callback: _Callback) -> None: ... -def unhook_all() -> None: ... - - -def record(button: _MouseButton = ..., target_types: tuple[_MouseEvent] - = ...) -> ButtonEvent | WheelEvent | MoveEvent: ... - - -def play( - events: Sequence[ButtonEvent | WheelEvent | MoveEvent], - speed_factor: float = ..., - include_clicks: bool = ..., - include_moves: bool = ..., - include_wheel: bool = ..., -) -> None: ... - - -replay = play -hold = press diff --git a/typings/pyautogui/__init__.pyi b/typings/pyautogui/__init__.pyi deleted file mode 100644 index 0fe65284..00000000 --- a/typings/pyautogui/__init__.pyi +++ /dev/null @@ -1,298 +0,0 @@ -import collections.abc -from collections.abc import Callable, Generator -from datetime import datetime -from typing import NamedTuple, TypeVar, overload - -from _typeshed import Incomplete -from typing_extensions import ParamSpec - -# from pyscreeze import Box - - -class PyAutoGUIException(Exception): - ... - - -class FailSafeException(PyAutoGUIException): - ... - - -class ImageNotFoundException(PyAutoGUIException): - ... - - -collectionsSequence = collections.abc.Sequence - -_P = ParamSpec("_P") -_R = TypeVar("_R") - -# TODO: Complete types with pyscreeze once we can import non-types dependencies -# See: https://github.com/python/typeshed/issues/5768 - - -class _Box(NamedTuple): - left: int - top: int - width: int - height: int - - -def raisePyAutoGUIImageNotFoundException(wrappedFunction: Callable[_P, _R]) -> Callable[_P, _R]: ... -def locate(*args: Incomplete, **kwargs: Incomplete) -> _Box | None: ... -def locateAll(*args: Incomplete, **kwargs: Incomplete) -> Generator[_Box, None, None]: ... -def locateAllOnScreen(*args: Incomplete, **kwargs: Incomplete) -> Generator[_Box, None, None]: ... -def locateCenterOnScreen(*args: Incomplete, **kwargs: Incomplete) -> Point | None: ... -def locateOnScreen(*args: Incomplete, **kwargs: Incomplete) -> _Box | None: ... -def locateOnWindow(*args: Incomplete, **kwargs: Incomplete) -> _Box | None: ... -def mouseInfo() -> None: ... -def useImageNotFoundException(value: bool | None = ...) -> None: ... - - -KEY_NAMES: list[str] -KEYBOARD_KEYS: list[str] -LEFT: str -MIDDLE: str -RIGHT: str -PRIMARY: str -SECONDARY: str -QWERTY: str -QWERTZ: str - - -def isShiftCharacter(character: str) -> bool: ... - - -MINIMUM_DURATION: float -MINIMUM_SLEEP: float -PAUSE: float -DARWIN_CATCH_UP_TIME: float -FAILSAFE: bool -FAILSAFE_POINTS: list[tuple[int, int]] -LOG_SCREENSHOTS: bool -LOG_SCREENSHOTS_LIMIT: int -G_LOG_SCREENSHOTS_FILENAMES: list[str] - - -class Point(NamedTuple): - x: float - y: float - - -class Size(NamedTuple): - width: int - height: int - - -def getPointOnLine(x1: float, y1: float, x2: float, y2: float, n: float) -> tuple[float, float]: ... -def linear(n: float) -> float: ... -def position(x: int | None = ..., y: int | None = ...) -> Point: ... -def size() -> Size: ... -@overload -def onScreen(xy: tuple[float, float]) -> bool: ... -@overload -def onScreen(x: float, y: float) -> bool: ... - - -def mouseDown( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - button: str = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def mouseUp( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - button: str = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def click( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - clicks: int = ..., - interval: float = ..., - button: str = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def leftClick( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - interval: float = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def rightClick( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - interval: float = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def middleClick( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - interval: float = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def doubleClick( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - interval: float = ..., - button: str = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def tripleClick( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - interval: float = ..., - button: str = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def scroll( - clicks: float, - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def hscroll( - clicks: float, - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def vscroll( - clicks: float, - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., -) -> None: ... - - -def moveTo( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool = ..., - _pause: bool = ..., -) -> None: ... - - -def moveRel( - xOffset: float | collectionsSequence[int] | None = ..., - yOffset: float | collectionsSequence[int] | None = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - logScreenshot: bool = ..., - _pause: bool = ..., -) -> None: ... - - -move = moveRel - - -def dragTo( - x: float | collectionsSequence[int] | None = ..., - y: float | collectionsSequence[int] | None = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - button: str = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., - mouseDownUp: bool = ..., -) -> None: ... - - -def dragRel( - xOffset: float | collectionsSequence[int] = ..., - yOffset: float | collectionsSequence[int] = ..., - duration: float = ..., - tween: Callable[[float], float] = ..., - button: str = ..., - logScreenshot: bool | None = ..., - _pause: bool = ..., - mouseDownUp: bool = ..., -) -> None: ... - - -drag = dragRel - - -def isValidKey(key: str) -> bool: ... -def keyDown(key: str, logScreenshot: bool | None = ..., _pause: bool = ...) -> None: ... -def keyUp(key: str, logScreenshot: bool | None = ..., _pause: bool = ...) -> None: ... - - -def press(keys: str | list[str], presses: int = ..., interval: float = ..., - logScreenshot: bool | None = ..., _pause: bool = ...) -> None: ... - - -def hold(keys: str | list[str], - logScreenshot: bool | None = ..., - _pause: bool = ...) -> Generator[None, - None, - None]: ... - - -def typewrite( - message: str | list[str], - interval: float = ..., - logScreenshot: bool | None = ..., - _pause: bool = ...) -> None: ... - - -write = typewrite - - -def hotkey(*args: str, **kwargs: float | bool | None) -> None: ... -def failSafeCheck() -> None: ... -def displayMousePosition(xOffset: float = ..., yOffset: float = ...) -> None: ... -def sleep(seconds: int) -> None: ... -def countdown(seconds: int) -> None: ... -def run(commandStr: str, _ssCount: list[int] | None = ...) -> None: ... -def printInfo(dontPrint: bool = ...) -> str: ... -def getInfo() -> tuple[str, str, str, str, Size, datetime]: ... diff --git a/typings/pyautogui/_pyautogui_osx.pyi b/typings/pyautogui/_pyautogui_osx.pyi deleted file mode 100644 index 569791ac..00000000 --- a/typings/pyautogui/_pyautogui_osx.pyi +++ /dev/null @@ -1,2 +0,0 @@ -keyboardMapping: dict[str, int] -special_key_translate_table: dict[str, int] diff --git a/typings/pyautogui/_pyautogui_win.pyi b/typings/pyautogui/_pyautogui_win.pyi deleted file mode 100644 index 738766ce..00000000 --- a/typings/pyautogui/_pyautogui_win.pyi +++ /dev/null @@ -1,39 +0,0 @@ -import ctypes - -MOUSEEVENTF_MOVE: int -MOUSEEVENTF_LEFTDOWN: int -MOUSEEVENTF_LEFTUP: int -MOUSEEVENTF_LEFTCLICK: int -MOUSEEVENTF_RIGHTDOWN: int -MOUSEEVENTF_RIGHTUP: int -MOUSEEVENTF_RIGHTCLICK: int -MOUSEEVENTF_MIDDLEDOWN: int -MOUSEEVENTF_MIDDLEUP: int -MOUSEEVENTF_MIDDLECLICK: int -MOUSEEVENTF_ABSOLUTE: int -MOUSEEVENTF_WHEEL: int -MOUSEEVENTF_HWHEEL: int -KEYEVENTF_KEYDOWN: int -KEYEVENTF_KEYUP: int -INPUT_MOUSE: int -INPUT_KEYBOARD: int - - -class MOUSEINPUT(ctypes.Structure): - ... - - -class KEYBDINPUT(ctypes.Structure): - ... - - -class HARDWAREINPUT(ctypes.Structure): - ... - - -class INPUT(ctypes.Structure): - class _I(ctypes.Union): - ... - - -keyboardMapping: dict[str, int] diff --git a/typings/pyautogui/_pyautogui_x11.pyi b/typings/pyautogui/_pyautogui_x11.pyi deleted file mode 100644 index 24d90ecc..00000000 --- a/typings/pyautogui/_pyautogui_x11.pyi +++ /dev/null @@ -1,2 +0,0 @@ -BUTTON_NAME_MAPPING: dict[str | int, int] -keyboardMapping: dict[str, int] diff --git a/typings/pyi_splash/__init__.pyi b/typings/pyi_splash/__init__.pyi new file mode 100644 index 00000000..b7a573d4 --- /dev/null +++ b/typings/pyi_splash/__init__.pyi @@ -0,0 +1,14 @@ +# Referenced in: https://pyinstaller.org/en/stable/advanced-topics.html#module-pyi_splash +# https://github.com/pyinstaller/pyinstaller/blob/develop/PyInstaller/fake-modules/pyi_splash.py +from typing_extensions import Literal + +__all__ = ["CLOSE_CONNECTION", "FLUSH_CHARACTER", "is_alive", "close", "update_text"] + + +def is_alive() -> bool: ... +def update_text(msg: str) -> None: ... +def close() -> None: ... + + +CLOSE_CONNECTION: Literal[b"\u0004"] = ... +FLUSH_CHARACTER: Literal[b"\r"] = ... From 3898dea29e2aaf7994dab0545652d370466ad054 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 7 Sep 2022 21:01:50 -0400 Subject: [PATCH 3/5] Updated some code --- res/design.ui | 6 -- res/settings.ui | 61 ++++++++++++------- res/update_checker.ui | 6 -- src/AutoSplit.py | 27 ++++---- src/capture_method/BitBltCaptureMethod.py | 18 +++--- .../{interface.py => CaptureMethodBase.py} | 4 +- .../VideoCaptureDeviceCaptureMethod.py | 9 +-- .../WindowsGraphicsCaptureMethod.py | 6 +- src/capture_method/__init__.py | 37 +++++------ src/hotkeys.py | 2 +- src/menu_bar.py | 31 +++++++--- src/region_selection.py | 3 +- src/utils.py | 10 +-- 13 files changed, 119 insertions(+), 101 deletions(-) rename src/capture_method/{interface.py => CaptureMethodBase.py} (94%) diff --git a/res/design.ui b/res/design.ui index a34632df..03aa78f7 100644 --- a/res/design.ui +++ b/res/design.ui @@ -10,12 +10,6 @@ 424 - - - 0 - 0 - - 777 diff --git a/res/settings.ui b/res/settings.ui index 4f5cc6eb..ecc0a9d7 100644 --- a/res/settings.ui +++ b/res/settings.ui @@ -1,7 +1,7 @@ - DialogSettings - + SettingsWidget + 0 @@ -10,12 +10,6 @@ 661 - - - 0 - 0 - - 291 @@ -40,12 +34,6 @@ :/resources/icon.ico:/resources/icon.ico - - false - - - false - @@ -352,7 +340,7 @@ It is highly recommended to NOT use pHash if you use masked images. It is very i 6 - 193 + 190 261 61 @@ -364,7 +352,10 @@ It is highly recommended to NOT use pHash if you use masked images. It is very i - <html><head/><body><p>Custom image settings and flags are set in the <br></br> image file name. These will override the default <br></br> values. View the <a href="https://github.com/{GITHUB_REPOSITORY}#readme"><span style=" text-decoration: underline; color:#0000ff;">README</span></a> for full details on all <br></br> available custom image settings.</p></body></html> + <html><head/><body><p>Image settings and flags can be set per image through the image file name. These will override the default values. View the <a href="https://github.com/{GITHUB_REPOSITORY}#readme"><span style="text-decoration: underline; color:#0000ff;">README</span></a> for full details on all available custom image settings.</p></body></html> + + + true @@ -399,6 +390,34 @@ It is highly recommended to NOT use pHash if you use masked images. It is very i 999999999 + + + + 140 + 210 + 71 + 31 + + + + + 8 + true + + + + README + + + + 0 + 0 + + + + This is a workaround because custom_image_settings_info_label simply will not open links with a left click no matter what we tried. + + @@ -714,11 +733,11 @@ reset image - set_split_hotkey_button - set_reset_hotkey_button - set_undo_split_hotkey_button - set_skip_split_hotkey_button - set_pause_hotkey_button + split_input + reset_input + undo_split_input + skip_split_input + pause_input fps_limit_spinbox live_capture_region_checkbox capture_method_combobox diff --git a/res/update_checker.ui b/res/update_checker.ui index 53567fc1..75fbc852 100644 --- a/res/update_checker.ui +++ b/res/update_checker.ui @@ -13,12 +13,6 @@ 133 - - - 0 - 0 - - 313 diff --git a/src/AutoSplit.py b/src/AutoSplit.py index 0a98c6f8..670e5a0b 100644 --- a/src/AutoSplit.py +++ b/src/AutoSplit.py @@ -6,7 +6,6 @@ import os import signal import sys -from collections.abc import Callable from time import time from types import FunctionType @@ -21,7 +20,7 @@ import user_profile from AutoControlledWorker import AutoControlledWorker from AutoSplitImage import COMPARISON_RESIZE, START_KEYWORD, AutoSplitImage, ImageType -from capture_method import CaptureMethodEnum, CaptureMethodInterface +from capture_method import CaptureMethodBase, CaptureMethodEnum from gen import about, design, settings, update_checker from hotkeys import HOTKEYS, after_setting_hotkey, send_command from menu_bar import (about_qt, about_qt_for_python, check_for_updates, get_default_settings_from_ui, open_about, @@ -64,15 +63,7 @@ class AutoSplit(QMainWindow, design.Ui_MainWindow): AboutWidget: about.Ui_AboutAutoSplitWidget | None = None UpdateCheckerWidget: update_checker.Ui_UpdateChecker | None = None CheckForUpdatesThread: QtCore.QThread | None = None - SettingsWidget: settings.Ui_DialogSettings | None = None - - # hotkeys need to be initialized to be passed as thread arguments in hotkeys.py - # and for type safety in both hotkeys.py and settings_file.py - split_hotkey: Callable[[], None] | None = None - reset_hotkey: Callable[[], None] | None = None - skip_split_hotkey: Callable[[], None] | None = None - undo_split_hotkey: Callable[[], None] | None = None - pause_hotkey: Callable[[], None] | None = None + SettingsWidget: settings.Ui_SettingsWidget | None = None # Initialize a few attributes hwnd = 0 @@ -82,7 +73,7 @@ class AutoSplit(QMainWindow, design.Ui_MainWindow): split_image_number = 0 split_images_and_loop_number: list[tuple[AutoSplitImage, int]] = [] split_groups: list[list[int]] = [] - capture_method = CaptureMethodInterface() + capture_method = CaptureMethodBase() # Last loaded settings empty and last successful loaded settings file path to None until we try to load them last_loaded_settings = DEFAULT_PROFILE @@ -123,6 +114,10 @@ def __init__(self, parent: QWidget | None = None): # pylint: disable=too-many-s self.width_spinbox.setFrame(False) self.height_spinbox.setFrame(False) + # Hotkeys need to be initialized to be passed as thread arguments in hotkeys.py + for hotkey in HOTKEYS: + setattr(self, f"{hotkey}_hotkey", None) + # Get default values defined in SettingsDialog self.settings_dict = get_default_settings_from_ui(self) user_profile.load_check_for_updates_on_open(self) @@ -211,7 +206,7 @@ def __init__(self, parent: QWidget | None = None): # pylint: disable=too-many-s self.show() try: - import pyi_splash # type: ignore # pylint: disable=import-outside-toplevel + import pyi_splash # pyright: ignore[reportMissingModuleSource] # pylint: disable=import-outside-toplevel pyi_splash.close() except ModuleNotFoundError: pass @@ -450,7 +445,7 @@ def skip_split(self, navigate_image_only: bool = False): # or Splitting/skipping when there are no images left if self.start_auto_splitter_button.text() == START_AUTO_SPLITTER_TEXT \ or "Delayed Split" in self.current_split_image.text() \ - or (not self.skip_split_button.isEnabled() and not self.is_auto_controlled) \ + or not (self.skip_split_button.isEnabled() or self.is_auto_controlled or navigate_image_only) \ or self.__is_current_split_out_of_range(): return @@ -795,6 +790,10 @@ def __reset_if_should(self, capture: cv2.Mat | None): self.reset() else: self.table_reset_image_live_label.setText("disabled") + else: + self.table_reset_image_live_label.setText("N/A") + self.table_reset_image_threshold_label.setText("N/A") + self.table_reset_image_highest_label.setText("N/A") return self.__check_for_reset_state_update_ui() diff --git a/src/capture_method/BitBltCaptureMethod.py b/src/capture_method/BitBltCaptureMethod.py index 23d2a30f..e7ddabdc 100644 --- a/src/capture_method/BitBltCaptureMethod.py +++ b/src/capture_method/BitBltCaptureMethod.py @@ -11,7 +11,7 @@ import win32ui from win32 import win32gui -from capture_method.interface import CaptureMethodInterface +from capture_method.CaptureMethodBase import CaptureMethodBase from utils import get_window_bounds, is_valid_hwnd if TYPE_CHECKING: @@ -21,7 +21,7 @@ PW_RENDERFULLCONTENT = 0x00000002 -class BitBltCaptureMethod(CaptureMethodInterface): +class BitBltCaptureMethod(CaptureMethodBase): _render_full_content = False def get_frame(self, autosplit: AutoSplit) -> tuple[cv2.Mat | None, bool]: @@ -57,15 +57,11 @@ def get_frame(self, autosplit: AutoSplit) -> tuple[cv2.Mat | None, bool]: image.shape = (selection["height"], selection["width"], 4) except (win32ui.error, pywintypes.error): return None, False - # We already obtained the image, so we can ignore errors during cleanup - try: - dc_object.DeleteDC() - dc_object.DeleteDC() - compatible_dc.DeleteDC() - win32gui.ReleaseDC(hwnd, window_dc) - win32gui.DeleteObject(bitmap.GetHandle()) - except win32ui.error: - pass + # Cleanup DC and handle + dc_object.DeleteDC() + compatible_dc.DeleteDC() + win32gui.ReleaseDC(hwnd, window_dc) + win32gui.DeleteObject(bitmap.GetHandle()) return image, False def recover_window(self, captured_window_title: str, autosplit: AutoSplit): diff --git a/src/capture_method/interface.py b/src/capture_method/CaptureMethodBase.py similarity index 94% rename from src/capture_method/interface.py rename to src/capture_method/CaptureMethodBase.py index 253eb6cc..630ba43d 100644 --- a/src/capture_method/interface.py +++ b/src/capture_method/CaptureMethodBase.py @@ -12,13 +12,13 @@ # pylint: disable=no-self-use,unnecessary-dunder-call -class CaptureMethodInterface(): +class CaptureMethodBase(): def __init__(self, autosplit: AutoSplit | None = None): pass def reinitialize(self, autosplit: AutoSplit): self.close(autosplit) - self.__init__(autosplit) # pylint: disable=unnecessary-dunder-call + self.__init__(autosplit) # pylint: disable=unnecessary-dunder-call # type: ignore[misc] def close(self, autosplit: AutoSplit): pass diff --git a/src/capture_method/VideoCaptureDeviceCaptureMethod.py b/src/capture_method/VideoCaptureDeviceCaptureMethod.py index 612697ff..c96f7f59 100644 --- a/src/capture_method/VideoCaptureDeviceCaptureMethod.py +++ b/src/capture_method/VideoCaptureDeviceCaptureMethod.py @@ -5,7 +5,7 @@ import cv2 -from capture_method.interface import CaptureMethodInterface +from capture_method.CaptureMethodBase import CaptureMethodBase from error_messages import CREATE_NEW_ISSUE_MESSAGE, exception_traceback from utils import is_valid_image @@ -13,7 +13,7 @@ from AutoSplit import AutoSplit -class VideoCaptureDeviceCaptureMethod(CaptureMethodInterface): +class VideoCaptureDeviceCaptureMethod(CaptureMethodBase): capture_device: cv2.VideoCapture capture_thread: Thread | None last_captured_frame: cv2.Mat | None = None @@ -34,12 +34,13 @@ def __read_loop(self, autosplit: AutoSplit): self.last_captured_frame = image if result else None self.is_old_image = False except Exception as exception: # pylint: disable=broad-except # We really want to catch everything here - error = exception + # mypy false positives + error = exception # type: ignore[misc] self.capture_device.release() autosplit.show_error_signal.emit(lambda: exception_traceback( "AutoSplit encountered an unhandled exception while trying to grab a frame and has stopped capture. " + CREATE_NEW_ISSUE_MESSAGE, - error)) + error)) # type: ignore[misc] def __init__(self, autosplit: AutoSplit): super().__init__() diff --git a/src/capture_method/WindowsGraphicsCaptureMethod.py b/src/capture_method/WindowsGraphicsCaptureMethod.py index 8a687078..a0848a29 100644 --- a/src/capture_method/WindowsGraphicsCaptureMethod.py +++ b/src/capture_method/WindowsGraphicsCaptureMethod.py @@ -13,7 +13,7 @@ from winsdk.windows.graphics.imaging import BitmapBufferAccessMode, SoftwareBitmap from winsdk.windows.media.capture import MediaCapture -from capture_method.interface import CaptureMethodInterface +from capture_method.CaptureMethodBase import CaptureMethodBase from utils import WINDOWS_BUILD_NUMBER, is_valid_hwnd if TYPE_CHECKING: @@ -22,7 +22,7 @@ WGC_NO_BORDER_MIN_BUILD = 20348 -class WindowsGraphicsCaptureMethod(CaptureMethodInterface): +class WindowsGraphicsCaptureMethod(CaptureMethodBase): size: SizeInt32 frame_pool: Direct3D11CaptureFramePool | None = None session: GraphicsCaptureSession | None = None @@ -130,7 +130,7 @@ def recover_window(self, captured_window_title: str, autosplit: AutoSplit): autosplit.hwnd = hwnd self.close(autosplit) try: - self.__init__(autosplit) # pylint: disable=unnecessary-dunder-call + self.__init__(autosplit) # pylint: disable=unnecessary-dunder-call # type: ignore[misc] # Unrecordable hwnd found as the game is crashing except OSError as exception: if str(exception).endswith("The parameter is incorrect"): diff --git a/src/capture_method/__init__.py b/src/capture_method/__init__.py index 8a6cd28f..901419fc 100644 --- a/src/capture_method/__init__.py +++ b/src/capture_method/__init__.py @@ -6,14 +6,13 @@ from enum import Enum, EnumMeta, unique from typing import TYPE_CHECKING, TypedDict -import cv2 from pygrabber import dshow_graph from winsdk.windows.media.capture import MediaCapture from capture_method.BitBltCaptureMethod import BitBltCaptureMethod +from capture_method.CaptureMethodBase import CaptureMethodBase from capture_method.DesktopDuplicationCaptureMethod import DesktopDuplicationCaptureMethod from capture_method.ForceFullContentRenderingCaptureMethod import ForceFullContentRenderingCaptureMethod -from capture_method.interface import CaptureMethodInterface from capture_method.VideoCaptureDeviceCaptureMethod import VideoCaptureDeviceCaptureMethod from capture_method.WindowsGraphicsCaptureMethod import WindowsGraphicsCaptureMethod from utils import WINDOWS_BUILD_NUMBER @@ -37,12 +36,12 @@ class CaptureMethodInfo(): name: str short_description: str description: str - implementation: type[CaptureMethodInterface] + implementation: type[CaptureMethodBase] class CaptureMethodMeta(EnumMeta): # Allow checking if simple string is enum - def __contains__(self, other: str): + def __contains__(self, other: str): # type: ignore[override] try: self(other) # pyright: ignore [reportGeneralTypeIssues] pylint: disable=no-value-for-parameter except ValueError: @@ -102,7 +101,7 @@ def __getitem__(self, key: CaptureMethodEnum): name="None", short_description="", description="", - implementation=CaptureMethodInterface + implementation=CaptureMethodBase ) CAPTURE_METHODS = CaptureMethodDict({ @@ -209,19 +208,23 @@ async def get_all_video_capture_devices() -> list[CameraInfo]: named_video_inputs = dshow_graph.FilterGraph().get_input_devices() async def get_camera_info(index: int, device_name: str): - video_capture = cv2.VideoCapture(index) - video_capture.setExceptionMode(True) backend = "" - try: - # https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html#ga023786be1ee68a9105bf2e48c700294d - backend = video_capture.getBackendName() # STS_ASSERT - video_capture.grab() # STS_ERROR - except cv2.error as error: - return CameraInfo(index, device_name, True, backend) \ - if error.code in (cv2.Error.STS_ERROR, cv2.Error.STS_ASSERT) \ - else None - finally: - video_capture.release() + # Probing freezes some devices (like GV-USB2 and AverMedia) if already in use + # https://github.com/Toufool/Auto-Split/issues/169 + # FIXME: Maybe offer the option to the user to obtain more info about their devies? + # Off by default. With a tooltip to explain the risk. + # video_capture = cv2.VideoCapture(index) + # video_capture.setExceptionMode(True) + # try: + # # https://docs.opencv.org/3.4/d4/d15/group__videoio__flags__base.html#ga023786be1ee68a9105bf2e48c700294d + # backend = video_capture.getBackendName() # STS_ASSERT + # video_capture.grab() # STS_ERROR + # except cv2.error as error: + # return CameraInfo(index, device_name, True, backend) \ + # if error.code in (cv2.Error.STS_ERROR, cv2.Error.STS_ASSERT) \ + # else None + # finally: + # video_capture.release() return CameraInfo(index, device_name, False, backend) future = asyncio.gather(*[ diff --git a/src/hotkeys.py b/src/hotkeys.py index a7aff538..fb23600a 100644 --- a/src/hotkeys.py +++ b/src/hotkeys.py @@ -225,7 +225,7 @@ def is_valid_hotkey_name(hotkey_name: str): def set_hotkey(autosplit: AutoSplit, hotkey: Hotkey, preselected_hotkey_name: str = ""): if autosplit.SettingsWidget: # Unfocus all fields - cast(QtWidgets.QDialog, autosplit.SettingsWidget).setFocus() + cast(QtWidgets.QWidget, autosplit.SettingsWidget).setFocus() getattr(autosplit.SettingsWidget, f"set_{hotkey}_hotkey_button").setText(PRESS_A_KEY_TEXT) # Disable some buttons diff --git a/src/menu_bar.py b/src/menu_bar.py index 37dd5602..8cbcb36c 100644 --- a/src/menu_bar.py +++ b/src/menu_bar.py @@ -118,7 +118,7 @@ def get_capture_method_index(capture_method: str | CaptureMethodEnum): return 0 -class __SettingsWidget(QtWidgets.QDialog, settings_ui.Ui_DialogSettings): +class __SettingsWidget(QtWidgets.QWidget, settings_ui.Ui_SettingsWidget): __video_capture_devices: list[CameraInfo] = [] """ Used to temporarily store the existing cameras, @@ -180,10 +180,7 @@ def __set_all_capture_devices(self): else: self.capture_device_combobox.setPlaceholderText("No device found.") - def __init__(self, autosplit: AutoSplit): - super().__init__() - self.setupUi(self) - self.autosplit = autosplit + def __apply_os_specific_ui_fixes(self): # Spinbox frame disappears and reappears on Windows 11. It's much cleaner to just disable them. # Most likely related: https://bugreports.qt.io/browse/QTBUG-95215?jql=labels%20%3D%20Windows11 # Arrow buttons tend to move a lot as well @@ -192,13 +189,27 @@ def __init__(self, autosplit: AutoSplit): self.default_similarity_threshold_spinbox.setFrame(False) self.default_delay_time_spinbox.setFrame(False) self.default_pause_time_spinbox.setFrame(False) - # Don't autofocus any particular field - self.setFocus() + def __set_readme_link(self): self.custom_image_settings_info_label.setText( self.custom_image_settings_info_label .text() .format(GITHUB_REPOSITORY=GITHUB_REPOSITORY)) + # HACK: This is a workaround because custom_image_settings_info_label + # simply will not open links with a left click no matter what we tried. + self.readme_link_button.clicked.connect( + lambda: webbrowser.open(f"https://github.com/{GITHUB_REPOSITORY}#readme")) + self.readme_link_button.setStyleSheet("border: 0px; background-color:rgba(0,0,0,0%);") + + def __init__(self, autosplit: AutoSplit): + super().__init__() + self.setupUi(self) + self.autosplit = autosplit + self.__apply_os_specific_ui_fixes() + self.__set_readme_link() + # Don't autofocus any particular field + self.setFocus() + # region Build the Capture method combobox capture_method_values = CAPTURE_METHODS.values() @@ -292,13 +303,13 @@ def hotkey_connect(hotkey: Hotkey): def open_settings(autosplit: AutoSplit): - if not autosplit.SettingsWidget or cast(QtWidgets.QDialog, autosplit.SettingsWidget).isHidden(): + if not autosplit.SettingsWidget or cast(QtWidgets.QWidget, autosplit.SettingsWidget).isHidden(): autosplit.SettingsWidget = __SettingsWidget(autosplit) def get_default_settings_from_ui(autosplit: AutoSplit): - temp_dialog = QtWidgets.QDialog() - default_settings_dialog = settings_ui.Ui_DialogSettings() + temp_dialog = QtWidgets.QWidget() + default_settings_dialog = settings_ui.Ui_SettingsWidget() default_settings_dialog.setupUi(temp_dialog) default_settings: user_profile.UserProfileDict = { "split_hotkey": default_settings_dialog.split_input.text(), diff --git a/src/region_selection.py b/src/region_selection.py index 4ea266cd..54ef22a6 100644 --- a/src/region_selection.py +++ b/src/region_selection.py @@ -149,7 +149,8 @@ def __get_window_from_point(x: int, y: int): # Want to pull the parent window from the window handle # By using GetAncestor we are able to get the parent window instead # of the owner window. - while win32gui.IsChild(win32gui.GetParent(hwnd), hwnd): + # TODO: Fix stubs, IsChild should return a boolean + while win32gui.IsChild(win32gui.GetParent(hwnd), hwnd): # type: ignore[func-returns-value] hwnd = cast(int, user32.GetAncestor(hwnd, GA_ROOT)) window_text = win32gui.GetWindowText(hwnd) diff --git a/src/utils.py b/src/utils.py index b4147944..014d181d 100644 --- a/src/utils.py +++ b/src/utils.py @@ -46,7 +46,8 @@ def is_valid_hwnd(hwnd: int): if not hwnd: return False if sys.platform == "win32": - return bool(win32gui.IsWindow(hwnd) and win32gui.GetWindowText(hwnd)) + # TODO: Fix stubs, IsWindow should return a boolean + return bool(win32gui.IsWindow(hwnd) and win32gui.GetWindowText(hwnd)) # type: ignore[func-returns-value] return True @@ -102,9 +103,8 @@ def wrapped(*args: Any, **kwargs: Any): """The directory of either AutoSplit.exe or AutoSplit.py""" # Shared strings -# DIRTY_VERSION_EXTENSION = "" -DIRTY_VERSION_EXTENSION = "-" + AUTOSPLIT_BUILD_NUMBER -"""Set DIRTY_VERSION_EXTENSION to an empty string to generate a clean version number""" -AUTOSPLIT_VERSION = "2.0.0-alpha.4" + DIRTY_VERSION_EXTENSION +# Set AUTOSPLIT_BUILD_NUMBER to an empty string to generate a clean version number +# AUTOSPLIT_BUILD_NUMBER = "" # pyright: ignore[reportConstantRedefinition] # noqa: F811 +AUTOSPLIT_VERSION = "2.0.0-alpha.6" + (f"-{AUTOSPLIT_BUILD_NUMBER}" if AUTOSPLIT_BUILD_NUMBER else "") START_AUTO_SPLITTER_TEXT = "Start Auto Splitter" GITHUB_REPOSITORY = AUTOSPLIT_GITHUB_REPOSITORY From 0547be9a143c5297e5b907d4e4bcb5177dd21c6b Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 7 Sep 2022 21:19:38 -0400 Subject: [PATCH 4/5] Fix #47 --- src/user_profile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/user_profile.py b/src/user_profile.py index d7d3e0c8..1679ffd1 100644 --- a/src/user_profile.py +++ b/src/user_profile.py @@ -137,7 +137,8 @@ def __load_settings_from_file(autosplit: AutoSplit, load_settings_file_path: str set_hotkey(autosplit, hotkey, cast(str, autosplit.settings_dict[hotkey_name])) change_capture_method(cast(CaptureMethodEnum, autosplit.settings_dict["capture_method"]), autosplit) - autosplit.capture_method.recover_window(autosplit.settings_dict["captured_window_title"], autosplit) + if autosplit.settings_dict["capture_method"] != CaptureMethodEnum.VIDEO_CAPTURE_DEVICE: + autosplit.capture_method.recover_window(autosplit.settings_dict["captured_window_title"], autosplit) if not autosplit.capture_method.check_selected_region_exists(autosplit): autosplit.live_image.setText("Reload settings after opening" + f'\n"{autosplit.settings_dict["captured_window_title"]}"' From 82776c6fd92ced1e3074215f8fc1e8da9682c8a2 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 7 Sep 2022 21:31:37 -0400 Subject: [PATCH 5/5] Revert mypy stuff --- pyproject.toml | 17 +---------------- scripts/requirements-dev.txt | 2 -- src/capture_method/CaptureMethodBase.py | 2 +- .../VideoCaptureDeviceCaptureMethod.py | 5 ++--- .../WindowsGraphicsCaptureMethod.py | 2 +- src/capture_method/__init__.py | 2 +- src/region_selection.py | 2 +- src/utils.py | 2 +- 8 files changed, 8 insertions(+), 26 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 93dafc06..062ba09c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,20 +11,6 @@ max_line_length = 120 recursive = true aggressive = 3 -# https://mypy.readthedocs.io/en/stable/config_file.html -[tool.mypy] -mypy_path = "$MYPY_CONFIG_FILE_DIR/typings" -show_column_numbers=true -show_error_codes=true -# Not all of our imports are types -ignore_missing_imports=true -disable_error_code=[ - # False positives with PyQt .connect. pylint(no-member) works instead - "union-attr", "attr-defined", - # Doesn't see our QTest overload - "call-arg", "arg-type" - ] - # https://github.com/microsoft/pyright/blob/main/docs/configuration.md#sample-pyprojecttoml-file [tool.pyright] typeCheckingMode = "strict" @@ -34,6 +20,7 @@ reportCallInDefaultInitializer="error" reportMissingSuperCall="none" # False positives on base classes reportPropertyTypeMismatch="error" reportUninitializedInstanceVariable="error" +reportUnnecessaryTypeIgnoreComment="error" # Exclude from scanning when running pyright exclude = [ # Auto generated, produces unecessary `# type: ignore` @@ -45,8 +32,6 @@ ignore = [ "**/*.pyi", ] reportUnusedCallResult="none" -# We need the comment to flag false positives in mypy -reportUnnecessaryTypeIgnoreComment="none" # Type stubs may not be completable reportMissingTypeStubs = "warning" # False positives with TYPE_CHECKING diff --git a/scripts/requirements-dev.txt b/scripts/requirements-dev.txt index 679c21f7..41816245 100644 --- a/scripts/requirements-dev.txt +++ b/scripts/requirements-dev.txt @@ -29,6 +29,4 @@ qt6-applications pywin32-stubs>=1.0.7 # Added error types types-keyboard types-pyautogui -types-requests -types-toml typing-extensions diff --git a/src/capture_method/CaptureMethodBase.py b/src/capture_method/CaptureMethodBase.py index 630ba43d..3f6d1212 100644 --- a/src/capture_method/CaptureMethodBase.py +++ b/src/capture_method/CaptureMethodBase.py @@ -18,7 +18,7 @@ def __init__(self, autosplit: AutoSplit | None = None): def reinitialize(self, autosplit: AutoSplit): self.close(autosplit) - self.__init__(autosplit) # pylint: disable=unnecessary-dunder-call # type: ignore[misc] + self.__init__(autosplit) # pylint: disable=unnecessary-dunder-call def close(self, autosplit: AutoSplit): pass diff --git a/src/capture_method/VideoCaptureDeviceCaptureMethod.py b/src/capture_method/VideoCaptureDeviceCaptureMethod.py index c96f7f59..20e2fb18 100644 --- a/src/capture_method/VideoCaptureDeviceCaptureMethod.py +++ b/src/capture_method/VideoCaptureDeviceCaptureMethod.py @@ -34,13 +34,12 @@ def __read_loop(self, autosplit: AutoSplit): self.last_captured_frame = image if result else None self.is_old_image = False except Exception as exception: # pylint: disable=broad-except # We really want to catch everything here - # mypy false positives - error = exception # type: ignore[misc] + error = exception self.capture_device.release() autosplit.show_error_signal.emit(lambda: exception_traceback( "AutoSplit encountered an unhandled exception while trying to grab a frame and has stopped capture. " + CREATE_NEW_ISSUE_MESSAGE, - error)) # type: ignore[misc] + error)) def __init__(self, autosplit: AutoSplit): super().__init__() diff --git a/src/capture_method/WindowsGraphicsCaptureMethod.py b/src/capture_method/WindowsGraphicsCaptureMethod.py index a0848a29..7fca63d8 100644 --- a/src/capture_method/WindowsGraphicsCaptureMethod.py +++ b/src/capture_method/WindowsGraphicsCaptureMethod.py @@ -130,7 +130,7 @@ def recover_window(self, captured_window_title: str, autosplit: AutoSplit): autosplit.hwnd = hwnd self.close(autosplit) try: - self.__init__(autosplit) # pylint: disable=unnecessary-dunder-call # type: ignore[misc] + self.__init__(autosplit) # pylint: disable=unnecessary-dunder-call # Unrecordable hwnd found as the game is crashing except OSError as exception: if str(exception).endswith("The parameter is incorrect"): diff --git a/src/capture_method/__init__.py b/src/capture_method/__init__.py index 901419fc..2957bef2 100644 --- a/src/capture_method/__init__.py +++ b/src/capture_method/__init__.py @@ -41,7 +41,7 @@ class CaptureMethodInfo(): class CaptureMethodMeta(EnumMeta): # Allow checking if simple string is enum - def __contains__(self, other: str): # type: ignore[override] + def __contains__(self, other: str): try: self(other) # pyright: ignore [reportGeneralTypeIssues] pylint: disable=no-value-for-parameter except ValueError: diff --git a/src/region_selection.py b/src/region_selection.py index 54ef22a6..d943e448 100644 --- a/src/region_selection.py +++ b/src/region_selection.py @@ -150,7 +150,7 @@ def __get_window_from_point(x: int, y: int): # By using GetAncestor we are able to get the parent window instead # of the owner window. # TODO: Fix stubs, IsChild should return a boolean - while win32gui.IsChild(win32gui.GetParent(hwnd), hwnd): # type: ignore[func-returns-value] + while win32gui.IsChild(win32gui.GetParent(hwnd), hwnd): hwnd = cast(int, user32.GetAncestor(hwnd, GA_ROOT)) window_text = win32gui.GetWindowText(hwnd) diff --git a/src/utils.py b/src/utils.py index 014d181d..227c7f43 100644 --- a/src/utils.py +++ b/src/utils.py @@ -47,7 +47,7 @@ def is_valid_hwnd(hwnd: int): return False if sys.platform == "win32": # TODO: Fix stubs, IsWindow should return a boolean - return bool(win32gui.IsWindow(hwnd) and win32gui.GetWindowText(hwnd)) # type: ignore[func-returns-value] + return bool(win32gui.IsWindow(hwnd) and win32gui.GetWindowText(hwnd)) return True