From a098b2819173fb56a6f479df9b29ef76d2b63d56 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sat, 26 Aug 2023 19:18:52 +0200 Subject: [PATCH 1/2] setup pre-commit --- .pre-commit-config.yaml | 331 +++-------------------- CONTRIBUTING.md | 27 ++ pyproject.toml | 93 +++++++ qtpy/Qsci.py | 14 +- qtpy/Qt3DAnimation.py | 12 +- qtpy/Qt3DCore.py | 12 +- qtpy/Qt3DExtras.py | 12 +- qtpy/Qt3DInput.py | 12 +- qtpy/Qt3DLogic.py | 12 +- qtpy/Qt3DRender.py | 12 +- qtpy/QtAxContainer.py | 6 +- qtpy/QtBluetooth.py | 2 +- qtpy/QtCharts.py | 16 +- qtpy/QtConcurrent.py | 6 +- qtpy/QtCore.py | 112 ++++++-- qtpy/QtDBus.py | 6 +- qtpy/QtDataVisualization.py | 10 +- qtpy/QtDesigner.py | 2 +- qtpy/QtGui.py | 162 +++++++---- qtpy/QtLocation.py | 4 +- qtpy/QtMacExtras.py | 10 +- qtpy/QtNetworkAuth.py | 10 +- qtpy/QtNfc.py | 2 +- qtpy/QtOpenGL.py | 35 ++- qtpy/QtOpenGLWidgets.py | 4 +- qtpy/QtPdf.py | 4 +- qtpy/QtPdfWidgets.py | 4 +- qtpy/QtPrintSupport.py | 29 +- qtpy/QtPurchasing.py | 11 +- qtpy/QtQuick.py | 2 +- qtpy/QtQuick3D.py | 2 +- qtpy/QtQuickControls2.py | 6 +- qtpy/QtQuickWidgets.py | 2 +- qtpy/QtScxml.py | 6 +- qtpy/QtSql.py | 14 +- qtpy/QtStateMachine.py | 8 +- qtpy/QtSvgWidgets.py | 4 +- qtpy/QtTest.py | 2 +- qtpy/QtTextToSpeech.py | 4 +- qtpy/QtUiTools.py | 6 +- qtpy/QtWebEngine.py | 7 +- qtpy/QtWebEngineCore.py | 6 +- qtpy/QtWebEngineQuick.py | 9 +- qtpy/QtWebEngineWidgets.py | 53 ++-- qtpy/QtWidgets.py | 161 ++++++++--- qtpy/QtWinExtras.py | 10 +- qtpy/QtX11Extras.py | 10 +- qtpy/QtXmlPatterns.py | 4 +- qtpy/__init__.py | 183 +++++++------ qtpy/_utils.py | 81 ++++-- qtpy/cli.py | 55 ++-- qtpy/compat.py | 127 ++++++--- qtpy/enums_compat.py | 2 +- qtpy/shiboken.py | 8 +- qtpy/sip.py | 6 +- qtpy/tests/conftest.py | 39 +-- qtpy/tests/optional_deps/__init__.py | 15 +- qtpy/tests/optional_deps/optional_dep.py | 1 + qtpy/tests/test_cli.py | 107 +++++--- qtpy/tests/test_compat.py | 19 +- qtpy/tests/test_macos_checks.py | 82 +++--- qtpy/tests/test_main.py | 57 ++-- qtpy/tests/test_missing_optional_deps.py | 4 +- qtpy/tests/test_qdesktopservice_split.py | 5 +- qtpy/tests/test_qt3danimation.py | 1 - qtpy/tests/test_qt3dcore.py | 1 + qtpy/tests/test_qt3drender.py | 1 + qtpy/tests/test_qtbluetooth.py | 1 - qtpy/tests/test_qtcharts.py | 3 +- qtpy/tests/test_qtconcurrent.py | 1 + qtpy/tests/test_qtcore.py | 73 +++-- qtpy/tests/test_qtdbus.py | 2 - qtpy/tests/test_qtdesigner.py | 1 + qtpy/tests/test_qtgui.py | 77 ++++-- qtpy/tests/test_qthelp.py | 3 - qtpy/tests/test_qtlocation.py | 6 +- qtpy/tests/test_qtmacextras.py | 10 +- qtpy/tests/test_qtmultimedia.py | 2 +- qtpy/tests/test_qtmultimediawidgets.py | 2 - qtpy/tests/test_qtnetwork.py | 3 +- qtpy/tests/test_qtopengl.py | 4 - qtpy/tests/test_qtopenglwidgets.py | 3 +- qtpy/tests/test_qtpositioning.py | 1 + qtpy/tests/test_qtqml.py | 3 +- qtpy/tests/test_qtquick.py | 1 - qtpy/tests/test_qtquickwidgets.py | 4 - qtpy/tests/test_qtremoteobjects.py | 1 - qtpy/tests/test_qtsensors.py | 1 - qtpy/tests/test_qtserialport.py | 1 + qtpy/tests/test_qtsql.py | 4 +- qtpy/tests/test_qtsvg.py | 3 +- qtpy/tests/test_qttest.py | 17 +- qtpy/tests/test_qttexttospeech.py | 8 +- qtpy/tests/test_qtwebchannel.py | 4 - qtpy/tests/test_qtwebenginecore.py | 1 - qtpy/tests/test_qtwebenginequick.py | 3 +- qtpy/tests/test_qtwebenginewidgets.py | 8 +- qtpy/tests/test_qtwebsockets.py | 4 - qtpy/tests/test_qtwidgets.py | 131 ++++++--- qtpy/tests/test_qtwinextras.py | 5 +- qtpy/tests/test_qtxml.py | 3 - qtpy/tests/test_qtxmlpatterns.py | 3 +- qtpy/tests/test_uic.py | 62 +++-- qtpy/tests/utils.py | 4 +- qtpy/uic.py | 98 +++---- setup.cfg | 10 +- 106 files changed, 1531 insertions(+), 1122 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c245b946..5df198d3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,297 +1,36 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks - -minimum_pre_commit_version: '2.10.0' - -default_language_version: - python: python3 - -default_stages: [commit] - - repos: -# Meta checks -- repo: meta - hooks: - - id: check-useless-excludes - -# General fixers -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: trailing-whitespace - name: Trim trailing whitespace - exclude_types: [svg] - - id: mixed-line-ending - name: Normalize mixed line endings - args: [--fix=lf] - exclude_types: [batch] - - id: end-of-file-fixer - name: Fix end of files - exclude_types: [svg] - - id: fix-byte-order-marker - name: Remove Unicode BOM - - -# More general fixers -- repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.3.0 - hooks: - - id: remove-tabs - name: Replace tabs with spaces - types: [text] - exclude_types: [svg, batch] - args: [--whitespaces-count, '4'] - - -# Check and fix spelling -- repo: https://github.com/codespell-project/codespell - rev: v2.1.0 - hooks: - # Define separate hooks for checking and correcting spelling errors - # since codespell doesn't print verbose output when writing changes - - id: codespell - name: Check spelling - exclude: '\.codespellrc|\.gitattributes' - - id: codespell - name: Fix spelling - exclude: '\.codespellrc|\.gitattributes' - args: [--write-changes] - -# Lint and fix CSS with stylelint -- repo: https://github.com/awebdeveloper/pre-commit-stylelint - rev: '0.0.2' - hooks: - - id: stylelint - name: Lint and fix CSS (stylelint) - args: [--fix, --color, --maxWarnings, '0'] - additional_dependencies: - - 'stylelint@14.9.1' - - 'stylelint-config-standard@26.0.0' - - 'stylelint-csstree-validator@2.0.0' - - 'stylelint-declaration-block-no-ignored-properties@2.5.0' - - 'stylelint-no-unsupported-browser-features@5.0.3' - - 'stylelint-order@5.0.0' - -# Lint CSS with CSSlint -- repo: https://github.com/pre-commit/mirrors-csslint - rev: v1.0.5 - hooks: - - id: csslint - name: Check CSS (CSSlint) - -# Pretty-format INI -- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.4.0 - hooks: - - id: pretty-format-ini - name: Format INI - types: [ini] - args: [--autofix] - -# Check JSON -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: check-json - name: Check JSON - -# Add doctoc to Markdown -- repo: https://github.com/thlorenz/doctoc - rev: v2.2.0 - hooks: - - id: doctoc - name: Generate Markdown ToCs - args: [--maxlevel, '3', --update-only] - -# Lint Markdown -- repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.32.1 - hooks: - - id: markdownlint - name: Lint Markdown - args: [--fix] - -# Format packaging -- repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.17.0 - hooks: - - id: setup-cfg-fmt - name: Format packaging with setup-cfg-fmt - -# Check packaging -- repo: https://github.com/regebro/pyroma - rev: '3.2' - hooks: - - id: pyroma - -# Fix Python -- repo: https://github.com/asottile/pyupgrade - rev: v2.24.0 - hooks: - - id: pyupgrade - name: Fix Python with Pyupgrade - args: ['--py37-plus', '--keep-runtime-typing'] - -# Check Python -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: check-ast - name: Check Python - -- repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.9.0 - hooks: - - id: python-check-mock-methods - name: Check for bad mock methods - - id: python-no-log-warn - name: Check for logger.warn - -# Format Python -- repo: https://github.com/psf/black - rev: '21.7b0' - hooks: - - id: black - name: Format Python with Black - -- repo: https://github.com/asottile/add-trailing-comma - rev: v2.1.0 - hooks: - - id: add-trailing-comma - args: [--py36-plus] - -- repo: https://github.com/pycqa/isort - rev: '5.9.3' - hooks: - - id: isort - name: Format Python imports with isort - -# Lint Python -- repo: https://github.com/pycqa/flake8 - rev: '4.0.1' - hooks: - - id: flake8 - name: Lint Python with Flake8 - exclude: '\bworkshops\b' - -- repo: https://github.com/pycqa/pylint - rev: v2.14.5 - hooks: - - id: pylint - name: Lint Python with Pylint - exclude: '\bworkshops\b' - -# Check Python dependencies -- repo: https://github.com/Lucas-C/pre-commit-hooks-safety - rev: v1.3.0 - hooks: - - id: python-safety-dependencies-check - name: Run Safety check on dependencies - files: 'requirements*.txt' - -# Check RST -- repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.9.0 - hooks: - - id: rst-backticks - name: Check RST backticks - - id: rst-directive-colons - name: Check RST directive colons - - id: rst-inline-touching-normal - name: Check RST inline touching normal - -# Lint RST -- repo: https://github.com/sphinx-contrib/sphinx-lint - rev: 'v0.6.1' - hooks: - - id: sphinx-lint - name: Lint RST - args: [--enable, all, --disable, line-too-long] - -# Check TOML and XML -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: check-toml - name: Check TOML - - id: check-xml - name: Check XML - -# Pretty-format YAML -- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.4.0 - hooks: - - id: pretty-format-yaml - name: Format YAML - exclude: '\bworkshops\b' - args: [--autofix, --indent, '2', --preserve-quotes] - -# Check YAML -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: check-yaml - name: Check YAML - -# Lint YAML -- repo: https://github.com/adrienverge/yamllint.git - rev: v1.27.1 - hooks: - - id: yamllint - name: Lint YAML - types: [yaml] - args: [-s, -f, colored] - -# Replace CRLF with LF post-prettify -- repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.3.0 - hooks: - - id: remove-crlf - name: Replace CRLFs with LF - types: [text] - exclude_types: [batch, svg] - - -# General checkers - -- repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.9.0 - hooks: - - id: text-unicode-replacement-char - name: Check for Unicode replacement chars - -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 - hooks: - - id: check-added-large-files - name: Check for added large files - args: [--maxkb=1025] - - id: check-case-conflict - name: Check for case conflicts - - id: check-executables-have-shebangs - name: Check that executables have shebangs - exclude: 'pre\-commit\-config\.yaml' - - id: check-shebang-scripts-are-executable - name: Check that shebangs are executable - exclude: 'pre\-commit\-config\.yaml' - - id: check-merge-conflict - name: Check for merge conflicts - - -# Commit message hooks # - -# Check that commit message follows basic rules -- repo: https://github.com/jorisroovers/gitlint - rev: v0.17.0 - hooks: - - id: gitlint - name: Check commit message - -# Check commit message spelling -- repo: https://github.com/codespell-project/codespell - rev: v2.1.0 - hooks: - - id: codespell - name: Check commit message spelling - stages: [commit-msg] +- repo: https://github.com/psf/black-pre-commit-mirror + rev: 23.7.0 + hooks: + - id: black + pass_filenames: true + exclude: _vendor|vendored|examples +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.281 + hooks: + - id: ruff + exclude: _vendor|vendored +- repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.23.3 + hooks: + - id: check-github-workflows +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-json + - id: check-yaml + - id: check-toml + - id: check-merge-conflict + - id: end-of-file-fixer + - id: trailing-whitespace + exclude: .*\.md + - id: debug-statements + - id: mixed-line-ending +- repo: https://github.com/asottile/setup-cfg-fmt + rev: v2.4.0 + hooks: + - id: setup-cfg-fmt + args: ["--include-version-classifiers", "--max-py-version", "3.11", "--min-py-version", "3.7"] + +ci: + autoupdate_schedule: monthly diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 739c3257..34bed6b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,6 +105,33 @@ python -m pip install -e .[test] You can then import and use QtPy as normal. When you make changes in your local copy of the git repository, they will be reflected in your installed copy as soon as you re-run Python. +### Pre-commit hooks + +We use [pre-commit](https://pre-commit.com/) to run some checks before each commit. To install it in local environment, run: + +```bash +pip install pre-commit +``` + +or globally with pipx: + +```bash +pipx install pre-commit +``` + +or from conda: + +```bash +conda install -c conda-forge pre-commit +``` + +Then, install the pre-commit hooks with: + +```bash +pre-commit install +``` + +If you do not want to run the hooks locally the `pre-commit.ci` workflow will run them for you on GitHub. ## Deciding Which Branch to Use diff --git a/pyproject.toml b/pyproject.toml index 15caa0ee..3e5aa223 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,3 +3,96 @@ requires = [ "setuptools>=42", ] build-backend = "setuptools.build_meta" + + +[tool.black] +target-version = ['py37', 'py38', 'py39', 'py310', 'py311'] +skip-string-normalization = true +line-length = 79 +exclude = ''' +( + /( + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + | examples + | vendored + | _vendor + )/ + | napari/resources/qt.py + | tools/minreq.py +) +''' + + +[tool.ruff] +line-length = 79 +select = [ + "E", "F", "W", #flake8 + "UP", # pyupgrade + "I", # isort + "YTT", #flake8-2020 + "TCH", # flake8-type-checing + "BLE", # flake8-blind-exception + "B", # flake8-bugbear + "A", # flake8-builtins + "C4", # flake8-comprehensions + "ISC", # flake8-implicit-str-concat + "G", # flake8-logging-format + "PIE", # flake8-pie + "COM", # flake8-commas + "SIM", # flake8-simplify + "INP", # flake8-no-pep420 + "PYI", # flake8-pyi + "Q", # flake8-quotes + "RSE", # flake8-raise + "RET", # flake8-return + "TID", # flake8-tidy-imports # replace absolutify import + "TRY", # tryceratops + "ICN", # flake8-import-conventions + "RUF", # ruff specyfic rules +] +ignore = [ + "F403", # star impoert + "E501", # line too long - black will handle it + "F405", # star import + "F401", # unused import - cannot determine wole list of symbols to export and use in __all__ +] + +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".mypy_cache", + ".pants.d", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + "*vendored*", + "*_vendor*", +] + +target-version = "py37" +fix = true + + +[tool.ruff.per-file-ignores] +"qtpy/QtCore.py" = ["F821"] +"qtpy/__init__.py" = ["TRY003", "F811"] +"qtpy/uic.py" = ["TRY003"] diff --git a/qtpy/Qsci.py b/qtpy/Qsci.py index 18d1ef06..85fec911 100644 --- a/qtpy/Qsci.py +++ b/qtpy/Qsci.py @@ -13,7 +13,7 @@ PYSIDE2, PYSIDE6, QtBindingMissingModuleError, - QtModuleNotInstalledError + QtModuleNotInstalledError, ) if PYQT5: @@ -21,16 +21,16 @@ from PyQt5.Qsci import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qsci', missing_package='QScintilla' + name="Qsci", + missing_package="QScintilla", ) from error elif PYQT6: try: from PyQt6.Qsci import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qsci', missing_package='PyQt6-QScintilla' + name="Qsci", + missing_package="PyQt6-QScintilla", ) from error -elif PYSIDE2: - raise QtBindingMissingModuleError(name='Qsci') -elif PYSIDE6: - raise QtBindingMissingModuleError(name='Qsci') +elif PYSIDE2 or PYSIDE6: + raise QtBindingMissingModuleError(name="Qsci") diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index 4befdd92..8d68e593 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -20,26 +20,30 @@ from PyQt5.Qt3DAnimation import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DAnimation', missing_package='PyQt3D' + name="Qt3DAnimation", + missing_package="PyQt3D", ) from error elif PYQT6: try: from PyQt6.Qt3DAnimation import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DAnimation', missing_package='PyQt6-3D' + name="Qt3DAnimation", + missing_package="PyQt6-3D", ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DAnimation as __temp import inspect + import PySide2.Qt3DAnimation as __temp + for __name in inspect.getmembers(__temp.Qt3DAnimation): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide6.Qt3DAnimation as __temp import inspect + import PySide6.Qt3DAnimation as __temp + for __name in inspect.getmembers(__temp.Qt3DAnimation): globals()[__name[0]] = __name[1] diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 007ef876..5c15df19 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -20,26 +20,30 @@ from PyQt5.Qt3DCore import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DCore', missing_package='PyQt3D' + name="Qt3DCore", + missing_package="PyQt3D", ) from error elif PYQT6: try: from PyQt6.Qt3DCore import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DCore', missing_package='PyQt6-3D' + name="Qt3DCore", + missing_package="PyQt6-3D", ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DCore as __temp import inspect + import PySide2.Qt3DCore as __temp + for __name in inspect.getmembers(__temp.Qt3DCore): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide6.Qt3DCore as __temp import inspect + import PySide6.Qt3DCore as __temp + for __name in inspect.getmembers(__temp.Qt3DCore): globals()[__name[0]] = __name[1] diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 81b82c16..d146065d 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -20,26 +20,30 @@ from PyQt5.Qt3DExtras import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DExtras', missing_package='PyQt3D' + name="Qt3DExtras", + missing_package="PyQt3D", ) from error elif PYQT6: try: from PyQt6.Qt3DExtras import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DExtras', missing_package='PyQt6-3D' + name="Qt3DExtras", + missing_package="PyQt6-3D", ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DExtras as __temp import inspect + import PySide2.Qt3DExtras as __temp + for __name in inspect.getmembers(__temp.Qt3DExtras): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide6.Qt3DExtras as __temp import inspect + import PySide6.Qt3DExtras as __temp + for __name in inspect.getmembers(__temp.Qt3DExtras): globals()[__name[0]] = __name[1] diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 5a706be5..1dbc3b44 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -20,26 +20,30 @@ from PyQt5.Qt3DInput import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DInput', missing_package='PyQt3D' + name="Qt3DInput", + missing_package="PyQt3D", ) from error elif PYQT6: try: from PyQt6.Qt3DInput import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DInput', missing_package='PyQt6-3D' + name="Qt3DInput", + missing_package="PyQt6-3D", ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DInput as __temp import inspect + import PySide2.Qt3DInput as __temp + for __name in inspect.getmembers(__temp.Qt3DInput): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide6.Qt3DInput as __temp import inspect + import PySide6.Qt3DInput as __temp + for __name in inspect.getmembers(__temp.Qt3DInput): globals()[__name[0]] = __name[1] diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index 59077c06..3fea3f51 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -20,26 +20,30 @@ from PyQt5.Qt3DLogic import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DLogic', missing_package='PyQt3D' + name="Qt3DLogic", + missing_package="PyQt3D", ) from error elif PYQT6: try: from PyQt6.Qt3DLogic import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DLogic', missing_package='PyQt6-3D' + name="Qt3DLogic", + missing_package="PyQt6-3D", ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DLogic as __temp import inspect + import PySide2.Qt3DLogic as __temp + for __name in inspect.getmembers(__temp.Qt3DLogic): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide6.Qt3DLogic as __temp import inspect + import PySide6.Qt3DLogic as __temp + for __name in inspect.getmembers(__temp.Qt3DLogic): globals()[__name[0]] = __name[1] diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index d7bf9f00..72f44f2c 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -20,26 +20,30 @@ from PyQt5.Qt3DRender import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DRender', missing_package='PyQt3D' + name="Qt3DRender", + missing_package="PyQt3D", ) from error elif PYQT6: try: from PyQt6.Qt3DRender import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DRender', missing_package='PyQt6-3D' + name="Qt3DRender", + missing_package="PyQt6-3D", ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DRender as __temp import inspect + import PySide2.Qt3DRender as __temp + for __name in inspect.getmembers(__temp.Qt3DRender): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide6.Qt3DRender as __temp import inspect + import PySide6.Qt3DRender as __temp + for __name in inspect.getmembers(__temp.Qt3DRender): globals()[__name[0]] = __name[1] diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py index 801491d6..fa2ba4ed 100644 --- a/qtpy/QtAxContainer.py +++ b/qtpy/QtAxContainer.py @@ -15,10 +15,8 @@ QtBindingMissingModuleError, ) -if PYQT5: - raise QtBindingMissingModuleError(name='QtAxContainer') -elif PYQT6: - raise QtBindingMissingModuleError(name='QtAxContainer') +if PYQT5 or PYQT6: + raise QtBindingMissingModuleError(name="QtAxContainer") elif PYSIDE2: from PySide2.QtAxContainer import * elif PYSIDE6: diff --git a/qtpy/QtBluetooth.py b/qtpy/QtBluetooth.py index 2f649743..237f81f9 100644 --- a/qtpy/QtBluetooth.py +++ b/qtpy/QtBluetooth.py @@ -20,6 +20,6 @@ elif PYQT6: from PyQt6.QtBluetooth import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtBluetooth') + raise QtBindingMissingModuleError(name="QtBluetooth") elif PYSIDE6: from PySide6.QtBluetooth import * diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 568d9c99..81716b0f 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -17,29 +17,31 @@ if PYQT5: try: - from PyQt5.QtChart import * from PyQt5 import QtChart as QtCharts + from PyQt5.QtChart import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtCharts', missing_package='PyQtChart' + name="QtCharts", + missing_package="PyQtChart", ) from error elif PYQT6: try: - from PyQt6.QtCharts import * from PyQt6 import QtCharts + from PyQt6.QtCharts import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtCharts', missing_package='PyQt6-Charts' + name="QtCharts", + missing_package="PyQt6-Charts", ) from error elif PYSIDE2: - from PySide2.QtCharts import * + import inspect # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.QtCharts as __temp - import inspect + from PySide2.QtCharts import * for __name in inspect.getmembers(__temp.QtCharts): globals()[__name[0]] = __name[1] elif PYSIDE6: - from PySide6.QtCharts import * from PySide6 import QtCharts + from PySide6.QtCharts import * diff --git a/qtpy/QtConcurrent.py b/qtpy/QtConcurrent.py index ca14d42e..a8e3ed3f 100644 --- a/qtpy/QtConcurrent.py +++ b/qtpy/QtConcurrent.py @@ -15,10 +15,8 @@ QtBindingMissingModuleError, ) -if PYQT5: - raise QtBindingMissingModuleError(name='QtConcurrent') -elif PYQT6: - raise QtBindingMissingModuleError(name='QtConcurrent') +if PYQT5 or PYQT6: + raise QtBindingMissingModuleError(name="QtConcurrent") elif PYSIDE2: from PySide2.QtConcurrent import * elif PYSIDE6: diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 0592a151..09a42b1e 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -7,24 +7,29 @@ # ----------------------------------------------------------------------------- """Provides QtCore classes and functions.""" +import contextlib from typing import TYPE_CHECKING from packaging.version import parse -from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QT_VERSION as _qt_version +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 +from . import QT_VERSION as _qt_version from ._utils import possibly_static_exec, possibly_static_exec_ if PYQT5: from PyQt5.QtCore import * - from PyQt5.QtCore import pyqtSignal as Signal from PyQt5.QtCore import pyqtBoundSignal as SignalInstance - from PyQt5.QtCore import pyqtSlot as Slot from PyQt5.QtCore import pyqtProperty as Property + from PyQt5.QtCore import pyqtSignal as Signal + from PyQt5.QtCore import pyqtSlot as Slot + try: from PyQt5.QtCore import Q_ENUM as QEnum + del Q_ENUM except ImportError: # fallback for Qt5.9 from PyQt5.QtCore import Q_ENUMS as QEnum + del Q_ENUMS from PyQt5.QtCore import QT_VERSION_STR as __version__ @@ -34,38 +39,50 @@ elif PYQT6: from PyQt6 import QtCore from PyQt6.QtCore import * - from PyQt6.QtCore import pyqtSignal as Signal + from PyQt6.QtCore import QT_VERSION_STR as __version__ from PyQt6.QtCore import pyqtBoundSignal as SignalInstance - from PyQt6.QtCore import pyqtSlot as Slot - from PyQt6.QtCore import pyqtProperty as Property from PyQt6.QtCore import pyqtEnum as QEnum - from PyQt6.QtCore import QT_VERSION_STR as __version__ + from PyQt6.QtCore import pyqtProperty as Property + from PyQt6.QtCore import pyqtSignal as Signal + from PyQt6.QtCore import pyqtSlot as Slot # For issue #311 # Seems like there is an error with sip. Without first # trying to import `PyQt6.QtGui.Qt`, some functions like # `PyQt6.QtCore.Qt.mightBeRichText` are missing. if not TYPE_CHECKING: - try: + with contextlib.suppress(ImportError): from PyQt6.QtGui import Qt - except ImportError: - pass # Map missing methods - QCoreApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QCoreApplication, *args, **kwargs) + QCoreApplication.exec_ = lambda *args, **kwargs: possibly_static_exec( + QCoreApplication, + *args, + **kwargs, + ) QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) # Those are imported from `import *` - del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, pyqtEnum, QT_VERSION_STR + del ( + pyqtSignal, + pyqtBoundSignal, + pyqtSlot, + pyqtProperty, + pyqtEnum, + QT_VERSION_STR, + ) # Allow unscoped access for enums inside the QtCore module from .enums_compat import promote_enums + promote_enums(QtCore) del QtCore # Alias deprecated ItemDataRole enum values removed in Qt6 - Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole + Qt.BackgroundColorRole = ( + Qt.ItemDataRole.BackgroundColorRole + ) = Qt.BackgroundRole Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole # Alias for MiddleButton removed in PyQt6 but available in PyQt5, PySide2 and PySide6 @@ -78,61 +95,98 @@ Qt.ItemFlags = lambda value=0: Qt.ItemFlag(value) elif PYSIDE2: + import PySide2.QtCore from PySide2.QtCore import * - import PySide2.QtCore __version__ = PySide2.QtCore.__version__ # Missing QtGui utility functions on Qt - if getattr(Qt, 'mightBeRichText', None) is None: + if getattr(Qt, "mightBeRichText", None) is None: try: from PySide2.QtGui import Qt as guiQt + Qt.mightBeRichText = guiQt.mightBeRichText del guiQt except ImportError: # Fails with PySide2 5.12.0 pass - QCoreApplication.exec = lambda *args, **kwargs: possibly_static_exec_(QCoreApplication, *args, **kwargs) + QCoreApplication.exec = lambda *args, **kwargs: possibly_static_exec_( + QCoreApplication, + *args, + **kwargs, + ) QEventLoop.exec = lambda self, *args, **kwargs: self.exec_(*args, **kwargs) QThread.exec = lambda self, *args, **kwargs: self.exec_(*args, **kwargs) - QTextStreamManipulator.exec = lambda self, *args, **kwargs: self.exec_(*args, **kwargs) + QTextStreamManipulator.exec = lambda self, *args, **kwargs: self.exec_( + *args, + **kwargs, + ) elif PYSIDE6: - from PySide6.QtCore import * import PySide6.QtCore + from PySide6.QtCore import * + __version__ = PySide6.QtCore.__version__ # Missing QtGui utility functions on Qt - if getattr(Qt, 'mightBeRichText', None) is None: + if getattr(Qt, "mightBeRichText", None) is None: from PySide6.QtGui import Qt as guiQt + Qt.mightBeRichText = guiQt.mightBeRichText del guiQt # Alias deprecated ItemDataRole enum values removed in Qt6 - Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole + Qt.BackgroundColorRole = ( + Qt.ItemDataRole.BackgroundColorRole + ) = Qt.BackgroundRole Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole Qt.MidButton = Qt.MiddleButton # Map DeprecationWarning methods - QCoreApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QCoreApplication, *args, **kwargs) + QCoreApplication.exec_ = lambda *args, **kwargs: possibly_static_exec( + QCoreApplication, + *args, + **kwargs, + ) QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec( + *args, + **kwargs, + ) # Passing as default value 0 in the same way PySide6 6.3.2 does for the `Qt.ItemFlags` definition. - if parse(_qt_version) > parse('6.3'): + if parse(_qt_version) > parse("6.3"): Qt.ItemFlags = lambda value=0: Qt.ItemFlag(value) # For issue #153 and updated for issue #305 if PYQT5 or PYQT6: - QDate.toPython = lambda self, *args, **kwargs: self.toPyDate(*args, **kwargs) - QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) - QTime.toPython = lambda self, *args, **kwargs: self.toPyTime(*args, **kwargs) + QDate.toPython = lambda self, *args, **kwargs: self.toPyDate( + *args, + **kwargs, + ) + QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime( + *args, + **kwargs, + ) + QTime.toPython = lambda self, *args, **kwargs: self.toPyTime( + *args, + **kwargs, + ) if PYSIDE2 or PYSIDE6: - QDate.toPyDate = lambda self, *args, **kwargs: self.toPython(*args, **kwargs) - QDateTime.toPyDateTime = lambda self, *args, **kwargs: self.toPython(*args, **kwargs) - QTime.toPyTime = lambda self, *args, **kwargs: self.toPython(*args, **kwargs) + QDate.toPyDate = lambda self, *args, **kwargs: self.toPython( + *args, + **kwargs, + ) + QDateTime.toPyDateTime = lambda self, *args, **kwargs: self.toPython( + *args, + **kwargs, + ) + QTime.toPyTime = lambda self, *args, **kwargs: self.toPython( + *args, + **kwargs, + ) # Mirror https://github.com/spyder-ide/qtpy/pull/393 if PYQT5 or PYSIDE2: diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index 6d479fa5..1d41aab9 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -23,9 +23,9 @@ elif PYQT6: from PyQt6.QtDBus import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtDBus') + raise QtBindingMissingModuleError(name="QtDBus") elif PYSIDE6: - if sys.platform != 'win32': + if sys.platform != "win32": from PySide6.QtDBus import * else: - raise QtModuleNotInOSError(name='QtDBus') + raise QtModuleNotInOSError(name="QtDBus") diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index 692430f1..0a4facf8 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -20,21 +20,23 @@ from PyQt5.QtDataVisualization import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtDataVisualization', missing_package='PyQtDataVisualization' + name="QtDataVisualization", + missing_package="PyQtDataVisualization", ) from error elif PYQT6: try: from PyQt6.QtDataVisualization import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtDataVisualization', - missing_package='PyQt6-DataVisualization', + name="QtDataVisualization", + missing_package="PyQt6-DataVisualization", ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.QtDataVisualization as __temp import inspect + import PySide2.QtDataVisualization as __temp + for __name in inspect.getmembers(__temp.QtDataVisualization): globals()[__name[0]] = __name[1] elif PYSIDE6: diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index 969d8480..acf61d57 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -20,6 +20,6 @@ elif PYQT6: from PyQt6.QtDesigner import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtDesigner') + raise QtBindingMissingModuleError(name="QtDesigner") elif PYSIDE6: from PySide6.QtDesigner import * diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index b03b0528..8b3a0ba9 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -8,41 +8,51 @@ """Provides QtGui classes and functions.""" -from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from ._utils import possibly_static_exec, getattr_missing_optional_dep - +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError +from ._utils import getattr_missing_optional_dep, possibly_static_exec _missing_optional_names = {} _QTOPENGL_NAMES = { - 'QOpenGLBuffer', - 'QOpenGLContext', - 'QOpenGLContextGroup', - 'QOpenGLDebugLogger', - 'QOpenGLDebugMessage', - 'QOpenGLFramebufferObject', - 'QOpenGLFramebufferObjectFormat', - 'QOpenGLPixelTransferOptions', - 'QOpenGLShader', - 'QOpenGLShaderProgram', - 'QOpenGLTexture', - 'QOpenGLTextureBlitter', - 'QOpenGLVersionProfile', - 'QOpenGLVertexArrayObject', - 'QOpenGLWindow', + "QOpenGLBuffer", + "QOpenGLContext", + "QOpenGLContextGroup", + "QOpenGLDebugLogger", + "QOpenGLDebugMessage", + "QOpenGLFramebufferObject", + "QOpenGLFramebufferObjectFormat", + "QOpenGLPixelTransferOptions", + "QOpenGLShader", + "QOpenGLShaderProgram", + "QOpenGLTexture", + "QOpenGLTextureBlitter", + "QOpenGLVersionProfile", + "QOpenGLVertexArrayObject", + "QOpenGLWindow", } + def __getattr__(name): """Custom getattr to chain and wrap errors due to missing optional deps.""" raise getattr_missing_optional_dep( - name, module_name=__name__, optional_names=_missing_optional_names) + name, + module_name=__name__, + optional_names=_missing_optional_names, + ) if PYQT5: from PyQt5.QtGui import * # Backport items moved to QtGui in Qt6 - from PyQt5.QtWidgets import QAction, QActionGroup, QFileSystemModel, QShortcut, QUndoCommand + from PyQt5.QtWidgets import ( + QAction, + QActionGroup, + QFileSystemModel, + QShortcut, + QUndoCommand, + ) + elif PYQT6: from PyQt6 import QtGui from PyQt6.QtGui import * @@ -55,32 +65,57 @@ def __getattr__(name): except ImportError as error: for name in _QTOPENGL_NAMES: _missing_optional_names[name] = { - 'name': 'PyQt6.QtOpenGL', - 'missing_package': 'pyopengl', - 'import_error': error, + "name": "PyQt6.QtOpenGL", + "missing_package": "pyopengl", + "import_error": error, } - QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) - QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance( + *args, + **kwargs, + ) + QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance( + *args, + **kwargs, + ) # Map missing/renamed methods QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QGuiApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QGuiApplication, *args, **kwargs) - QTextDocument.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) + QGuiApplication.exec_ = lambda *args, **kwargs: possibly_static_exec( + QGuiApplication, + *args, + **kwargs, + ) + QTextDocument.print_ = lambda self, *args, **kwargs: self.print( + *args, + **kwargs, + ) # Allow unscoped access for enums inside the QtGui module from .enums_compat import promote_enums + promote_enums(QtGui) del QtGui elif PYSIDE2: from PySide2.QtGui import * # Backport items moved to QtGui in Qt6 - from PySide2.QtWidgets import QAction, QActionGroup, QFileSystemModel, QShortcut, QUndoCommand - - if hasattr(QFontMetrics, 'horizontalAdvance'): + from PySide2.QtWidgets import ( + QAction, + QActionGroup, + QFileSystemModel, + QShortcut, + QUndoCommand, + ) + + if hasattr(QFontMetrics, "horizontalAdvance"): # Needed to prevent raising a DeprecationWarning when using QFontMetrics.width - QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) + QFontMetrics.width = ( + lambda self, *args, **kwargs: self.horizontalAdvance( + *args, + **kwargs, + ) + ) elif PYSIDE6: from PySide6.QtGui import * @@ -92,20 +127,30 @@ def __getattr__(name): except ImportError as error: for name in _QTOPENGL_NAMES: _missing_optional_names[name] = { - 'name': 'PySide6.QtOpenGL', - 'missing_package': 'pyopengl', - 'import_error': error, + "name": "PySide6.QtOpenGL", + "missing_package": "pyopengl", + "import_error": error, } # Backport `QFileSystemModel` moved to QtGui in Qt6 from PySide6.QtWidgets import QFileSystemModel - QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) - QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance( + *args, + **kwargs, + ) + QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance( + *args, + **kwargs, + ) # Map DeprecationWarning methods QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QGuiApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QGuiApplication, *args, **kwargs) + QGuiApplication.exec_ = lambda *args, **kwargs: possibly_static_exec( + QGuiApplication, + *args, + **kwargs, + ) if PYSIDE2 or PYSIDE6: # PySide{2,6} do not accept the `mode` keyword argument in @@ -121,13 +166,14 @@ def __getattr__(name): # 6.3.0; older version, down to PySide 1, are probably affected as well [1]. # # * PySide2 5.15.0 and 5.15.2.1 silently ignore invalid keyword arguments, - # i.e. passing the `mode` keyword argument has no effect and doesn’t + # i.e. passing the `mode` keyword argument has no effect and doesn`t # raise an exception. Older versions, down to PySide 1, are probably # affected as well [1]. At least PySide2 5.15.3 and PySide6 6.3.0 raise an # exception when `mode` or any other invalid keyword argument is passed. # # [1] https://bugreports.qt.io/browse/PYSIDE-185 movePosition = QTextCursor.movePosition + def movePositionPatched( self, operation: QTextCursor.MoveOperation, @@ -135,21 +181,27 @@ def movePositionPatched( n: int = 1, ) -> bool: return movePosition(self, operation, mode, n) + QTextCursor.movePosition = movePositionPatched if PYQT5 or PYSIDE2: # Part of the fix for https://github.com/spyder-ide/qtpy/issues/394 from qtpy.QtCore import QPointF as __QPointF + QNativeGestureEvent.x = lambda self: self.localPos().toPoint().x() QNativeGestureEvent.y = lambda self: self.localPos().toPoint().y() QNativeGestureEvent.position = lambda self: self.localPos() QNativeGestureEvent.globalX = lambda self: self.globalPos().x() QNativeGestureEvent.globalY = lambda self: self.globalPos().y() QNativeGestureEvent.globalPosition = lambda self: __QPointF( - float(self.globalPos().x()), float(self.globalPos().y())) + float(self.globalPos().x()), + float(self.globalPos().y()), + ) QEnterEvent.position = lambda self: self.localPos() QEnterEvent.globalPosition = lambda self: __QPointF( - float(self.globalX()), float(self.globalY())) + float(self.globalX()), + float(self.globalY()), + ) QTabletEvent.position = lambda self: self.posF() QTabletEvent.globalPosition = lambda self: self.globalPosF() QHoverEvent.x = lambda self: self.pos().x() @@ -159,15 +211,29 @@ def movePositionPatched( # nor `QHoverEvent.globalY` in the Qt5 docs. QMouseEvent.position = lambda self: self.localPos() QMouseEvent.globalPosition = lambda self: __QPointF( - float(self.globalX()), float(self.globalY())) + float(self.globalX()), + float(self.globalY()), + ) # Follow similar approach for `QDropEvent` and child classes QDropEvent.position = lambda self: self.posF() if PYQT6 or PYSIDE6: # Part of the fix for https://github.com/spyder-ide/qtpy/issues/394 - for _class in (QNativeGestureEvent, QEnterEvent, QTabletEvent, QHoverEvent, - QMouseEvent): - for _obsolete_function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY'): + for _class in ( + QNativeGestureEvent, + QEnterEvent, + QTabletEvent, + QHoverEvent, + QMouseEvent, + ): + for _obsolete_function in ( + "pos", + "x", + "y", + "globalPos", + "globalX", + "globalY", + ): if hasattr(_class, _obsolete_function): delattr(_class, _obsolete_function) QSinglePointEvent.pos = lambda self: self.position().toPoint() @@ -176,8 +242,12 @@ def movePositionPatched( QSinglePointEvent.x = lambda self: self.position().toPoint().x() QSinglePointEvent.y = lambda self: self.position().toPoint().y() QSinglePointEvent.globalPos = lambda self: self.globalPosition().toPoint() - QSinglePointEvent.globalX = lambda self: self.globalPosition().toPoint().x() - QSinglePointEvent.globalY = lambda self: self.globalPosition().toPoint().y() + QSinglePointEvent.globalX = ( + lambda self: self.globalPosition().toPoint().x() + ) + QSinglePointEvent.globalY = ( + lambda self: self.globalPosition().toPoint().y() + ) # Follow similar approach for `QDropEvent` and child classes QDropEvent.pos = lambda self: self.position().toPoint() diff --git a/qtpy/QtLocation.py b/qtpy/QtLocation.py index 1f7132dc..160c9476 100644 --- a/qtpy/QtLocation.py +++ b/qtpy/QtLocation.py @@ -18,8 +18,8 @@ if PYQT5: from PyQt5.QtLocation import * elif PYQT6: - raise QtBindingMissingModuleError(name='QtLocation') + raise QtBindingMissingModuleError(name="QtLocation") elif PYSIDE2: from PySide2.QtLocation import * elif PYSIDE6: - raise QtBindingMissingModuleError(name='QtLocation') + raise QtBindingMissingModuleError(name="QtLocation") diff --git a/qtpy/QtMacExtras.py b/qtpy/QtMacExtras.py index d495c1eb..58d23b8c 100644 --- a/qtpy/QtMacExtras.py +++ b/qtpy/QtMacExtras.py @@ -14,18 +14,18 @@ PYQT6, PYSIDE2, PYSIDE6, - QtModuleNotInQtVersionError, QtModuleNotInOSError, + QtModuleNotInQtVersionError, ) -if sys.platform == 'darwin': +if sys.platform == "darwin": if PYQT5: from PyQt5.QtMacExtras import * elif PYQT6: - raise QtModuleNotInQtVersionError(name='QtMacExtras') + raise QtModuleNotInQtVersionError(name="QtMacExtras") elif PYSIDE2: from PySide2.QtMacExtras import * elif PYSIDE6: - raise QtModuleNotInQtVersionError(name='QtMacExtras') + raise QtModuleNotInQtVersionError(name="QtMacExtras") else: - raise QtModuleNotInOSError(name='QtMacExtras') + raise QtModuleNotInOSError(name="QtMacExtras") diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py index abf7526e..f49adcec 100644 --- a/qtpy/QtNetworkAuth.py +++ b/qtpy/QtNetworkAuth.py @@ -12,8 +12,8 @@ PYQT6, PYSIDE2, PYSIDE6, - QtModuleNotInstalledError, QtBindingMissingModuleError, + QtModuleNotInstalledError, ) if PYQT5: @@ -21,16 +21,18 @@ from PyQt5.QtNetworkAuth import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtNetworkAuth', missing_package='PyQtNetworkAuth' + name="QtNetworkAuth", + missing_package="PyQtNetworkAuth", ) from error elif PYQT6: try: from PyQt6.QtNetworkAuth import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtNetworkAuth', missing_package='PyQt6-NetworkAuth' + name="QtNetworkAuth", + missing_package="PyQt6-NetworkAuth", ) from error elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtNetworkAuth') + raise QtBindingMissingModuleError(name="QtNetworkAuth") elif PYSIDE6: from PySide6.QtNetworkAuth import * diff --git a/qtpy/QtNfc.py b/qtpy/QtNfc.py index 38007248..8dafd7ce 100644 --- a/qtpy/QtNfc.py +++ b/qtpy/QtNfc.py @@ -20,6 +20,6 @@ elif PYQT6: from PyQt6.QtNfc import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtNfc') + raise QtBindingMissingModuleError(name="QtNfc") elif PYSIDE6: from PySide6.QtNfc import * diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 345853a1..af881ed9 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -7,61 +7,60 @@ """Provides QtOpenGL classes and functions.""" +import contextlib + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: - from PyQt5.QtOpenGL import * from PyQt5.QtGui import ( QOpenGLBuffer, - QOpenGLFramebufferObject, - QOpenGLFramebufferObjectFormat, - QOpenGLShader, - QOpenGLShaderProgram, QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, QOpenGLDebugMessage, + QOpenGLFramebufferObject, + QOpenGLFramebufferObjectFormat, QOpenGLPixelTransferOptions, + QOpenGLShader, + QOpenGLShaderProgram, QOpenGLTexture, QOpenGLTextureBlitter, QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow, ) + from PyQt5.QtOpenGL import * # These are not present on some architectures such as armhf - try: + with contextlib.suppress(ImportError): from PyQt5.QtGui import QOpenGLTimeMonitor, QOpenGLTimerQuery - except ImportError: - pass + elif PYQT6: - from PyQt6.QtOpenGL import * from PyQt6.QtGui import QOpenGLContext, QOpenGLContextGroup + from PyQt6.QtOpenGL import * elif PYSIDE6: - from PySide6.QtOpenGL import * from PySide6.QtGui import QOpenGLContext, QOpenGLContextGroup + from PySide6.QtOpenGL import * elif PYSIDE2: - from PySide2.QtOpenGL import * from PySide2.QtGui import ( QOpenGLBuffer, - QOpenGLFramebufferObject, - QOpenGLFramebufferObjectFormat, - QOpenGLShader, - QOpenGLShaderProgram, QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, QOpenGLDebugMessage, + QOpenGLFramebufferObject, + QOpenGLFramebufferObjectFormat, QOpenGLPixelTransferOptions, + QOpenGLShader, + QOpenGLShaderProgram, QOpenGLTexture, QOpenGLTextureBlitter, QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow, ) + from PySide2.QtOpenGL import * # These are not present on some architectures such as armhf - try: + with contextlib.suppress(ImportError): from PySide2.QtGui import QOpenGLTimeMonitor, QOpenGLTimerQuery - except ImportError: - pass diff --git a/qtpy/QtOpenGLWidgets.py b/qtpy/QtOpenGLWidgets.py index fb9fa62a..c000e0fd 100644 --- a/qtpy/QtOpenGLWidgets.py +++ b/qtpy/QtOpenGLWidgets.py @@ -16,10 +16,10 @@ ) if PYQT5: - raise QtBindingMissingModuleError(name='QtOpenGLWidgets') + raise QtBindingMissingModuleError(name="QtOpenGLWidgets") elif PYQT6: from PyQt6.QtOpenGLWidgets import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtOpenGLWidgets') + raise QtBindingMissingModuleError(name="QtOpenGLWidgets") elif PYSIDE6: from PySide6.QtOpenGLWidgets import * diff --git a/qtpy/QtPdf.py b/qtpy/QtPdf.py index 3a6796ca..f98cbd0f 100644 --- a/qtpy/QtPdf.py +++ b/qtpy/QtPdf.py @@ -16,12 +16,12 @@ ) if PYQT5: - raise QtBindingMissingModuleError(name='QtPdf') + raise QtBindingMissingModuleError(name="QtPdf") elif PYQT6: # Available with version >=6.4.0 from PyQt6.QtPdf import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtPdf') + raise QtBindingMissingModuleError(name="QtPdf") elif PYSIDE6: # Available with version >=6.4.0 from PySide6.QtPdf import * diff --git a/qtpy/QtPdfWidgets.py b/qtpy/QtPdfWidgets.py index 1b0a2e11..a437db60 100644 --- a/qtpy/QtPdfWidgets.py +++ b/qtpy/QtPdfWidgets.py @@ -16,12 +16,12 @@ ) if PYQT5: - raise QtBindingMissingModuleError(name='QtPdfWidgets') + raise QtBindingMissingModuleError(name="QtPdfWidgets") elif PYQT6: # Available with version >=6.4.0 from PyQt6.QtPdfWidgets import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtPdfWidgets') + raise QtBindingMissingModuleError(name="QtPdfWidgets") elif PYSIDE6: # Available with version >=6.4.0 from PySide6.QtPdfWidgets import * diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 263747d2..d78ff4cc 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -7,19 +7,36 @@ """Provides QtPrintSupport classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtPrintSupport import * elif PYQT6: from PyQt6.QtPrintSupport import * - QPageSetupDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QPrintDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QPrintPreviewWidget.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) + + QPageSetupDialog.exec_ = lambda self, *args, **kwargs: self.exec( + *args, + **kwargs, + ) + QPrintDialog.exec_ = lambda self, *args, **kwargs: self.exec( + *args, + **kwargs, + ) + QPrintPreviewWidget.print_ = lambda self, *args, **kwargs: self.print( + *args, + **kwargs, + ) elif PYSIDE6: from PySide6.QtPrintSupport import * + # Map DeprecationWarning methods - QPageSetupDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QPrintDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QPageSetupDialog.exec_ = lambda self, *args, **kwargs: self.exec( + *args, + **kwargs, + ) + QPrintDialog.exec_ = lambda self, *args, **kwargs: self.exec( + *args, + **kwargs, + ) elif PYSIDE2: from PySide2.QtPrintSupport import * diff --git a/qtpy/QtPurchasing.py b/qtpy/QtPurchasing.py index 00141a25..fd694483 100644 --- a/qtpy/QtPurchasing.py +++ b/qtpy/QtPurchasing.py @@ -21,11 +21,8 @@ from PyQt5.QtPurchasing import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtPurchasing', missing_package='PyQtPurchasing' + name="QtPurchasing", + missing_package="PyQtPurchasing", ) from error -elif PYQT6: - raise QtBindingMissingModuleError(name='QtPurchasing') -elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtPurchasing') -elif PYSIDE6: - raise QtBindingMissingModuleError(name='QtPurchasing') +elif PYQT6 or PYSIDE2 or PYSIDE6: + raise QtBindingMissingModuleError(name="QtPurchasing") diff --git a/qtpy/QtQuick.py b/qtpy/QtQuick.py index 7f140cb1..1c2b7da3 100644 --- a/qtpy/QtQuick.py +++ b/qtpy/QtQuick.py @@ -7,7 +7,7 @@ """Provides QtQuick classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtQuick import * diff --git a/qtpy/QtQuick3D.py b/qtpy/QtQuick3D.py index eeb0a60c..a8138f97 100644 --- a/qtpy/QtQuick3D.py +++ b/qtpy/QtQuick3D.py @@ -20,6 +20,6 @@ elif PYQT6: from PyQt6.QtQuick3D import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtQuick3D') + raise QtBindingMissingModuleError(name="QtQuick3D") elif PYSIDE6: from PySide6.QtQuick3D import * diff --git a/qtpy/QtQuickControls2.py b/qtpy/QtQuickControls2.py index 743aa5d4..634d5445 100644 --- a/qtpy/QtQuickControls2.py +++ b/qtpy/QtQuickControls2.py @@ -15,10 +15,8 @@ QtBindingMissingModuleError, ) -if PYQT5: - raise QtBindingMissingModuleError(name='QtQuickControls2') -elif PYQT6: - raise QtBindingMissingModuleError(name='QtQuickControls2') +if PYQT5 or PYQT6: + raise QtBindingMissingModuleError(name="QtQuickControls2") elif PYSIDE2: from PySide2.QtQuickControls2 import * elif PYSIDE6: diff --git a/qtpy/QtQuickWidgets.py b/qtpy/QtQuickWidgets.py index 4ed4a21c..136b411e 100644 --- a/qtpy/QtQuickWidgets.py +++ b/qtpy/QtQuickWidgets.py @@ -7,7 +7,7 @@ """Provides QtQuickWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtQuickWidgets import * diff --git a/qtpy/QtScxml.py b/qtpy/QtScxml.py index 81a9ac13..40da5ef7 100644 --- a/qtpy/QtScxml.py +++ b/qtpy/QtScxml.py @@ -15,10 +15,8 @@ QtBindingMissingModuleError, ) -if PYQT5: - raise QtBindingMissingModuleError(name='QtScxml') -elif PYQT6: - raise QtBindingMissingModuleError(name='QtScxml') +if PYQT5 or PYQT6: + raise QtBindingMissingModuleError(name="QtScxml") elif PYSIDE2: from PySide2.QtScxml import * elif PYSIDE6: diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 5f7395b8..76a63760 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -7,19 +7,27 @@ """Provides QtSql classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtSql import * elif PYQT6: from PyQt6.QtSql import * - QSqlDatabase.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + + QSqlDatabase.exec_ = lambda self, *args, **kwargs: self.exec( + *args, + **kwargs, + ) QSqlQuery.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QSqlResult.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) elif PYSIDE6: from PySide6.QtSql import * + # Map DeprecationWarning methods - QSqlDatabase.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QSqlDatabase.exec_ = lambda self, *args, **kwargs: self.exec( + *args, + **kwargs, + ) QSqlQuery.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QSqlResult.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) elif PYSIDE2: diff --git a/qtpy/QtStateMachine.py b/qtpy/QtStateMachine.py index 4afbcf49..343ce4a3 100644 --- a/qtpy/QtStateMachine.py +++ b/qtpy/QtStateMachine.py @@ -15,11 +15,7 @@ QtBindingMissingModuleError, ) -if PYQT5: - raise QtBindingMissingModuleError(name='QtStateMachine') -elif PYQT6: - raise QtBindingMissingModuleError(name='QtStateMachine') -elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtStateMachine') +if PYQT5 or PYQT6 or PYSIDE2: + raise QtBindingMissingModuleError(name="QtStateMachine") elif PYSIDE6: from PySide6.QtStateMachine import * diff --git a/qtpy/QtSvgWidgets.py b/qtpy/QtSvgWidgets.py index 07eedb07..7e91dfc9 100644 --- a/qtpy/QtSvgWidgets.py +++ b/qtpy/QtSvgWidgets.py @@ -16,10 +16,10 @@ ) if PYQT5: - raise QtBindingMissingModuleError(name='QtSvgWidgets') + raise QtBindingMissingModuleError(name="QtSvgWidgets") elif PYQT6: from PyQt6.QtSvgWidgets import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtSvgWidgets') + raise QtBindingMissingModuleError(name="QtSvgWidgets") elif PYSIDE6: from PySide6.QtSvgWidgets import * diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 3be7f95a..b14418f2 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -8,7 +8,7 @@ """Provides QtTest and functions""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtTest import * diff --git a/qtpy/QtTextToSpeech.py b/qtpy/QtTextToSpeech.py index cd978328..c37192a1 100644 --- a/qtpy/QtTextToSpeech.py +++ b/qtpy/QtTextToSpeech.py @@ -18,8 +18,8 @@ if PYQT5: from PyQt5.QtTextToSpeech import * elif PYQT6: - raise QtBindingMissingModuleError(name='QtTextToSpeech') + raise QtBindingMissingModuleError(name="QtTextToSpeech") elif PYSIDE2: from PySide2.QtTextToSpeech import * elif PYSIDE6: - raise QtBindingMissingModuleError(name='QtTextToSpeech') + raise QtBindingMissingModuleError(name="QtTextToSpeech") diff --git a/qtpy/QtUiTools.py b/qtpy/QtUiTools.py index 5104a645..ceca1efa 100644 --- a/qtpy/QtUiTools.py +++ b/qtpy/QtUiTools.py @@ -15,10 +15,8 @@ QtBindingMissingModuleError, ) -if PYQT5: - raise QtBindingMissingModuleError(name='QtUiTools') -elif PYQT6: - raise QtBindingMissingModuleError(name='QtUiTools') +if PYQT5 or PYQT6: + raise QtBindingMissingModuleError(name="QtUiTools") elif PYSIDE2: from PySide2.QtUiTools import * elif PYSIDE6: diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index c5c80d52..7cc08ff0 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -22,11 +22,12 @@ from PyQt5.QtWebEngine import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngine', missing_package='PyQtWebEngine' + name="QtWebEngine", + missing_package="PyQtWebEngine", ) from error elif PYQT6: - raise QtModuleNotInQtVersionError(name='QtWebEngine') + raise QtModuleNotInQtVersionError(name="QtWebEngine") elif PYSIDE2: from PySide2.QtWebEngine import * elif PYSIDE6: - raise QtModuleNotInQtVersionError(name='QtWebEngine') + raise QtModuleNotInQtVersionError(name="QtWebEngine") diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py index 76efbbfb..69aa4eea 100644 --- a/qtpy/QtWebEngineCore.py +++ b/qtpy/QtWebEngineCore.py @@ -20,14 +20,16 @@ from PyQt5.QtWebEngineCore import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineCore', missing_package='PyQtWebEngine' + name="QtWebEngineCore", + missing_package="PyQtWebEngine", ) from error elif PYQT6: try: from PyQt6.QtWebEngineCore import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineCore', missing_package='PyQt6-WebEngine' + name="QtWebEngineCore", + missing_package="PyQt6-WebEngine", ) from error elif PYSIDE2: from PySide2.QtWebEngineCore import * diff --git a/qtpy/QtWebEngineQuick.py b/qtpy/QtWebEngineQuick.py index 659834a3..717ac94d 100644 --- a/qtpy/QtWebEngineQuick.py +++ b/qtpy/QtWebEngineQuick.py @@ -12,20 +12,21 @@ PYQT6, PYSIDE2, PYSIDE6, - QtModuleNotInstalledError, QtBindingMissingModuleError, + QtModuleNotInstalledError, ) if PYQT5: - raise QtBindingMissingModuleError(name='QtWebEngineQuick') + raise QtBindingMissingModuleError(name="QtWebEngineQuick") elif PYQT6: try: from PyQt6.QtWebEngineQuick import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineQuick', missing_package='PyQt6-WebEngine' + name="QtWebEngineQuick", + missing_package="PyQt6-WebEngine", ) from error elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtWebEngineQuick') + raise QtBindingMissingModuleError(name="QtWebEngineQuick") elif PYSIDE6: from PySide6.QtWebEngineQuick import * diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 05a18378..f48c972f 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -16,7 +16,6 @@ QtModuleNotInstalledError, ) - # To test if we are using WebEngine or WebKit # NOTE: This constant is imported by other projects (e.g. Spyder), so please # don't remove it. @@ -25,39 +24,47 @@ if PYQT5: try: - from PyQt5.QtWebEngineWidgets import QWebEnginePage - from PyQt5.QtWebEngineWidgets import QWebEngineView - from PyQt5.QtWebEngineWidgets import QWebEngineSettings - from PyQt5.QtWebEngineWidgets import QWebEngineScript - # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 - from PyQt5.QtWebEngineWidgets import QWebEngineProfile + from PyQt5.QtWebEngineWidgets import ( + QWebEnginePage, + QWebEngineProfile, + QWebEngineScript, + QWebEngineSettings, + QWebEngineView, + ) except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineWidgets', missing_package='PyQtWebEngine' + name="QtWebEngineWidgets", + missing_package="PyQtWebEngine", ) from error elif PYQT6: try: + from PyQt6.QtWebEngineCore import ( + QWebEnginePage, + QWebEngineProfile, + QWebEngineScript, + QWebEngineSettings, + ) from PyQt6.QtWebEngineWidgets import * - from PyQt6.QtWebEngineCore import QWebEnginePage - from PyQt6.QtWebEngineCore import QWebEngineSettings - from PyQt6.QtWebEngineCore import QWebEngineProfile - from PyQt6.QtWebEngineCore import QWebEngineScript except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineWidgets', missing_package='PyQt6-WebEngine' + name="QtWebEngineWidgets", + missing_package="PyQt6-WebEngine", ) from error elif PYSIDE2: - from PySide2.QtWebEngineWidgets import QWebEnginePage - from PySide2.QtWebEngineWidgets import QWebEngineView - from PySide2.QtWebEngineWidgets import QWebEngineSettings - from PySide2.QtWebEngineWidgets import QWebEngineScript - # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 - from PySide2.QtWebEngineWidgets import QWebEngineProfile + from PySide2.QtWebEngineWidgets import ( + QWebEnginePage, + QWebEngineProfile, + QWebEngineScript, + QWebEngineSettings, + QWebEngineView, + ) elif PYSIDE6: + from PySide6.QtWebEngineCore import ( + QWebEnginePage, + QWebEngineProfile, + QWebEngineScrip, + QWebEngineSettings, + ) from PySide6.QtWebEngineWidgets import * - from PySide6.QtWebEngineCore import QWebEnginePage - from PySide6.QtWebEngineCore import QWebEngineSettings - from PySide6.QtWebEngineCore import QWebEngineProfile - from PySide6.QtWebEngineCore import QWebEngineScript diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 719fa61a..5e8e0886 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -11,30 +11,39 @@ from packaging.version import parse -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QT_VERSION as _qt_version +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 +from . import QT_VERSION as _qt_version from ._utils import ( add_action, - possibly_static_exec, getattr_missing_optional_dep, - static_method_kwargs_wrapper + possibly_static_exec, + static_method_kwargs_wrapper, ) - _missing_optional_names = {} def __getattr__(name): """Custom getattr to chain and wrap errors due to missing optional deps.""" raise getattr_missing_optional_dep( - name, module_name=__name__, optional_names=_missing_optional_names) + name, + module_name=__name__, + optional_names=_missing_optional_names, + ) if PYQT5: from PyQt5.QtWidgets import * elif PYQT6: from PyQt6 import QtWidgets + from PyQt6.QtGui import ( + QAction, + QActionGroup, + QFileSystemModel, + QShortcut, + QUndoCommand, + ) from PyQt6.QtWidgets import * - from PyQt6.QtGui import QAction, QActionGroup, QShortcut, QFileSystemModel, QUndoCommand # Attempt to import QOpenGLWidget, but if that fails, # don't raise an exception until the name is explicitly accessed. @@ -42,23 +51,50 @@ def __getattr__(name): try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget except ImportError as error: - _missing_optional_names['QOpenGLWidget'] = { - 'name': 'PyQt6.QtOpenGLWidgets', - 'missing_package': 'pyopengl', - 'import_error': error, + _missing_optional_names["QOpenGLWidget"] = { + "name": "PyQt6.QtOpenGLWidgets", + "missing_package": "pyopengl", + "import_error": error, } # Map missing/renamed methods - QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) - QTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) - QTextEdit.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) - QPlainTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) - QPlainTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) - QPlainTextEdit.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) - QApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QApplication, *args, **kwargs) + QTextEdit.setTabStopWidth = ( + lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) + ) + QTextEdit.tabStopWidth = ( + lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) + ) + QTextEdit.print_ = lambda self, *args, **kwargs: self.print( + *args, + **kwargs, + ) + QPlainTextEdit.setTabStopWidth = ( + lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) + ) + QPlainTextEdit.tabStopWidth = ( + lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) + ) + QPlainTextEdit.print_ = lambda self, *args, **kwargs: self.print( + *args, + **kwargs, + ) + QApplication.exec_ = lambda *args, **kwargs: possibly_static_exec( + QApplication, + *args, + **kwargs, + ) QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec(QMenu, *args, **kwargs) - QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom()) + QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec( + QMenu, + *args, + **kwargs, + ) + QLineEdit.getTextMargins = lambda self: ( + self.textMargins().left(), + self.textMargins().top(), + self.textMargins().right(), + self.textMargins().bottom(), + ) # Add removed definition for `QFileDialog.Options` as an alias of `QFileDialog.Option` # passing as default value 0 in the same way PySide6 6.5+ does. @@ -68,13 +104,14 @@ def __getattr__(name): # Allow unscoped access for enums inside the QtWidgets module from .enums_compat import promote_enums + promote_enums(QtWidgets) del QtWidgets elif PYSIDE2: from PySide2.QtWidgets import * elif PYSIDE6: - from PySide6.QtWidgets import * from PySide6.QtGui import QAction, QActionGroup, QShortcut, QUndoCommand + from PySide6.QtWidgets import * # Attempt to import QOpenGLWidget, but if that fails, # don't raise an exception until the name is explicitly accessed. @@ -82,59 +119,99 @@ def __getattr__(name): try: from PySide6.QtOpenGLWidgets import QOpenGLWidget except ImportError as error: - _missing_optional_names['QOpenGLWidget'] = { - 'name': 'PySide6.QtOpenGLWidgets', - 'missing_package': 'pyopengl', - 'import_error': error, + _missing_optional_names["QOpenGLWidget"] = { + "name": "PySide6.QtOpenGLWidgets", + "missing_package": "pyopengl", + "import_error": error, } # Map missing/renamed methods - QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) - QTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) - QPlainTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) - QPlainTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) - QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom()) + QTextEdit.setTabStopWidth = ( + lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) + ) + QTextEdit.tabStopWidth = ( + lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) + ) + QPlainTextEdit.setTabStopWidth = ( + lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) + ) + QPlainTextEdit.tabStopWidth = ( + lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) + ) + QLineEdit.getTextMargins = lambda self: ( + self.textMargins().left(), + self.textMargins().top(), + self.textMargins().right(), + self.textMargins().bottom(), + ) # Map DeprecationWarning methods - QApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QApplication, *args, **kwargs) + QApplication.exec_ = lambda *args, **kwargs: possibly_static_exec( + QApplication, + *args, + **kwargs, + ) QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec(QMenu, *args, **kwargs) + QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec( + QMenu, + *args, + **kwargs, + ) # Passing as default value 0 in the same way PySide6 < 6.3.2 does for the `QFileDialog.Options` definition. - if parse(_qt_version) > parse('6.3'): + if parse(_qt_version) > parse("6.3"): QFileDialog.Options = lambda value=0: QFileDialog.Option(value) if PYSIDE2 or PYSIDE6: # Make PySide2/6 `QFileDialog` static methods accept the `directory` kwarg as `dir` QFileDialog.getExistingDirectory = static_method_kwargs_wrapper( - QFileDialog.getExistingDirectory, "directory", "dir" + QFileDialog.getExistingDirectory, + "directory", + "dir", ) QFileDialog.getOpenFileName = static_method_kwargs_wrapper( - QFileDialog.getOpenFileName, "directory", "dir" + QFileDialog.getOpenFileName, + "directory", + "dir", ) QFileDialog.getOpenFileNames = static_method_kwargs_wrapper( - QFileDialog.getOpenFileNames, "directory", "dir" + QFileDialog.getOpenFileNames, + "directory", + "dir", ) QFileDialog.getSaveFileName = static_method_kwargs_wrapper( - QFileDialog.getSaveFileName, "directory", "dir" + QFileDialog.getSaveFileName, + "directory", + "dir", ) else: # Make PyQt5/6 `QFileDialog` static methods accept the `dir` kwarg as `directory` QFileDialog.getExistingDirectory = static_method_kwargs_wrapper( - QFileDialog.getExistingDirectory, "dir", "directory" + QFileDialog.getExistingDirectory, + "dir", + "directory", ) QFileDialog.getOpenFileName = static_method_kwargs_wrapper( - QFileDialog.getOpenFileName, "dir", "directory" + QFileDialog.getOpenFileName, + "dir", + "directory", ) QFileDialog.getOpenFileNames = static_method_kwargs_wrapper( - QFileDialog.getOpenFileNames, "dir", "directory" + QFileDialog.getOpenFileNames, + "dir", + "directory", ) QFileDialog.getSaveFileName = static_method_kwargs_wrapper( - QFileDialog.getSaveFileName, "dir", "directory" + QFileDialog.getSaveFileName, + "dir", + "directory", ) # Make `addAction` compatible with Qt6 >= 6.3 -if PYQT5 or PYSIDE2 or parse(_qt_version) < parse('6.3'): +if PYQT5 or PYSIDE2 or parse(_qt_version) < parse("6.3"): QMenu.addAction = partialmethod(add_action, old_add_action=QMenu.addAction) - QToolBar.addAction = partialmethod(add_action, old_add_action=QToolBar.addAction) + QToolBar.addAction = partialmethod( + add_action, + old_add_action=QToolBar.addAction, + ) diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index 0396f953..bf2fb785 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -14,18 +14,18 @@ PYQT6, PYSIDE2, PYSIDE6, - QtModuleNotInQtVersionError, QtModuleNotInOSError, + QtModuleNotInQtVersionError, ) -if sys.platform == 'win32': +if sys.platform == "win32": if PYQT5: from PyQt5.QtWinExtras import * elif PYQT6: - raise QtModuleNotInQtVersionError(name='QtWinExtras') + raise QtModuleNotInQtVersionError(name="QtWinExtras") elif PYSIDE2: from PySide2.QtWinExtras import * elif PYSIDE6: - raise QtModuleNotInQtVersionError(name='QtWinExtras') + raise QtModuleNotInQtVersionError(name="QtWinExtras") else: - raise QtModuleNotInOSError(name='QtWinExtras') + raise QtModuleNotInOSError(name="QtWinExtras") diff --git a/qtpy/QtX11Extras.py b/qtpy/QtX11Extras.py index 51247c13..016727fb 100644 --- a/qtpy/QtX11Extras.py +++ b/qtpy/QtX11Extras.py @@ -14,18 +14,18 @@ PYQT6, PYSIDE2, PYSIDE6, - QtModuleNotInQtVersionError, QtModuleNotInOSError, + QtModuleNotInQtVersionError, ) -if sys.platform == 'linux': +if sys.platform == "linux": if PYQT5: from PyQt5.QtX11Extras import * elif PYQT6: - raise QtModuleNotInQtVersionError(name='QtX11Extras') + raise QtModuleNotInQtVersionError(name="QtX11Extras") elif PYSIDE2: from PySide2.QtX11Extras import * elif PYSIDE6: - raise QtModuleNotInQtVersionError(name='QtX11Extras') + raise QtModuleNotInQtVersionError(name="QtX11Extras") else: - raise QtModuleNotInOSError(name='QtX11Extras') + raise QtModuleNotInOSError(name="QtX11Extras") diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py index 5e63f448..a7e0b738 100644 --- a/qtpy/QtXmlPatterns.py +++ b/qtpy/QtXmlPatterns.py @@ -18,8 +18,8 @@ if PYQT5: from PyQt5.QtXmlPatterns import * elif PYQT6: - raise QtBindingMissingModuleError(name='QtXmlPatterns') + raise QtBindingMissingModuleError(name="QtXmlPatterns") elif PYSIDE2: from PySide2.QtXmlPatterns import * elif PYSIDE6: - raise QtBindingMissingModuleError(name='QtXmlPatterns') + raise QtBindingMissingModuleError(name="QtXmlPatterns") diff --git a/qtpy/__init__.py b/qtpy/__init__.py index ddc95bcb..48e9d84e 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -54,14 +54,16 @@ """ -from packaging.version import parse +import contextlib import os import platform import sys import warnings +from packaging.version import parse + # Version of QtPy -__version__ = '2.4.0.dev0' +__version__ = "2.4.0.dev0" class PythonQtError(RuntimeError): @@ -78,7 +80,8 @@ class PythonQtValueError(ValueError): class QtBindingsNotFoundError(PythonQtError, ImportError): """Error raised if no bindings could be selected.""" - _msg = 'No Qt bindings could be found' + + _msg = "No Qt bindings could be found" def __init__(self): super().__init__(self._msg) @@ -86,67 +89,72 @@ def __init__(self): class QtModuleNotFoundError(ModuleNotFoundError, PythonQtError): """Raised when a Python Qt binding submodule is not installed/supported.""" - _msg = 'The {name} module was not found.' - _msg_binding = '{binding}' - _msg_extra = '' + + _msg = "The {name} module was not found." + _msg_binding = "{binding}" + _msg_extra = "" def __init__(self, *, name, msg=None, **msg_kwargs): global API_NAME binding = self._msg_binding.format(binding=API_NAME) - msg = msg or f'{self._msg} {self._msg_extra}'.strip() + msg = msg or f"{self._msg} {self._msg_extra}".strip() msg = msg.format(name=name, binding=binding, **msg_kwargs) super().__init__(msg, name=name) class QtModuleNotInOSError(QtModuleNotFoundError): """Raised when a module is not supported on the current operating system.""" - _msg = '{name} does not exist on this operating system.' + + _msg = "{name} does not exist on this operating system." class QtModuleNotInQtVersionError(QtModuleNotFoundError): """Raised when a module is not implemented in the current Qt version.""" - _msg = '{name} does not exist in {version}.' + + _msg = "{name} does not exist in {version}." def __init__(self, *, name, msg=None, **msg_kwargs): global QT5, QT6 - version = 'Qt5' if QT5 else 'Qt6' + version = "Qt5" if QT5 else "Qt6" super().__init__(name=name, version=version) class QtBindingMissingModuleError(QtModuleNotFoundError): """Raised when a module is not supported by a given binding.""" - _msg_extra = 'It is not currently implemented in {binding}.' + + _msg_extra = "It is not currently implemented in {binding}." class QtModuleNotInstalledError(QtModuleNotFoundError): """Raise when a module is supported by the binding, but not installed.""" - _msg_extra = 'It must be installed separately' + + _msg_extra = "It must be installed separately" def __init__(self, *, missing_package=None, **superclass_kwargs): self.missing_package = missing_package if missing_package is not None: - self._msg_extra += ' as {missing_package}.' + self._msg_extra += " as {missing_package}." super().__init__(missing_package=missing_package, **superclass_kwargs) # Qt API environment variable name -QT_API = 'QT_API' +QT_API = "QT_API" # Names of the expected PyQt5 api -PYQT5_API = ['pyqt5'] +PYQT5_API = ["pyqt5"] -PYQT6_API = ['pyqt6'] +PYQT6_API = ["pyqt6"] # Names of the expected PySide2 api -PYSIDE2_API = ['pyside2'] +PYSIDE2_API = ["pyside2"] # Names of the expected PySide6 api -PYSIDE6_API = ['pyside6'] +PYSIDE6_API = ["pyside6"] # Minimum supported versions of Qt and the bindings -QT5_VERSION_MIN = PYQT5_VERSION_MIN = '5.9.0' -PYSIDE2_VERSION_MIN = '5.12.0' -QT6_VERSION_MIN = PYQT6_VERSION_MIN = PYSIDE6_VERSION_MIN = '6.2.0' +QT5_VERSION_MIN = PYQT5_VERSION_MIN = "5.9.0" +PYSIDE2_VERSION_MIN = "5.12.0" +QT6_VERSION_MIN = PYQT6_VERSION_MIN = PYSIDE6_VERSION_MIN = "6.2.0" QT_VERSION_MIN = QT5_VERSION_MIN PYQT_VERSION_MIN = PYQT5_VERSION_MIN @@ -155,14 +163,19 @@ def __init__(self, *, missing_package=None, **superclass_kwargs): # Detecting if a binding was specified by the user binding_specified = QT_API in os.environ -API_NAMES = {'pyqt5': 'PyQt5', 'pyside2': 'PySide2', - 'pyqt6': 'PyQt6', 'pyside6': 'PySide6'} -API = os.environ.get(QT_API, 'pyqt5').lower() +API_NAMES = { + "pyqt5": "PyQt5", + "pyside2": "PySide2", + "pyqt6": "PyQt6", + "pyside6": "PySide6", +} +API = os.environ.get(QT_API, "pyqt5").lower() initial_api = API if API not in API_NAMES: raise PythonQtValueError( - f'Specified QT_API={repr(QT_API.lower())} is not in valid options: ' - f'{API_NAMES}') + f"Specified QT_API={QT_API.lower()!r} is not in valid options: " + f"{API_NAMES}", + ) is_old_pyqt = is_pyqt46 = False QT5 = PYQT5 = True @@ -173,41 +186,49 @@ def __init__(self, *, missing_package=None, **superclass_kwargs): QT_VERSION = None # Unless `FORCE_QT_API` is set, use previously imported Qt Python bindings -if not os.environ.get('FORCE_QT_API'): - if 'PyQt5' in sys.modules: - API = initial_api if initial_api in PYQT5_API else 'pyqt5' - elif 'PySide2' in sys.modules: - API = initial_api if initial_api in PYSIDE2_API else 'pyside2' - elif 'PyQt6' in sys.modules: - API = initial_api if initial_api in PYQT6_API else 'pyqt6' - elif 'PySide6' in sys.modules: - API = initial_api if initial_api in PYSIDE6_API else 'pyside6' +if not os.environ.get("FORCE_QT_API"): + if "PyQt5" in sys.modules: + API = initial_api if initial_api in PYQT5_API else "pyqt5" + elif "PySide2" in sys.modules: + API = initial_api if initial_api in PYSIDE2_API else "pyside2" + elif "PyQt6" in sys.modules: + API = initial_api if initial_api in PYQT6_API else "pyqt6" + elif "PySide6" in sys.modules: + API = initial_api if initial_api in PYSIDE6_API else "pyside6" if API in PYQT5_API: try: - from PyQt5.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore - from PyQt5.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore + from PyQt5.QtCore import ( + PYQT_VERSION_STR as PYQT_VERSION, + ) + from PyQt5.QtCore import ( + QT_VERSION_STR as QT_VERSION, + ) QT5 = PYQT5 = True - if sys.platform == 'darwin': + if sys.platform == "darwin": macos_version = parse(platform.mac_ver()[0]) - if macos_version < parse('10.10'): - if parse(QT_VERSION) >= parse('5.9'): - raise PythonQtError("Qt 5.9 or higher only works in " - "macOS 10.10 or higher. Your " - "program will fail in this " - "system.") - elif macos_version < parse('10.11'): - if parse(QT_VERSION) >= parse('5.11'): - raise PythonQtError("Qt 5.11 or higher only works in " - "macOS 10.11 or higher. Your " - "program will fail in this " - "system.") + qt_ver = parse(QT_VERSION) + if macos_version < parse("10.10") and qt_ver >= parse("5.9"): + raise PythonQtError( + "Qt 5.9 or higher only works in " + "macOS 10.10 or higher. Your " + "program will fail in this " + "system.", + ) + elif macos_version < parse("10.11") and qt_ver >= parse("5.11"): + raise PythonQtError( + "Qt 5.11 or higher only works in " + "macOS 10.11 or higher. Your " + "program will fail in this " + "system.", + ) del macos_version + del qt_ver except ImportError: - API = 'pyside2' + API = "pyside2" else: os.environ[QT_API] = API @@ -219,31 +240,38 @@ def __init__(self, *, missing_package=None, **superclass_kwargs): PYQT5 = False QT5 = PYSIDE2 = True - if sys.platform == 'darwin': + if sys.platform == "darwin": macos_version = parse(platform.mac_ver()[0]) - if macos_version < parse('10.11'): - if parse(QT_VERSION) >= parse('5.11'): - raise PythonQtError("Qt 5.11 or higher only works in " - "macOS 10.11 or higher. Your " - "program will fail in this " - "system.") + qt_ver = parse(QT_VERSION) + if macos_version < parse("10.11") and qt_ver >= parse("5.11"): + raise PythonQtError( + "Qt 5.11 or higher only works in " + "macOS 10.11 or higher. Your " + "program will fail in this " + "system.", + ) del macos_version + del qt_ver except ImportError: - API = 'pyqt6' + API = "pyqt6" else: os.environ[QT_API] = API if API in PYQT6_API: try: - from PyQt6.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore - from PyQt6.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore + from PyQt6.QtCore import ( + PYQT_VERSION_STR as PYQT_VERSION, + ) + from PyQt6.QtCore import ( + QT_VERSION_STR as QT_VERSION, + ) QT5 = PYQT5 = False QT6 = PYQT6 = True except ImportError: - API = 'pyside6' + API = "pyside6" else: os.environ[QT_API] = API @@ -263,49 +291,48 @@ def __init__(self, *, missing_package=None, **superclass_kwargs): # If a correct API name is passed to QT_API and it could not be found, # switches to another and informs through the warning -if API != initial_api and binding_specified: +if initial_api != API and binding_specified: warnings.warn( - f'Selected binding {initial_api!r} could not be found; ' - f'falling back to {API!r}', + f"Selected binding {initial_api!r} could not be found; " + f"falling back to {API!r}", PythonQtWarning, + stacklevel=2, ) # Set display name of the Qt API API_NAME = API_NAMES[API] -try: +with contextlib.suppress(ImportError, PythonQtError): # QtDataVisualization backward compatibility (QtDataVisualization vs. QtDatavisualization) # Only available for Qt5 bindings > 5.9 on Windows from . import QtDataVisualization as QtDatavisualization # analysis:ignore -except (ImportError, PythonQtError): - pass def _warn_old_minor_version(name, old_version, min_version): """Warn if using a Qt or binding version no longer supported by QtPy.""" warning_message = ( - f'{name} version {old_version} is not supported by QtPy. ' - 'To ensure your application works correctly with QtPy, ' - f'please upgrade to {name} {min_version} or later.' + f"{name} version {old_version} is not supported by QtPy. " + "To ensure your application works correctly with QtPy, " + f"please upgrade to {name} {min_version} or later." ) - warnings.warn(warning_message, PythonQtWarning) + warnings.warn(warning_message, PythonQtWarning, stacklevel=2) # Warn if using an End of Life or unsupported Qt API/binding minor version if QT_VERSION: if QT5 and (parse(QT_VERSION) < parse(QT5_VERSION_MIN)): - _warn_old_minor_version('Qt5', QT_VERSION, QT5_VERSION_MIN) + _warn_old_minor_version("Qt5", QT_VERSION, QT5_VERSION_MIN) elif QT6 and (parse(QT_VERSION) < parse(QT6_VERSION_MIN)): - _warn_old_minor_version('Qt6', QT_VERSION, QT6_VERSION_MIN) + _warn_old_minor_version("Qt6", QT_VERSION, QT6_VERSION_MIN) if PYQT_VERSION: if PYQT5 and (parse(PYQT_VERSION) < parse(PYQT5_VERSION_MIN)): - _warn_old_minor_version('PyQt5', PYQT_VERSION, PYQT5_VERSION_MIN) + _warn_old_minor_version("PyQt5", PYQT_VERSION, PYQT5_VERSION_MIN) elif PYQT6 and (parse(PYQT_VERSION) < parse(PYQT6_VERSION_MIN)): - _warn_old_minor_version('PyQt6', PYQT_VERSION, PYQT6_VERSION_MIN) + _warn_old_minor_version("PyQt6", PYQT_VERSION, PYQT6_VERSION_MIN) elif PYSIDE_VERSION: if PYSIDE2 and (parse(PYSIDE_VERSION) < parse(PYSIDE2_VERSION_MIN)): - _warn_old_minor_version('PySide2', PYSIDE_VERSION, PYSIDE2_VERSION_MIN) + _warn_old_minor_version("PySide2", PYSIDE_VERSION, PYSIDE2_VERSION_MIN) elif PYSIDE6 and (parse(PYSIDE_VERSION) < parse(PYSIDE6_VERSION_MIN)): - _warn_old_minor_version('PySide6', PYSIDE_VERSION, PYSIDE6_VERSION_MIN) + _warn_old_minor_version("PySide6", PYSIDE_VERSION, PYSIDE6_VERSION_MIN) diff --git a/qtpy/_utils.py b/qtpy/_utils.py index fdf59b46..ec851fab 100644 --- a/qtpy/_utils.py +++ b/qtpy/_utils.py @@ -7,17 +7,21 @@ """Provides utility functions for use by QtPy itself.""" from functools import wraps +from typing import TYPE_CHECKING import qtpy +if TYPE_CHECKING: + from qtpy.QtWidgets import QAction + def _wrap_missing_optional_dep_error( - attr_error, - *, - import_error, - wrapper=qtpy.QtModuleNotInstalledError, - **wrapper_kwargs, - ): + attr_error, + *, + import_error, + wrapper=qtpy.QtModuleNotInstalledError, + **wrapper_kwargs, +): """Create a __cause__-chained wrapper error for a missing optional dep.""" qtpy_error = wrapper(**wrapper_kwargs) import_error.__cause__ = attr_error @@ -27,9 +31,14 @@ def _wrap_missing_optional_dep_error( def getattr_missing_optional_dep(name, module_name, optional_names): """Wrap AttributeError in a special error if it matches.""" - attr_error = AttributeError(f'module {module_name!r} has no attribute {name!r}') + attr_error = AttributeError( + f"module {module_name!r} has no attribute {name!r}", + ) if name in optional_names: - return _wrap_missing_optional_dep_error(attr_error, **optional_names[name]) + return _wrap_missing_optional_dep_error( + attr_error, + **optional_names[name], + ) return attr_error @@ -43,8 +52,8 @@ def possibly_static_exec(cls, *args, **kwargs): # A special case (`self.exec_()`) to avoid the function resolving error return args[0].exec() return args[0].exec(*args[1:], **kwargs) - else: - return cls.exec(*args, **kwargs) + + return cls.exec(*args, **kwargs) def possibly_static_exec_(cls, *args, **kwargs): @@ -57,15 +66,14 @@ def possibly_static_exec_(cls, *args, **kwargs): # A special case (`self.exec()`) to avoid the function resolving error return args[0].exec_() return args[0].exec_(*args[1:], **kwargs) - else: - return cls.exec_(*args, **kwargs) + + return cls.exec_(*args, **kwargs) def add_action(self, *args, old_add_action): """Re-order arguments of `addAction` to backport compatibility with Qt>=6.3.""" from qtpy.QtCore import QObject from qtpy.QtGui import QIcon, QKeySequence - from qtpy.QtWidgets import QAction action: QAction icon: QIcon @@ -73,11 +81,18 @@ def add_action(self, *args, old_add_action): shortcut: QKeySequence | QKeySequence.StandardKey | str | int receiver: QObject member: bytes - if all(isinstance(arg, t) - for arg, t in zip(args, [str, - (QKeySequence, QKeySequence.StandardKey, str, int), - QObject, - bytes])): + if all( + isinstance(arg, t) + for arg, t in zip( + args, + [ + str, + (QKeySequence, QKeySequence.StandardKey, str, int), + QObject, + bytes, + ], + ) + ): if len(args) == 2: text, shortcut = args action = old_add_action(self, text) @@ -92,12 +107,19 @@ def add_action(self, *args, old_add_action): else: return old_add_action(self, *args) return action - elif all(isinstance(arg, t) - for arg, t in zip(args, [QIcon, - str, - (QKeySequence, QKeySequence.StandardKey, str, int), - QObject, - bytes])): + if all( + isinstance(arg, t) + for arg, t in zip( + args, + [ + QIcon, + str, + (QKeySequence, QKeySequence.StandardKey, str, int), + QObject, + bytes, + ], + ) + ): if len(args) == 3: icon, text, shortcut = args action = old_add_action(self, icon, text) @@ -108,7 +130,14 @@ def add_action(self, *args, old_add_action): action.setShortcut(QKeySequence(shortcut)) elif len(args) == 5: icon, text, shortcut, receiver, member = args - action = old_add_action(self, icon, text, receiver, member, QKeySequence(shortcut)) + action = old_add_action( + self, + icon, + text, + receiver, + member, + QKeySequence(shortcut), + ) else: return old_add_action(self, *args) return action @@ -121,10 +150,12 @@ def static_method_kwargs_wrapper(func, from_kwarg_name, to_kwarg_name): Makes static methods accept the `from_kwarg_name` kwarg as `to_kwarg_name`. """ + @staticmethod @wraps(func) def _from_kwarg_name_to_kwarg_name_(*args, **kwargs): if from_kwarg_name in kwargs: kwargs[to_kwarg_name] = kwargs.pop(from_kwarg_name) return func(*args, **kwargs) + return _from_kwarg_name_to_kwarg_name_ diff --git a/qtpy/cli.py b/qtpy/cli.py index 2fb57716..95da0252 100644 --- a/qtpy/cli.py +++ b/qtpy/cli.py @@ -9,41 +9,47 @@ # Standard library imports import argparse -import textwrap import json +import textwrap def print_version(): """Print the current version of the package.""" import qtpy - print('QtPy version', qtpy.__version__) + + print("QtPy version", qtpy.__version__) def get_api_status(): """Get the status of each Qt API usage.""" import qtpy - return {name: qtpy.API == name for name in qtpy.API_NAMES} + return {name: name == qtpy.API for name in qtpy.API_NAMES} + def generate_mypy_args(): """Generate a string with always-true/false args to pass to mypy.""" - options = {False: '--always-false', True: '--always-true'} + options = {False: "--always-false", True: "--always-true"} apis_active = get_api_status() - mypy_args = ' '.join( - f'{options[is_active]}={name.upper()}' + return " ".join( + f"{options[is_active]}={name.upper()}" for name, is_active in apis_active.items() ) - return mypy_args def generate_pyright_config_json(): """Generate Pyright config to be used in `pyrightconfig.json`.""" apis_active = get_api_status() - return json.dumps({ - "defineConstant": {name.upper(): is_active for name, is_active in apis_active.items()} - }) + return json.dumps( + { + "defineConstant": { + name.upper(): is_active + for name, is_active in apis_active.items() + }, + }, + ) def generate_pyright_config_toml(): @@ -51,7 +57,8 @@ def generate_pyright_config_toml(): apis_active = get_api_status() return "[tool.pyright.defineConstant]\n" + "\n".join( - f"{name.upper()} = {str(is_active).lower()}" for name, is_active in apis_active.items() + f"{name.upper()} = {str(is_active).lower()}" + for name, is_active in apis_active.items() ) @@ -82,23 +89,28 @@ def print_pyright_configs(): def generate_arg_parser(): """Generate the argument parser for the dev CLI for QtPy.""" parser = argparse.ArgumentParser( - description='Features to support development with QtPy.', + description="Features to support development with QtPy.", ) parser.set_defaults(func=parser.print_help) parser.add_argument( - '--version', action='store_const', dest='func', const=print_version, - help='If passed, will print the version and exit', + "--version", + action="store_const", + dest="func", + const=print_version, + help="If passed, will print the version and exit", ) cli_subparsers = parser.add_subparsers( - title='Subcommands', help='Subcommand to run', metavar='Subcommand', + title="Subcommands", + help="Subcommand to run", + metavar="Subcommand", ) # Parser for the MyPy args subcommand mypy_args_parser = cli_subparsers.add_parser( - name='mypy-args', - help='Generate command line arguments for using mypy with QtPy.', + name="mypy-args", + help="Generate command line arguments for using mypy with QtPy.", formatter_class=argparse.RawTextHelpFormatter, description=textwrap.dedent( """ @@ -120,8 +132,8 @@ def generate_arg_parser(): # Parser for the Pyright config subcommand pyright_config_parser = cli_subparsers.add_parser( - name='pyright-config', - help='Generate Pyright config for using Pyright with QtPy.', + name="pyright-config", + help="Generate Pyright config for using Pyright with QtPy.", formatter_class=argparse.RawTextHelpFormatter, description=textwrap.dedent( """ @@ -145,9 +157,10 @@ def main(args=None): parser = generate_arg_parser() parsed_args = parser.parse_args(args=args) - reserved_params = {'func'} + reserved_params = {"func"} cleaned_args = { - key: value for key, value in vars(parsed_args).items() + key: value + for key, value in vars(parsed_args).items() if key not in reserved_params } parsed_args.func(**cleaned_args) diff --git a/qtpy/compat.py b/qtpy/compat.py index 325c0ddb..4c6d428f 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -13,10 +13,8 @@ PYSIDE2, PYSIDE6, ) - from .QtWidgets import QFileDialog - TEXT_TYPES = (str,) @@ -30,23 +28,26 @@ def to_text_string(obj, encoding=None): """Convert `obj` to (unicode) text string""" if encoding is None: return str(obj) - elif isinstance(obj, str): + if isinstance(obj, str): # In case this function is not used properly, this could happen return obj - else: - return str(obj, encoding) + + return str(obj, encoding) # ============================================================================= # QVariant conversion utilities # ============================================================================= PYQT_API_1 = False + + def to_qvariant(obj=None): # analysis:ignore """Convert Python object to QVariant This is a transitional function from PyQt API#1 (QVariant exist) to PyQt API#2 and Pyside (QVariant does not exist)""" return obj + def from_qvariant(qobj=None, pytype=None): # analysis:ignore """Convert QVariant object to Python object This is a transitional function from PyQt API #1 (QVariant exist) @@ -57,8 +58,12 @@ def from_qvariant(qobj=None, pytype=None): # analysis:ignore # ============================================================================= # Wrappers around QFileDialog static methods # ============================================================================= -def getexistingdirectory(parent=None, caption='', basedir='', - options=QFileDialog.ShowDirsOnly): +def getexistingdirectory( + parent=None, + caption="", + basedir="", + options=QFileDialog.ShowDirsOnly, +): """Wrapper around QtGui.QFileDialog.getExistingDirectory static method Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" # Calling QFileDialog static method @@ -67,8 +72,12 @@ def getexistingdirectory(parent=None, caption='', basedir='', _temp1, _temp2 = sys.stdout, sys.stderr sys.stdout, sys.stderr = None, None try: - result = QFileDialog.getExistingDirectory(parent, caption, basedir, - options) + result = QFileDialog.getExistingDirectory( + parent, + caption, + basedir, + options, + ) finally: if sys.platform == "win32": # On Windows platforms: restore standard outputs @@ -79,8 +88,15 @@ def getexistingdirectory(parent=None, caption='', basedir='', return result -def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', - filters='', selectedfilter='', options=None): +def _qfiledialog_wrapper( + attr, + parent=None, + caption="", + basedir="", + filters="", + selectedfilter="", + options=None, +): if options is None: options = QFileDialog.Option(0) @@ -102,48 +118,85 @@ def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', return output, selectedfilter -def getopenfilename(parent=None, caption='', basedir='', filters='', - selectedfilter='', options=None): +def getopenfilename( + parent=None, + caption="", + basedir="", + filters="", + selectedfilter="", + options=None, +): """Wrapper around QtGui.QFileDialog.getOpenFileName static method Returns a tuple (filename, selectedfilter) -- when dialog box is canceled, returns a tuple of empty strings Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" - return _qfiledialog_wrapper('getOpenFileName', parent=parent, - caption=caption, basedir=basedir, - filters=filters, selectedfilter=selectedfilter, - options=options) - - -def getopenfilenames(parent=None, caption='', basedir='', filters='', - selectedfilter='', options=None): + return _qfiledialog_wrapper( + "getOpenFileName", + parent=parent, + caption=caption, + basedir=basedir, + filters=filters, + selectedfilter=selectedfilter, + options=options, + ) + + +def getopenfilenames( + parent=None, + caption="", + basedir="", + filters="", + selectedfilter="", + options=None, +): """Wrapper around QtGui.QFileDialog.getOpenFileNames static method Returns a tuple (filenames, selectedfilter) -- when dialog box is canceled, returns a tuple (empty list, empty string) Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" - return _qfiledialog_wrapper('getOpenFileNames', parent=parent, - caption=caption, basedir=basedir, - filters=filters, selectedfilter=selectedfilter, - options=options) - - -def getsavefilename(parent=None, caption='', basedir='', filters='', - selectedfilter='', options=None): + return _qfiledialog_wrapper( + "getOpenFileNames", + parent=parent, + caption=caption, + basedir=basedir, + filters=filters, + selectedfilter=selectedfilter, + options=options, + ) + + +def getsavefilename( + parent=None, + caption="", + basedir="", + filters="", + selectedfilter="", + options=None, +): """Wrapper around QtGui.QFileDialog.getSaveFileName static method Returns a tuple (filename, selectedfilter) -- when dialog box is canceled, returns a tuple of empty strings Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" - return _qfiledialog_wrapper('getSaveFileName', parent=parent, - caption=caption, basedir=basedir, - filters=filters, selectedfilter=selectedfilter, - options=options) + return _qfiledialog_wrapper( + "getSaveFileName", + parent=parent, + caption=caption, + basedir=basedir, + filters=filters, + selectedfilter=selectedfilter, + options=options, + ) + # ============================================================================= -def isalive(object): +def isalive(obj): """Wrapper around sip.isdeleted and shiboken.isValid which tests whether an object is currently alive.""" if PYQT5 or PYQT6: from . import sip - return not sip.isdeleted(object) - elif PYSIDE2 or PYSIDE6: + + return not sip.isdeleted(obj) + if PYSIDE2 or PYSIDE6: from . import shiboken - return shiboken.isValid(object) + + return shiboken.isValid(obj) + return None diff --git a/qtpy/enums_compat.py b/qtpy/enums_compat.py index 70263fa2..89a7d114 100644 --- a/qtpy/enums_compat.py +++ b/qtpy/enums_compat.py @@ -25,7 +25,7 @@ def promote_enums(module): and adapted to also copy enum values aliased under different names. """ - class_names = [name for name in dir(module) if name.startswith('Q')] + class_names = [name for name in dir(module) if name.startswith("Q")] for class_name in class_names: klass = getattr(module, class_name) if not isinstance(klass, sip.wrappertype): diff --git a/qtpy/shiboken.py b/qtpy/shiboken.py index e9d79320..3e20a0c8 100644 --- a/qtpy/shiboken.py +++ b/qtpy/shiboken.py @@ -15,13 +15,9 @@ QtBindingMissingModuleError, ) -if PYQT5: - raise QtBindingMissingModuleError(name='shiboken') -elif PYQT6: - raise QtBindingMissingModuleError(name='shiboken') +if PYQT5 or PYQT6: + raise QtBindingMissingModuleError(name="shiboken") elif PYSIDE2: from shiboken2 import * - import shiboken2 as shiboken elif PYSIDE6: from shiboken6 import * - import shiboken6 as shiboken diff --git a/qtpy/sip.py b/qtpy/sip.py index 89a31ff3..205538c7 100644 --- a/qtpy/sip.py +++ b/qtpy/sip.py @@ -19,7 +19,5 @@ from PyQt5.sip import * elif PYQT6: from PyQt6.sip import * -elif PYSIDE2: - raise QtBindingMissingModuleError(name='sip') -elif PYSIDE6: - raise QtBindingMissingModuleError(name='sip') +elif PYSIDE2 or PYSIDE6: + raise QtBindingMissingModuleError(name="sip") diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index f9b06f2b..f5c5f9aa 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -6,8 +6,8 @@ def pytest_configure(config): """Configure the test environment.""" - if 'USE_QT_API' in os.environ: - os.environ['QT_API'] = os.environ['USE_QT_API'].lower() + if "USE_QT_API" in os.environ: + os.environ["QT_API"] = os.environ["USE_QT_API"].lower() # We need to import qtpy here to make sure that the API versions get set # straight away. @@ -18,51 +18,56 @@ def pytest_report_header(config): """Insert a customized header into the test report.""" versions = os.linesep - versions += 'PyQt5: ' + versions += "PyQt5: " try: from PyQt5 import Qt + versions += f"PyQt: {Qt.PYQT_VERSION_STR} - Qt: {Qt.QT_VERSION_STR}" except ImportError: - versions += 'not installed' + versions += "not installed" except AttributeError: - versions += 'unknown version' + versions += "unknown version" versions += os.linesep - versions += 'PySide2: ' + versions += "PySide2: " try: import PySide2 from PySide2 import QtCore + versions += f"PySide: {PySide2.__version__} - Qt: {QtCore.__version__}" except ImportError: - versions += 'not installed' + versions += "not installed" except AttributeError: - versions += 'unknown version' + versions += "unknown version" versions += os.linesep - versions += 'PyQt6: ' + versions += "PyQt6: " try: from PyQt6 import QtCore - versions += \ + + versions += ( f"PyQt: {QtCore.PYQT_VERSION_STR} - Qt: {QtCore.QT_VERSION_STR}" + ) except ImportError: - versions += 'not installed' + versions += "not installed" except AttributeError: - versions += 'unknown version' + versions += "unknown version" versions += os.linesep - versions += 'PySide6: ' + versions += "PySide6: " try: import PySide6 from PySide6 import QtCore + versions += f"PySide: {PySide6.__version__} - Qt: {QtCore.__version__}" except ImportError: - versions += 'not installed' + versions += "not installed" except AttributeError: - versions += 'unknown version' + versions += "unknown version" versions += os.linesep @@ -72,8 +77,10 @@ def pytest_report_header(config): @pytest.fixture def pdf_writer(qtbot): from pathlib import Path + from qtpy import QtGui - output_path = Path('test.pdf') + + output_path = Path("test.pdf") device = QtGui.QPdfWriter(str(output_path)) yield device, output_path output_path.unlink() diff --git a/qtpy/tests/optional_deps/__init__.py b/qtpy/tests/optional_deps/__init__.py index 27c8c4c9..2c957446 100644 --- a/qtpy/tests/optional_deps/__init__.py +++ b/qtpy/tests/optional_deps/__init__.py @@ -5,8 +5,8 @@ from qtpy._utils import getattr_missing_optional_dep -from .optional_dep import ExampleClass +from .optional_dep import ExampleClass _missing_optional_names = {} @@ -14,14 +14,17 @@ try: from .optional_dep import MissingClass except ImportError as error: - _missing_optional_names['MissingClass'] = { - 'name': 'optional_dep.MissingClass', - 'missing_package': 'test_package_please_ignore', - 'import_error': error, + _missing_optional_names["MissingClass"] = { + "name": "optional_dep.MissingClass", + "missing_package": "test_package_please_ignore", + "import_error": error, } def __getattr__(name): """Custom getattr to chain and wrap errors due to missing optional deps.""" raise getattr_missing_optional_dep( - name, module_name=__name__, optional_names=_missing_optional_names) + name, + module_name=__name__, + optional_names=_missing_optional_names, + ) diff --git a/qtpy/tests/optional_deps/optional_dep.py b/qtpy/tests/optional_deps/optional_dep.py index 530af118..818152b6 100644 --- a/qtpy/tests/optional_deps/optional_dep.py +++ b/qtpy/tests/optional_deps/optional_dep.py @@ -1,4 +1,5 @@ """Test module with an optional dependency that may be missing.""" + class ExampleClass: pass diff --git a/qtpy/tests/test_cli.py b/qtpy/tests/test_cli.py index 6b3efe3f..4b449502 100644 --- a/qtpy/tests/test_cli.py +++ b/qtpy/tests/test_cli.py @@ -8,85 +8,95 @@ import qtpy - SUBCOMMANDS = [ [], - ['mypy-args'], + ["mypy-args"], ] @pytest.mark.parametrize( - argnames=['subcommand'], + argnames=["subcommand"], argvalues=[[subcommand] for subcommand in SUBCOMMANDS], - ids=[' '.join(subcommand) for subcommand in SUBCOMMANDS], + ids=[" ".join(subcommand) for subcommand in SUBCOMMANDS], ) def test_cli_help_does_not_fail(subcommand): subprocess.run( - [sys.executable, '-m', 'qtpy', *subcommand, '--help'], check=True, + [sys.executable, "-m", "qtpy", *subcommand, "--help"], + check=True, ) def test_cli_version(): output = subprocess.run( - [sys.executable, '-m', 'qtpy', '--version'], + [sys.executable, "-m", "qtpy", "--version"], capture_output=True, check=True, - encoding='utf-8', + encoding="utf-8", ) assert output.stdout.strip().split()[-1] == qtpy.__version__ def test_cli_mypy_args(): output = subprocess.run( - [sys.executable, '-m', 'qtpy', 'mypy-args'], + [sys.executable, "-m", "qtpy", "mypy-args"], capture_output=True, check=True, - encoding='utf-8', + encoding="utf-8", ) if qtpy.PYQT5: - expected = ' '.join([ - '--always-true=PYQT5', - '--always-false=PYSIDE2', - '--always-false=PYQT6', - '--always-false=PYSIDE6', - ]) + expected = " ".join( + [ + "--always-true=PYQT5", + "--always-false=PYSIDE2", + "--always-false=PYQT6", + "--always-false=PYSIDE6", + ], + ) elif qtpy.PYSIDE2: - expected = ' '.join([ - '--always-false=PYQT5', - '--always-true=PYSIDE2', - '--always-false=PYQT6', - '--always-false=PYSIDE6', - ]) + expected = " ".join( + [ + "--always-false=PYQT5", + "--always-true=PYSIDE2", + "--always-false=PYQT6", + "--always-false=PYSIDE6", + ], + ) elif qtpy.PYQT6: - expected = ' '.join([ - '--always-false=PYQT5', - '--always-false=PYSIDE2', - '--always-true=PYQT6', - '--always-false=PYSIDE6', - ]) + expected = " ".join( + [ + "--always-false=PYQT5", + "--always-false=PYSIDE2", + "--always-true=PYQT6", + "--always-false=PYSIDE6", + ], + ) elif qtpy.PYSIDE6: - expected = ' '.join([ - '--always-false=PYQT5', - '--always-false=PYSIDE2', - '--always-false=PYQT6', - '--always-true=PYSIDE6', - ]) + expected = " ".join( + [ + "--always-false=PYQT5", + "--always-false=PYSIDE2", + "--always-false=PYQT6", + "--always-true=PYSIDE6", + ], + ) else: - assert False, 'No valid API to test' + pytest.fail("No Qt bindings detected") assert output.stdout.strip() == expected.strip() + def test_cli_pyright_config(): output = subprocess.run( - [sys.executable, '-m', 'qtpy', 'pyright-config'], + [sys.executable, "-m", "qtpy", "pyright-config"], capture_output=True, check=True, - encoding='utf-8', + encoding="utf-8", ) if qtpy.PYQT5: - expected = textwrap.dedent(""" + expected = textwrap.dedent( + """ pyrightconfig.json: {"defineConstant": {"PYQT5": true, "PYSIDE2": false, "PYQT6": false, "PYSIDE6": false}} @@ -96,9 +106,11 @@ def test_cli_pyright_config(): PYSIDE2 = false PYQT6 = false PYSIDE6 = false - """) + """, + ) elif qtpy.PYSIDE2: - expected = textwrap.dedent(""" + expected = textwrap.dedent( + """ pyrightconfig.json: {"defineConstant": {"PYQT5": false, "PYSIDE2": true, "PYQT6": false, "PYSIDE6": false}} @@ -108,9 +120,11 @@ def test_cli_pyright_config(): PYSIDE2 = true PYQT6 = false PYSIDE6 = false - """) + """, + ) elif qtpy.PYQT6: - expected = textwrap.dedent(""" + expected = textwrap.dedent( + """ pyrightconfig.json: {"defineConstant": {"PYQT5": false, "PYSIDE2": false, "PYQT6": true, "PYSIDE6": false}} @@ -120,9 +134,11 @@ def test_cli_pyright_config(): PYSIDE2 = false PYQT6 = true PYSIDE6 = false - """) + """, + ) elif qtpy.PYSIDE6: - expected = textwrap.dedent(""" + expected = textwrap.dedent( + """ pyrightconfig.json: {"defineConstant": {"PYQT5": false, "PYSIDE2": false, "PYQT6": false, "PYSIDE6": true}} @@ -132,8 +148,9 @@ def test_cli_pyright_config(): PYSIDE2 = false PYQT6 = false PYSIDE6 = true - """) + """, + ) else: - assert False, 'No valid API to test' + pytest.fail("No valid API to test") assert output.stdout.strip() == expected.strip() diff --git a/qtpy/tests/test_compat.py b/qtpy/tests/test_compat.py index 7ca9d1de..1e1fc28f 100644 --- a/qtpy/tests/test_compat.py +++ b/qtpy/tests/test_compat.py @@ -1,19 +1,24 @@ """Test the compat module.""" -import pytest import sys -from qtpy import compat, QtWidgets +import pytest + +from qtpy import QtWidgets, compat from qtpy.tests.utils import not_using_conda + @pytest.mark.skipif( - ((sys.version_info.major == 3 and sys.version_info.minor == 7) - and sys.platform.startswith('win') and not not_using_conda()), - reason="sip not included in Python3.7 on Windows" + ( + (sys.version_info.major == 3 and sys.version_info.minor == 7) + and sys.platform.startswith("win") + and not not_using_conda() + ), + reason="sip not included in Python3.7 on Windows", ) def test_isalive(qtbot): """Test compat.isalive""" test_widget = QtWidgets.QWidget() - assert compat.isalive(test_widget) == True + assert compat.isalive(test_widget) is True with qtbot.waitSignal(test_widget.destroyed): test_widget.deleteLater() - assert compat.isalive(test_widget) == False + assert compat.isalive(test_widget) is False diff --git a/qtpy/tests/test_macos_checks.py b/qtpy/tests/test_macos_checks.py index 6993b315..e9698b56 100644 --- a/qtpy/tests/test_macos_checks.py +++ b/qtpy/tests/test_macos_checks.py @@ -1,108 +1,106 @@ -from unittest import mock +import contextlib import platform import sys +from unittest import mock import pytest + from qtpy import PYQT5, PYSIDE2 @pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") -@mock.patch.object(platform, 'mac_ver') +@mock.patch.object(platform, "mac_ver") def test_qt59_exception(mac_ver, monkeypatch): # Remove qtpy to reimport it again - try: + with contextlib.suppress(KeyError): del sys.modules["qtpy"] - except KeyError: - pass # Patch stdlib to emulate a macOS system - monkeypatch.setattr("sys.platform", 'darwin') - mac_ver.return_value = ('10.9.2',) + monkeypatch.setattr("sys.platform", "darwin") + mac_ver.return_value = ("10.9.2",) # Patch Qt version - monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.9.1') + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", "5.9.1") # This should raise an Exception with pytest.raises(Exception) as e: import qtpy - assert '10.10' in str(e.value) - assert '5.9' in str(e.value) + assert "10.10" in str(e.value) + assert "5.9" in str(e.value) @pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") -@mock.patch.object(platform, 'mac_ver') +@mock.patch.object(platform, "mac_ver") def test_qt59_no_exception(mac_ver, monkeypatch): # Remove qtpy to reimport it again - try: + with contextlib.suppress(KeyError): del sys.modules["qtpy"] - except KeyError: - pass # Patch stdlib to emulate a macOS system - monkeypatch.setattr("sys.platform", 'darwin') - mac_ver.return_value = ('10.10.1',) + monkeypatch.setattr("sys.platform", "darwin") + mac_ver.return_value = ("10.10.1",) # Patch Qt version - monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.9.5') + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", "5.9.5") # This should not raise an Exception try: import qtpy - except Exception: + except Exception: # noqa: BLE001 pytest.fail("Error!") -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), - reason="Targeted to PyQt5 or PySide2") -@mock.patch.object(platform, 'mac_ver') +@pytest.mark.skipif( + not (PYQT5 or PYSIDE2), + reason="Targeted to PyQt5 or PySide2", +) +@mock.patch.object(platform, "mac_ver") def test_qt511_exception(mac_ver, monkeypatch): # Remove qtpy to reimport it again - try: + with contextlib.suppress(KeyError): del sys.modules["qtpy"] - except KeyError: - pass # Patch stdlib to emulate a macOS system - monkeypatch.setattr("sys.platform", 'darwin') - mac_ver.return_value = ('10.10.3',) + monkeypatch.setattr("sys.platform", "darwin") + mac_ver.return_value = ("10.10.3",) # Patch Qt version if PYQT5: - monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", "5.11.1") else: - monkeypatch.setattr("PySide2.QtCore.__version__", '5.11.1') + monkeypatch.setattr("PySide2.QtCore.__version__", "5.11.1") # This should raise an Exception with pytest.raises(Exception) as e: import qtpy - assert '10.11' in str(e.value) - assert '5.11' in str(e.value) + assert "10.11" in str(e.value) + assert "5.11" in str(e.value) -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), - reason="Targeted to PyQt5 or PySide2") -@mock.patch.object(platform, 'mac_ver') +@pytest.mark.skipif( + not (PYQT5 or PYSIDE2), + reason="Targeted to PyQt5 or PySide2", +) +@mock.patch.object(platform, "mac_ver") def test_qt511_no_exception(mac_ver, monkeypatch): # Remove qtpy to reimport it again - try: + with contextlib.suppress(KeyError): del sys.modules["qtpy"] - except KeyError: - pass # Patch stdlib to emulate a macOS system - monkeypatch.setattr("sys.platform", 'darwin') - mac_ver.return_value = ('10.13.2',) + monkeypatch.setattr("sys.platform", "darwin") + mac_ver.return_value = ("10.13.2",) # Patch Qt version if PYQT5: - monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", "5.11.1") else: - monkeypatch.setattr("PySide2.QtCore.__version__", '5.11.1') + monkeypatch.setattr("PySide2.QtCore.__version__", "5.11.1") - # This should not raise an Exception + # This should not raise an Exception try: import qtpy - except Exception: + except Exception: # noqa: BLE001 pytest.fail("Error!") diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index f6c6efbf..771c4894 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -1,59 +1,69 @@ +import contextlib import os -import sys import subprocess +import sys import pytest -from qtpy import QtCore, QtGui, QtWidgets, API_NAMES, PythonQtValueError +from qtpy import API_NAMES, QtCore, QtGui, QtWidgets -try: +with contextlib.suppress(Exception): # removed in qt 6.0 from qtpy import QtWebEngineWidgets -except Exception: - pass + def assert_pyside2(): """ Make sure that we are using PySide """ import PySide2 + assert QtCore.QEvent is PySide2.QtCore.QEvent assert QtGui.QPainter is PySide2.QtGui.QPainter assert QtWidgets.QWidget is PySide2.QtWidgets.QWidget - assert QtWebEngineWidgets.QWebEnginePage is PySide2.QtWebEngineWidgets.QWebEnginePage - assert os.environ['QT_API'] == 'pyside2' + assert ( + QtWebEngineWidgets.QWebEnginePage + is PySide2.QtWebEngineWidgets.QWebEnginePage + ) + assert os.environ["QT_API"] == "pyside2" + def assert_pyside6(): """ Make sure that we are using PySide """ import PySide6 + assert QtCore.QEvent is PySide6.QtCore.QEvent assert QtGui.QPainter is PySide6.QtGui.QPainter assert QtWidgets.QWidget is PySide6.QtWidgets.QWidget # Only valid for qt>=6.2 # assert QtWebEngineWidgets.QWebEnginePage is PySide6.QtWebEngineCore.QWebEnginePage - assert os.environ['QT_API'] == 'pyside6' + assert os.environ["QT_API"] == "pyside6" + def assert_pyqt5(): """ Make sure that we are using PyQt5 """ import PyQt5 + assert QtCore.QEvent is PyQt5.QtCore.QEvent assert QtGui.QPainter is PyQt5.QtGui.QPainter assert QtWidgets.QWidget is PyQt5.QtWidgets.QWidget - assert os.environ['QT_API'] == 'pyqt5' + assert os.environ["QT_API"] == "pyqt5" + def assert_pyqt6(): """ Make sure that we are using PyQt6 """ import PyQt6 + assert QtCore.QEvent is PyQt6.QtCore.QEvent assert QtGui.QPainter is PyQt6.QtGui.QPainter assert QtWidgets.QWidget is PyQt6.QtWidgets.QWidget - assert os.environ['QT_API'] == 'pyqt6' + assert os.environ["QT_API"] == "pyqt6" def test_qt_api(): @@ -61,15 +71,15 @@ def test_qt_api(): If QT_API is specified, we check that the correct Qt wrapper was used """ - QT_API = os.environ.get('QT_API', '').lower() + QT_API = os.environ.get("QT_API", "").lower() - if QT_API == 'pyqt5': + if QT_API == "pyqt5": assert_pyqt5() - elif QT_API == 'pyside2': + elif QT_API == "pyside2": assert_pyside2() - elif QT_API == 'pyqt6': + elif QT_API == "pyqt6": assert_pyqt6() - elif QT_API == 'pyside6': + elif QT_API == "pyside6": assert_pyside6() else: # If the tests are run locally, USE_QT_API and QT_API may not be @@ -86,6 +96,7 @@ def test_qt_api(): import PyQt6 except ImportError: import PySide6 + assert_pyside6() else: assert_pyqt6() @@ -95,16 +106,16 @@ def test_qt_api(): assert_pyqt5() -@pytest.mark.parametrize('api', API_NAMES.values()) +@pytest.mark.parametrize("api", API_NAMES.values()) def test_qt_api_environ(api): """ If no QT_API is specified but some Qt is imported, ensure QT_API is set properly. """ - mod = f'{api}.QtCore' - pytest.importorskip(mod, reason=f'Requires {api}') + mod = f"{api}.QtCore" + pytest.importorskip(mod, reason=f"Requires {api}") # clean env env = os.environ.copy() - for key in ('QT_API', 'USE_QT_API'): + for key in ("QT_API", "USE_QT_API"): if key in env: del env[key] cmd = f""" @@ -114,12 +125,12 @@ def test_qt_api_environ(api): print(API) print(os.environ['QT_API']) """ - output = subprocess.check_output([sys.executable, '-c', cmd], env=env) - got_api, env_qt_api = output.strip().decode('utf-8').splitlines() + output = subprocess.check_output([sys.executable, "-c", cmd], env=env) + got_api, env_qt_api = output.strip().decode("utf-8").splitlines() assert got_api == api.lower() assert env_qt_api == api.lower() # Also ensure we raise a nice error - env['QT_API'] = 'bad' + env["QT_API"] = "bad" cmd = """ try: import qtpy @@ -128,4 +139,4 @@ def test_qt_api_environ(api): else: raise AssertionError('QtPy imported despite bad QT_API') """ - subprocess.check_call([sys.executable, '-Oc', cmd], env=env) + subprocess.check_call([sys.executable, "-Oc", cmd], env=env) diff --git a/qtpy/tests/test_missing_optional_deps.py b/qtpy/tests/test_missing_optional_deps.py index 8b8460c3..e1ca82b0 100644 --- a/qtpy/tests/test_missing_optional_deps.py +++ b/qtpy/tests/test_missing_optional_deps.py @@ -17,6 +17,6 @@ def test_missing_optional_deps(): with pytest.raises(QtModuleNotInstalledError) as excinfo: from .optional_deps import MissingClass - - msg = 'The optional_dep.MissingClass module was not found. It must be installed separately as test_package_please_ignore.' + + msg = "The optional_dep.MissingClass module was not found. It must be installed separately as test_package_please_ignore." assert msg == str(excinfo.value) diff --git a/qtpy/tests/test_qdesktopservice_split.py b/qtpy/tests/test_qdesktopservice_split.py index 27021762..98192884 100644 --- a/qtpy/tests/test_qdesktopservice_split.py +++ b/qtpy/tests/test_qdesktopservice_split.py @@ -2,7 +2,6 @@ import pytest -import warnings def test_qstandarpath(): @@ -12,8 +11,8 @@ def test_qstandarpath(): assert QStandardPaths.StandardLocation is not None # Attributes from QDesktopServices shouldn't be in QStandardPaths - with pytest.raises(AttributeError) as excinfo: - QStandardPaths.setUrlHandler + with pytest.raises(AttributeError): + QStandardPaths.setUrlHandler # noqa: B018 def test_qdesktopservice(): diff --git a/qtpy/tests/test_qt3danimation.py b/qtpy/tests/test_qt3danimation.py index 6eac3c33..23171b08 100644 --- a/qtpy/tests/test_qt3danimation.py +++ b/qtpy/tests/test_qt3danimation.py @@ -1,5 +1,4 @@ import pytest -from qtpy import PYQT5, PYSIDE2 def test_qt3danimation(): diff --git a/qtpy/tests/test_qt3dcore.py b/qtpy/tests/test_qt3dcore.py index cd17542e..7cae9167 100644 --- a/qtpy/tests/test_qt3dcore.py +++ b/qtpy/tests/test_qt3dcore.py @@ -1,4 +1,5 @@ import pytest + from qtpy import PYQT6, PYSIDE6 diff --git a/qtpy/tests/test_qt3drender.py b/qtpy/tests/test_qt3drender.py index c620721b..7f5d450a 100644 --- a/qtpy/tests/test_qt3drender.py +++ b/qtpy/tests/test_qt3drender.py @@ -1,4 +1,5 @@ import pytest + from qtpy import PYQT6, PYSIDE6 diff --git a/qtpy/tests/test_qtbluetooth.py b/qtpy/tests/test_qtbluetooth.py index c1f1e547..f9294e96 100644 --- a/qtpy/tests/test_qtbluetooth.py +++ b/qtpy/tests/test_qtbluetooth.py @@ -1,5 +1,4 @@ import pytest -from qtpy import PYSIDE2 def test_qtbluetooth(): diff --git a/qtpy/tests/test_qtcharts.py b/qtpy/tests/test_qtcharts.py index 4873e01f..4cce6f95 100644 --- a/qtpy/tests/test_qtcharts.py +++ b/qtpy/tests/test_qtcharts.py @@ -4,7 +4,8 @@ @pytest.mark.skipif( - not (PYSIDE2 or PYSIDE6), reason="Only available by default in PySide" + not (PYSIDE2 or PYSIDE6), + reason="Only available by default in PySide", ) def test_qtcharts(): """Test the qtpy.QtCharts namespace""" diff --git a/qtpy/tests/test_qtconcurrent.py b/qtpy/tests/test_qtconcurrent.py index 7bd0c4ca..114de183 100644 --- a/qtpy/tests/test_qtconcurrent.py +++ b/qtpy/tests/test_qtconcurrent.py @@ -1,5 +1,6 @@ import pytest from packaging.version import parse + from qtpy import PYSIDE2, PYSIDE_VERSION diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 8d376dbb..4f981565 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -10,8 +10,8 @@ from qtpy import ( PYQT5, PYQT6, - PYSIDE2, PYQT_VERSION, + PYSIDE2, PYSIDE_VERSION, QtCore, ) @@ -26,7 +26,7 @@ def test_qtmsghandler(): assert QtCore.qInstallMessageHandler is not None -@pytest.mark.parametrize('method', ['toPython', 'toPyDateTime']) +@pytest.mark.parametrize("method", ["toPython", "toPyDateTime"]) def test_QDateTime_toPython_and_toPyDateTime(method): """Test `QDateTime.toPython` and `QDateTime.toPyDateTime`""" q_datetime = QtCore.QDateTime(NOW) @@ -35,7 +35,7 @@ def test_QDateTime_toPython_and_toPyDateTime(method): assert py_datetime == NOW -@pytest.mark.parametrize('method', ['toPython', 'toPyDate']) +@pytest.mark.parametrize("method", ["toPython", "toPyDate"]) def test_QDate_toPython_and_toPyDate(method): """Test `QDate.toPython` and `QDate.toPyDate`""" q_date = QtCore.QDateTime(NOW).date() @@ -44,7 +44,7 @@ def test_QDate_toPython_and_toPyDate(method): assert py_date == NOW.date() -@pytest.mark.parametrize('method', ['toPython', 'toPyTime']) +@pytest.mark.parametrize("method", ["toPython", "toPyTime"]) def test_QTime_toPython_and_toPyTime(method): """Test `QTime.toPython` and `QTime.toPyTime`""" q_time = QtCore.QDateTime(NOW).time() @@ -70,10 +70,13 @@ def test_qthread_exec(): assert QtCore.QThread.exec is not None -@pytest.mark.skipif(PYSIDE2 and parse(PYSIDE_VERSION) < parse("5.15"), - reason="QEnum macro doesn't seem to be present on PySide2 <5.15") +@pytest.mark.skipif( + PYSIDE2 and parse(PYSIDE_VERSION) < parse("5.15"), + reason="QEnum macro doesn't seem to be present on PySide2 <5.15", +) def test_qenum(): """Test QEnum macro""" + class EnumTest(QtCore.QObject): class Position(enum.IntEnum): West = 0 @@ -90,7 +93,10 @@ class Position(enum.IntEnum): def test_QLibraryInfo_location_and_path(): """Test `QLibraryInfo.location` and `QLibraryInfo.path`""" assert QtCore.QLibraryInfo.location is not None - assert QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PrefixPath) is not None + assert ( + QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PrefixPath) + is not None + ) assert QtCore.QLibraryInfo.path is not None assert QtCore.QLibraryInfo.path(QtCore.QLibraryInfo.PrefixPath) is not None @@ -104,11 +110,15 @@ def test_QLibraryInfo_LibraryLocation_and_LibraryPath(): def test_QCoreApplication_exec_(qapp): """Test `QtCore.QCoreApplication.exec_`""" assert QtCore.QCoreApplication.exec_ is not None - app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication([sys.executable, __file__]) + app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication( + [sys.executable, __file__], + ) assert app is not None QtCore.QTimer.singleShot(100, QtCore.QCoreApplication.instance().quit) QtCore.QCoreApplication.exec_() - app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication([sys.executable, __file__]) + app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication( + [sys.executable, __file__], + ) assert app is not None QtCore.QTimer.singleShot(100, QtCore.QCoreApplication.instance().quit) app.exec_() @@ -117,26 +127,34 @@ def test_QCoreApplication_exec_(qapp): def test_QCoreApplication_exec(qapp): """Test `QtCore.QCoreApplication.exec`""" assert QtCore.QCoreApplication.exec is not None - app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication([sys.executable, __file__]) + app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication( + [sys.executable, __file__], + ) assert app is not None QtCore.QTimer.singleShot(100, QtCore.QCoreApplication.instance().quit) QtCore.QCoreApplication.exec() - app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication([sys.executable, __file__]) + app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication( + [sys.executable, __file__], + ) assert app is not None QtCore.QTimer.singleShot(100, QtCore.QCoreApplication.instance().quit) app.exec() -@pytest.mark.skipif(PYQT5 or PYQT6, - reason="Doesn't seem to be present on PyQt5 and PyQt6") +@pytest.mark.skipif( + PYQT5 or PYQT6, + reason="Doesn't seem to be present on PyQt5 and PyQt6", +) def test_qtextstreammanipulator_exec(): """Test `QTextStreamManipulator.exec_` and `QTextStreamManipulator.exec`""" assert QtCore.QTextStreamManipulator.exec_ is not None assert QtCore.QTextStreamManipulator.exec is not None -@pytest.mark.skipif(PYSIDE2 or PYQT6, - reason="Doesn't seem to be present on PySide2 and PyQt6") +@pytest.mark.skipif( + PYSIDE2 or PYQT6, + reason="Doesn't seem to be present on PySide2 and PyQt6", +) def test_QtCore_SignalInstance(): class ClassWithSignal(QtCore.QObject): signal = QtCore.Signal() @@ -146,12 +164,17 @@ class ClassWithSignal(QtCore.QObject): assert isinstance(instance.signal, QtCore.SignalInstance) -@pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), - reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" - "to work with scoped enum access") +@pytest.mark.skipif( + PYQT5 and PYQT_VERSION.startswith("5.9"), + reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" + "to work with scoped enum access", +) def test_enum_access(): """Test scoped and unscoped enum access for qtpy.QtCore.*.""" - assert QtCore.QAbstractAnimation.Stopped == QtCore.QAbstractAnimation.State.Stopped + assert ( + QtCore.QAbstractAnimation.Stopped + == QtCore.QAbstractAnimation.State.Stopped + ) assert QtCore.QEvent.ActionAdded == QtCore.QEvent.Type.ActionAdded assert QtCore.Qt.AlignLeft == QtCore.Qt.AlignmentFlag.AlignLeft assert QtCore.Qt.Key_Return == QtCore.Qt.Key.Key_Return @@ -159,12 +182,18 @@ def test_enum_access(): assert QtCore.Qt.Widget == QtCore.Qt.WindowType.Widget assert QtCore.Qt.BackButton == QtCore.Qt.MouseButton.BackButton assert QtCore.Qt.XButton1 == QtCore.Qt.MouseButton.XButton1 - assert QtCore.Qt.BackgroundColorRole == QtCore.Qt.ItemDataRole.BackgroundColorRole + assert ( + QtCore.Qt.BackgroundColorRole + == QtCore.Qt.ItemDataRole.BackgroundColorRole + ) assert QtCore.Qt.TextColorRole == QtCore.Qt.ItemDataRole.TextColorRole assert QtCore.Qt.MidButton == QtCore.Qt.MouseButton.MiddleButton -@pytest.mark.skipif(PYSIDE2 and PYSIDE_VERSION.startswith('5.12.0'), - reason="Utility functions unavailable for PySide2 5.12.0") + +@pytest.mark.skipif( + PYSIDE2 and PYSIDE_VERSION.startswith("5.12.0"), + reason="Utility functions unavailable for PySide2 5.12.0", +) def test_qtgui_namespace_mightBeRichText(): """ Test included elements (mightBeRichText) from module QtGui. diff --git a/qtpy/tests/test_qtdbus.py b/qtpy/tests/test_qtdbus.py index 6b9a9ae2..55946925 100644 --- a/qtpy/tests/test_qtdbus.py +++ b/qtpy/tests/test_qtdbus.py @@ -1,6 +1,4 @@ import pytest -import sys -from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 def test_qtdbus(): diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index 6978b6be..206390da 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -1,4 +1,5 @@ import pytest + from qtpy import PYSIDE2 diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 2b66d671..1aa4f47e 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -4,7 +4,15 @@ import pytest -from qtpy import PYQT5, PYQT_VERSION, PYSIDE2, PYSIDE6, QtCore, QtGui, QtWidgets +from qtpy import ( + PYQT5, + PYQT_VERSION, + PYSIDE2, + PYSIDE6, + QtCore, + QtGui, + QtWidgets, +) from qtpy.tests.utils import not_using_conda @@ -31,11 +39,15 @@ def test_qdrag_functions(qtbot): def test_QGuiApplication_exec_(): """Test `QtGui.QGuiApplication.exec_`""" assert QtGui.QGuiApplication.exec_ is not None - app = QtGui.QGuiApplication.instance() or QtGui.QGuiApplication([sys.executable, __file__]) + app = QtGui.QGuiApplication.instance() or QtGui.QGuiApplication( + [sys.executable, __file__], + ) assert app is not None QtCore.QTimer.singleShot(100, QtGui.QGuiApplication.instance().quit) QtGui.QGuiApplication.exec_() - app = QtGui.QGuiApplication.instance() or QtGui.QGuiApplication([sys.executable, __file__]) + app = QtGui.QGuiApplication.instance() or QtGui.QGuiApplication( + [sys.executable, __file__], + ) assert app is not None QtCore.QTimer.singleShot(100, QtGui.QGuiApplication.instance().quit) app.exec_() @@ -59,9 +71,11 @@ def test_qtextdocument_functions(pdf_writer): assert output_path.exists() -@pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), - reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" - "to work with scoped enum access") +@pytest.mark.skipif( + PYQT5 and PYQT_VERSION.startswith("5.9"), + reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" + "to work with scoped enum access", +) def test_enum_access(): """Test scoped and unscoped enum access for qtpy.QtWidgets.*.""" assert QtGui.QColor.Rgb == QtGui.QColor.Spec.Rgb @@ -71,8 +85,9 @@ def test_enum_access(): @pytest.mark.skipif( - sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), - reason="Stalls on macOS CI with Python 3.7") + sys.platform == "darwin" and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7", +) def test_QSomethingEvent_pos_functions(qtbot): """ Test `QMouseEvent.pos` and related functions removed in Qt 6, @@ -81,7 +96,9 @@ def test_QSomethingEvent_pos_functions(qtbot): class Window(QtWidgets.QMainWindow): def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None: - assert event.globalPos() - event.pos() == self.mapToParent(QtCore.QPoint(0, 0)) + assert event.globalPos() - event.pos() == self.mapToParent( + QtCore.QPoint(0, 0), + ) assert event.pos().x() == event.x() assert event.pos().y() == event.y() assert event.globalPos().x() == event.globalX() @@ -103,38 +120,60 @@ def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None: # the rest of the functions are not actually tested # QSinglePointEvent (Qt6) child classes checks - for _class in ('QNativeGestureEvent', 'QEnterEvent', 'QTabletEvent'): - for _function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY', - 'position', 'globalPosition'): + for _class in ("QNativeGestureEvent", "QEnterEvent", "QTabletEvent"): + for _function in ( + "pos", + "x", + "y", + "globalPos", + "globalX", + "globalY", + "position", + "globalPosition", + ): assert hasattr(getattr(QtGui, _class), _function) # QHoverEvent checks - for _function in ('pos', 'x', 'y', 'position'): + for _function in ("pos", "x", "y", "position"): assert hasattr(QtGui.QHoverEvent, _function) # QDropEvent and child classes checks - for _class in ('QDropEvent', 'QDragMoveEvent', 'QDragEnterEvent'): - for _function in ('pos', 'posF', 'position'): + for _class in ("QDropEvent", "QDragMoveEvent", "QDragEnterEvent"): + for _function in ("pos", "posF", "position"): assert hasattr(getattr(QtGui, _class), _function) -@pytest.mark.skipif(not (PYSIDE2 or PYSIDE6), reason="PySide{2,6} specific test") +@pytest.mark.skipif( + not (PYSIDE2 or PYSIDE6), + reason="PySide{2,6} specific test", +) def test_qtextcursor_moveposition(): """Test monkeypatched QTextCursor.movePosition""" doc = QtGui.QTextDocument("foo bar baz") cursor = QtGui.QTextCursor(doc) assert not cursor.movePosition(QtGui.QTextCursor.Start) - assert cursor.movePosition(QtGui.QTextCursor.EndOfWord, mode=QtGui.QTextCursor.KeepAnchor) + assert cursor.movePosition( + QtGui.QTextCursor.EndOfWord, + mode=QtGui.QTextCursor.KeepAnchor, + ) assert cursor.selectedText() == "foo" assert cursor.movePosition(QtGui.QTextCursor.Start) - assert cursor.movePosition(QtGui.QTextCursor.WordRight, n=2, mode=QtGui.QTextCursor.KeepAnchor) + assert cursor.movePosition( + QtGui.QTextCursor.WordRight, + n=2, + mode=QtGui.QTextCursor.KeepAnchor, + ) assert cursor.selectedText() == "foo bar " assert cursor.movePosition(QtGui.QTextCursor.Start) assert cursor.position() == cursor.anchor() - assert cursor.movePosition(QtGui.QTextCursor.NextWord, QtGui.QTextCursor.KeepAnchor, 3) + assert cursor.movePosition( + QtGui.QTextCursor.NextWord, + QtGui.QTextCursor.KeepAnchor, + 3, + ) assert cursor.selectedText() == "foo bar baz" diff --git a/qtpy/tests/test_qthelp.py b/qtpy/tests/test_qthelp.py index 9b78912d..1107bc57 100644 --- a/qtpy/tests/test_qthelp.py +++ b/qtpy/tests/test_qthelp.py @@ -1,9 +1,6 @@ """Test for QtHelp namespace.""" -import pytest - - def test_qthelp(): """Test the qtpy.QtHelp namespace.""" from qtpy import QtHelp diff --git a/qtpy/tests/test_qtlocation.py b/qtpy/tests/test_qtlocation.py index bb599153..f23a3887 100644 --- a/qtpy/tests/test_qtlocation.py +++ b/qtpy/tests/test_qtlocation.py @@ -1,8 +1,12 @@ import pytest + from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +@pytest.mark.skipif( + not (PYQT5 or PYSIDE2), + reason="Only available in Qt5 bindings", +) def test_qtlocation(): """Test the qtpy.QtLocation namespace""" from qtpy import QtLocation diff --git a/qtpy/tests/test_qtmacextras.py b/qtpy/tests/test_qtmacextras.py index 60e8788c..1f33b616 100644 --- a/qtpy/tests/test_qtmacextras.py +++ b/qtpy/tests/test_qtmacextras.py @@ -1,11 +1,15 @@ -import pytest import sys -from qtpy import PYQT6, PYSIDE2, PYSIDE6 +import pytest + +from qtpy import PYQT6, PYSIDE6 from qtpy.tests.utils import using_conda -@pytest.mark.skipif(PYQT6 or PYSIDE6, reason="Not available on Qt6-based bindings") +@pytest.mark.skipif( + PYQT6 or PYSIDE6, + reason="Not available on Qt6-based bindings", +) @pytest.mark.skipif( sys.platform != "darwin" or using_conda(), reason="Only available in Qt5 bindings > 5.9 with pip on mac in CIs", diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 29e78ae5..354bcd65 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -2,7 +2,7 @@ import pytest -from qtpy import PYSIDE6, PYQT6 +from qtpy import PYQT6, PYSIDE6 def test_qtmultimedia(): diff --git a/qtpy/tests/test_qtmultimediawidgets.py b/qtpy/tests/test_qtmultimediawidgets.py index 3226dc37..6f80e4d6 100644 --- a/qtpy/tests/test_qtmultimediawidgets.py +++ b/qtpy/tests/test_qtmultimediawidgets.py @@ -1,9 +1,7 @@ """Test QtMultimediaWidgets.""" -import pytest from qtpy import PYQT5, PYSIDE2 -from qtpy.tests.utils import using_conda def test_qtmultimediawidgets(): diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index eb8ec387..77b91d23 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -1,5 +1,4 @@ -import pytest -from qtpy import PYSIDE2, PYSIDE6, PYQT6, QtNetwork +from qtpy import PYQT6, PYSIDE2, PYSIDE6, QtNetwork def test_qtnetwork(): diff --git a/qtpy/tests/test_qtopengl.py b/qtpy/tests/test_qtopengl.py index 567fed3b..93bb5bb6 100644 --- a/qtpy/tests/test_qtopengl.py +++ b/qtpy/tests/test_qtopengl.py @@ -1,7 +1,3 @@ -import pytest -from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 - - def test_qtopengl(): """Test the qtpy.QtOpenGL namespace""" from qtpy import QtOpenGL diff --git a/qtpy/tests/test_qtopenglwidgets.py b/qtpy/tests/test_qtopenglwidgets.py index 8cde7296..2271e926 100644 --- a/qtpy/tests/test_qtopenglwidgets.py +++ b/qtpy/tests/test_qtopenglwidgets.py @@ -1,5 +1,6 @@ import pytest -from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 + +from qtpy import PYQT5, PYSIDE2 @pytest.mark.skipif(PYSIDE2 or PYQT5, reason="Not available in PySide2/PyQt5") diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py index 15a59620..adf8f45a 100644 --- a/qtpy/tests/test_qtpositioning.py +++ b/qtpy/tests/test_qtpositioning.py @@ -3,6 +3,7 @@ from qtpy import QT6 from qtpy.tests.utils import using_conda + @pytest.mark.skipif( QT6 and using_conda(), reason="QPositioning bindings not included in Conda qt-main >= 6.4.3.", diff --git a/qtpy/tests/test_qtqml.py b/qtpy/tests/test_qtqml.py index 13e8db5c..9baf91bf 100644 --- a/qtpy/tests/test_qtqml.py +++ b/qtpy/tests/test_qtqml.py @@ -1,5 +1,4 @@ -import pytest -from qtpy import PYQT5, PYSIDE2, PYSIDE6 +from qtpy import PYSIDE2, PYSIDE6 def test_qtqml(): diff --git a/qtpy/tests/test_qtquick.py b/qtpy/tests/test_qtquick.py index add3db52..ee7c1ed5 100644 --- a/qtpy/tests/test_qtquick.py +++ b/qtpy/tests/test_qtquick.py @@ -1,4 +1,3 @@ -import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qtquickwidgets.py b/qtpy/tests/test_qtquickwidgets.py index e4df1b9d..4765cc14 100644 --- a/qtpy/tests/test_qtquickwidgets.py +++ b/qtpy/tests/test_qtquickwidgets.py @@ -1,7 +1,3 @@ -import pytest -from qtpy import PYQT5, PYSIDE2 - - def test_qtquickwidgets(): """Test the qtpy.QtQuickWidgets namespace""" from qtpy import QtQuickWidgets diff --git a/qtpy/tests/test_qtremoteobjects.py b/qtpy/tests/test_qtremoteobjects.py index 4d91fb2f..db009eab 100644 --- a/qtpy/tests/test_qtremoteobjects.py +++ b/qtpy/tests/test_qtremoteobjects.py @@ -1,5 +1,4 @@ import pytest -from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 def test_qtremoteobjects(): diff --git a/qtpy/tests/test_qtsensors.py b/qtpy/tests/test_qtsensors.py index c78cd55e..b15dc361 100644 --- a/qtpy/tests/test_qtsensors.py +++ b/qtpy/tests/test_qtsensors.py @@ -1,5 +1,4 @@ import pytest -from qtpy import PYSIDE6, PYQT6 def test_qtsensors(): diff --git a/qtpy/tests/test_qtserialport.py b/qtpy/tests/test_qtserialport.py index b506a459..a50c0ea2 100644 --- a/qtpy/tests/test_qtserialport.py +++ b/qtpy/tests/test_qtserialport.py @@ -1,4 +1,5 @@ import pytest + from qtpy import PYSIDE2 diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index f9dcf96b..5be5ea4d 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -73,14 +73,14 @@ def test_qtsql_members_aliases(database_connection): INSERT INTO test (name) VALUES ( "TESTING" ) - """ + """, ) select_table_query = QtSql.QSqlQuery() select_table_query.prepare( """ SELECT * FROM test - """ + """, ) select_table_query.exec_() record = select_table_query.record() diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py index d1a5cce7..c39c95f4 100644 --- a/qtpy/tests/test_qtsvg.py +++ b/qtpy/tests/test_qtsvg.py @@ -1,5 +1,6 @@ import pytest -from qtpy import PYSIDE6, PYQT6 + +from qtpy import PYQT6, PYSIDE6 def test_qtsvg(): diff --git a/qtpy/tests/test_qttest.py b/qtpy/tests/test_qttest.py index 64554916..2d67439f 100644 --- a/qtpy/tests/test_qttest.py +++ b/qtpy/tests/test_qttest.py @@ -1,6 +1,7 @@ import pytest from packaging import version -from qtpy import QtTest, PYQT5, PYQT6, PYSIDE6, PYQT_VERSION + +from qtpy import PYQT5, PYQT6, PYQT_VERSION, PYSIDE6, QtTest def test_qttest(): @@ -10,13 +11,19 @@ def test_qttest(): if PYQT5 or PYQT6 or PYSIDE6: assert QtTest.QSignalSpy is not None - if (PYQT5 and version.parse(PYQT_VERSION) >= version.parse('5.11')) or PYQT6 or PYSIDE6: + if ( + (PYQT5 and version.parse(PYQT_VERSION) >= version.parse("5.11")) + or PYQT6 + or PYSIDE6 + ): assert QtTest.QAbstractItemModelTester is not None -@pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), - reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" - "to work with scoped enum access") +@pytest.mark.skipif( + PYQT5 and PYQT_VERSION.startswith("5.9"), + reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" + "to work with scoped enum access", +) def test_enum_access(): """Test scoped and unscoped enum access for qtpy.QtTest.*.""" assert QtTest.QTest.Click == QtTest.QTest.KeyAction.Click diff --git a/qtpy/tests/test_qttexttospeech.py b/qtpy/tests/test_qttexttospeech.py index f62bc3e0..bcb97f09 100644 --- a/qtpy/tests/test_qttexttospeech.py +++ b/qtpy/tests/test_qttexttospeech.py @@ -1,10 +1,14 @@ import pytest from packaging import version -from qtpy import PYQT5, PYSIDE2, PYQT_VERSION + +from qtpy import PYQT5, PYQT_VERSION, PYSIDE2 @pytest.mark.skipif( - not ((PYQT5 and version.parse(PYQT_VERSION) >= version.parse("5.15.1")) or PYSIDE2), + not ( + (PYQT5 and version.parse(PYQT_VERSION) >= version.parse("5.15.1")) + or PYSIDE2 + ), reason="Only available in Qt5 bindings (PyQt5 >= 5.15.1 or PySide2)", ) def test_qttexttospeech(): diff --git a/qtpy/tests/test_qtwebchannel.py b/qtpy/tests/test_qtwebchannel.py index 4337241f..8a364bae 100644 --- a/qtpy/tests/test_qtwebchannel.py +++ b/qtpy/tests/test_qtwebchannel.py @@ -1,7 +1,3 @@ -import pytest -from qtpy import PYQT5, PYSIDE2 - - def test_qtwebchannel(): """Test the qtpy.QtWebChannel namespace""" from qtpy import QtWebChannel diff --git a/qtpy/tests/test_qtwebenginecore.py b/qtpy/tests/test_qtwebenginecore.py index a00287ee..8f2b8c9a 100644 --- a/qtpy/tests/test_qtwebenginecore.py +++ b/qtpy/tests/test_qtwebenginecore.py @@ -1,5 +1,4 @@ import pytest -from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6 def test_qtwebenginecore(): diff --git a/qtpy/tests/test_qtwebenginequick.py b/qtpy/tests/test_qtwebenginequick.py index 50b4bc56..cfeda743 100644 --- a/qtpy/tests/test_qtwebenginequick.py +++ b/qtpy/tests/test_qtwebenginequick.py @@ -1,5 +1,6 @@ import pytest -from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6 + +from qtpy import PYQT5, PYSIDE2 @pytest.mark.skipif(PYQT5 or PYSIDE2, reason="Only available in Qt6 bindings") diff --git a/qtpy/tests/test_qtwebenginewidgets.py b/qtpy/tests/test_qtwebenginewidgets.py index 680de397..6f48fa7c 100644 --- a/qtpy/tests/test_qtwebenginewidgets.py +++ b/qtpy/tests/test_qtwebenginewidgets.py @@ -1,8 +1,12 @@ import pytest -from qtpy import PYSIDE6, PYQT6 +from qtpy import PYQT6, PYSIDE6 -@pytest.mark.skipif(PYSIDE6 or PYQT6, reason="Only available in Qt<6,>=6.2 bindings") + +@pytest.mark.skipif( + PYSIDE6 or PYQT6, + reason="Only available in Qt<6,>=6.2 bindings", +) def test_qtwebenginewidgets(): """Test the qtpy.QtWebEngineWidget namespace""" diff --git a/qtpy/tests/test_qtwebsockets.py b/qtpy/tests/test_qtwebsockets.py index e03440bf..ae169023 100644 --- a/qtpy/tests/test_qtwebsockets.py +++ b/qtpy/tests/test_qtwebsockets.py @@ -1,7 +1,3 @@ -import pytest -from qtpy import PYQT5, PYSIDE2 - - def test_qtwebsockets(): """Test the qtpy.QtWebSockets namespace""" from qtpy import QtWebSockets diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 5607ccf5..70c937a8 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -6,9 +6,17 @@ import pytest from pytestqt.exceptions import TimeoutError -from qtpy import PYQT5, PYQT_VERSION, QtCore, QtGui, QtWidgets, PYQT6, PYSIDE6, PYSIDE2 -from qtpy.tests.utils import using_conda, not_using_conda - +from qtpy import ( + PYQT5, + PYQT6, + PYQT_VERSION, + PYSIDE2, + PYSIDE6, + QtCore, + QtGui, + QtWidgets, +) +from qtpy.tests.utils import not_using_conda, using_conda def test_qtextedit_functions(qtbot, pdf_writer): @@ -54,19 +62,24 @@ def test_qplaintextedit_functions(qtbot, pdf_writer): def test_QApplication_exec_(): """Test `QtWidgets.QApplication.exec_`""" assert QtWidgets.QApplication.exec_ is not None - app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([sys.executable, __file__]) + app = QtWidgets.QApplication.instance() or QtWidgets.QApplication( + [sys.executable, __file__], + ) assert app is not None QtCore.QTimer.singleShot(100, QtWidgets.QApplication.instance().quit) QtWidgets.QApplication.exec_() - app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([sys.executable, __file__]) + app = QtWidgets.QApplication.instance() or QtWidgets.QApplication( + [sys.executable, __file__], + ) assert app is not None QtCore.QTimer.singleShot(100, QtWidgets.QApplication.instance().quit) app.exec_() @pytest.mark.skipif( - sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), - reason="Stalls on macOS CI with Python 3.7") + sys.platform == "darwin" and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7", +) def test_qdialog_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QDialog.exec_ @@ -76,15 +89,18 @@ def test_qdialog_functions(qtbot): @pytest.mark.skipif( - sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), - reason="Stalls on macOS CI with Python 3.7") + sys.platform == "darwin" and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7", +) def test_qdialog_subclass(qtbot): """Test functions mapping for QtWidgets.QDialog when using a subclass""" assert QtWidgets.QDialog.exec_ + class CustomDialog(QtWidgets.QDialog): def __init__(self): super().__init__(None) self.setWindowTitle("Testing") + assert CustomDialog.exec_ dialog = CustomDialog() QtCore.QTimer.singleShot(100, dialog.accept) @@ -92,16 +108,21 @@ def __init__(self): @pytest.mark.skipif( - sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), - reason="Stalls on macOS CI with Python 3.7") + sys.platform == "darwin" and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7", +) def test_QMenu_functions(qtbot): """Test functions mapping for `QtWidgets.QMenu`.""" # A window is required for static calls window = QtWidgets.QMainWindow() menu = QtWidgets.QMenu(window) - menu.addAction('QtPy') - menu.addAction('QtPy with a shortcut', QtGui.QKeySequence.UnknownKey) - menu.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.UnknownKey) + menu.addAction("QtPy") + menu.addAction("QtPy with a shortcut", QtGui.QKeySequence.UnknownKey) + menu.addAction( + QtGui.QIcon(), + "QtPy with an icon and a shortcut", + QtGui.QKeySequence.UnknownKey, + ) window.show() with qtbot.waitExposed(window): @@ -110,33 +131,55 @@ def test_QMenu_functions(qtbot): menu.exec_() # Call static `QMenu.exec_` - QtCore.QTimer.singleShot(100, lambda: qtbot.keyClick( - QtWidgets.QApplication.widgetAt(1, 1), - QtCore.Qt.Key_Escape) + QtCore.QTimer.singleShot( + 100, + lambda: qtbot.keyClick( + QtWidgets.QApplication.widgetAt(1, 1), + QtCore.Qt.Key_Escape, + ), ) QtWidgets.QMenu.exec_(menu.actions(), QtCore.QPoint(1, 1)) @pytest.mark.skipif( - sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), - reason="Stalls on macOS CI with Python 3.7") + sys.platform == "darwin" and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7", +) def test_QToolBar_functions(qtbot): """Test `QtWidgets.QToolBar.addAction` compatibility with Qt6 arguments' order.""" toolbar = QtWidgets.QToolBar() - toolbar.addAction('QtPy with a shortcut', QtGui.QKeySequence.UnknownKey) - toolbar.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.UnknownKey) + toolbar.addAction("QtPy with a shortcut", QtGui.QKeySequence.UnknownKey) + toolbar.addAction( + QtGui.QIcon(), + "QtPy with an icon and a shortcut", + QtGui.QKeySequence.UnknownKey, + ) -@pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), - reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" - "to work with scoped enum access") +@pytest.mark.skipif( + PYQT5 and PYQT_VERSION.startswith("5.9"), + reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" + "to work with scoped enum access", +) def test_enum_access(): """Test scoped and unscoped enum access for qtpy.QtWidgets.*.""" - assert QtWidgets.QFileDialog.AcceptOpen == QtWidgets.QFileDialog.AcceptMode.AcceptOpen - assert QtWidgets.QMessageBox.InvalidRole == QtWidgets.QMessageBox.ButtonRole.InvalidRole + assert ( + QtWidgets.QFileDialog.AcceptOpen + == QtWidgets.QFileDialog.AcceptMode.AcceptOpen + ) + assert ( + QtWidgets.QMessageBox.InvalidRole + == QtWidgets.QMessageBox.ButtonRole.InvalidRole + ) assert QtWidgets.QStyle.State_None == QtWidgets.QStyle.StateFlag.State_None - assert QtWidgets.QSlider.TicksLeft == QtWidgets.QSlider.TickPosition.TicksAbove - assert QtWidgets.QStyle.SC_SliderGroove == QtWidgets.QStyle.SubControl.SC_SliderGroove + assert ( + QtWidgets.QSlider.TicksLeft + == QtWidgets.QSlider.TickPosition.TicksAbove + ) + assert ( + QtWidgets.QStyle.SC_SliderGroove + == QtWidgets.QStyle.SubControl.SC_SliderGroove + ) def test_opengl_imports(): @@ -150,9 +193,11 @@ def test_opengl_imports(): @pytest.mark.skipif( - sys.platform == 'darwin' and sys.version_info[:2] == (3, 7) and - (PYQT5 or PYSIDE2), - reason="Crashes on macOS with Python 3.7 with 'Illegal instruction: 4'") + sys.platform == "darwin" + and sys.version_info[:2] == (3, 7) + and (PYQT5 or PYSIDE2), + reason="Crashes on macOS with Python 3.7 with 'Illegal instruction: 4'", +) @pytest.mark.parametrize("keyword", ["dir", "directory"]) @pytest.mark.parametrize("instance", [True, False]) def test_qfiledialog_dir_compat(tmp_path, qtbot, keyword, instance): @@ -171,22 +216,26 @@ def test_qfiledialog_dir_compat(tmp_path, qtbot, keyword, instance): If True, the function is called on the instance of the QFileDialog, otherwise on the class. """ - + class CloseThread(QtCore.QThread): """ - On some implementations the `getExistingDirectory` functions starts own + On some implementations the `getExistingDirectory` functions starts own event loop that will not trigger QTimer started before the call. Until the dialog is closed the main event loop will be stopped. Because of this it is required to use the thread to interact with the dialog. """ + def run(self, allow_restart=True): sleep(0.5) need_restart = allow_restart app = QtWidgets.QApplication.instance() for dlg in app.topLevelWidgets(): - if not isinstance(dlg, QtWidgets.QFileDialog) or dlg.isHidden(): + if ( + not isinstance(dlg, QtWidgets.QFileDialog) + or dlg.isHidden() + ): continue # "when implemented this I try to use: # * dlg.close() - On Qt6 it will close the dialog, but it will @@ -200,8 +249,11 @@ def run(self, allow_restart=True): sleep(0.1) for dlg in app.topLevelWidgets(): # As described above, it may happen that dialog is not closed after first using enter. - # in such case we call `run` function again. The 0.5s sleep is enough for the second enter to close the dialog. - if not isinstance(dlg, QtWidgets.QFileDialog) or dlg.isHidden(): + # in such case we call `run` function again. The 0.5s sleep is enough for the second enter to close the dialog. + if ( + not isinstance(dlg, QtWidgets.QFileDialog) + or dlg.isHidden() + ): continue self.run(allow_restart=False) return @@ -210,20 +262,19 @@ def run(self, allow_restart=True): self.run() # We need to use the `DontUseNativeDialog` option to be able to interact - # with it from code. + # with it from code. try: opt = QtWidgets.QFileDialog.Option.DontUseNativeDialog except AttributeError: # old qt5 bindings opt = QtWidgets.QFileDialog.DontUseNativeDialog - + kwargs = { "caption": "Select a directory", keyword: str(tmp_path), "options": opt, } - thr = CloseThread() thr.start() qtbot.waitUntil(thr.isRunning, timeout=1000) @@ -236,5 +287,5 @@ def test_qfiledialog_flags_typedef(): """ Test existence of `QFlags