From 314c3d18d68dcd114fa552956827c1962a498279 Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Mon, 25 Nov 2024 12:55:07 +0100 Subject: [PATCH 01/10] CI: Add workflow to run tests with PyQt6/PySide6 For now the tests run on Linux only. Only pip-based installation is used as conda has no PyQt6 package yet. The workflow file based on the existing one for Linux. --- .github/scripts/install.sh | 6 +- .github/workflows/test-linux-qt6.yml | 160 +++++++++++++++++++++++++++ setup.py | 4 + 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test-linux-qt6.yml diff --git a/.github/scripts/install.sh b/.github/scripts/install.sh index 845ad97e9d7..fda3846205c 100755 --- a/.github/scripts/install.sh +++ b/.github/scripts/install.sh @@ -23,6 +23,11 @@ fi # Install dependencies if [ "$USE_CONDA" = "true" ]; then + if [ -n "$SPYDER_QT_BINDING" ]; then + # conda has no PyQt6 package + echo "Cannot use Qt 6 with Conda" 1>&2 + exit 1 + fi # Install dependencies per operating system if [ "$OS" = "win" ]; then @@ -69,7 +74,6 @@ else if [ "$OS" = "linux" ]; then pip install jedi==0.19.1 fi - fi # Install subrepos from source diff --git a/.github/workflows/test-linux-qt6.yml b/.github/workflows/test-linux-qt6.yml new file mode 100644 index 00000000000..94944d8469c --- /dev/null +++ b/.github/workflows/test-linux-qt6.yml @@ -0,0 +1,160 @@ +name: Linux tests with PyQt6/PySide6 + +on: + push: + branches: + - master + - qt6_ci # for testing only + paths: + - '.github/scripts/*.sh' + - '.github/workflows/*.yml' + - 'requirements/*.yml' + - 'MANIFEST.in' + - '**.bat' + - '**.py' + - '**.sh' + - '!installers-conda/**' + - '!.github/workflows/installers-conda.yml' + - '!.github/workflows/build-subrepos.yml' + - '!.github/workflows/purge-cache.yml' + - '!.github/scripts/installer_test.sh' + + workflow_call: + + workflow_dispatch: + inputs: + ssh: + # github_cli: gh workflow run test-linux.yml --ref -f ssh=true + description: 'Enable ssh debugging' + required: false + default: false + type: boolean + +concurrency: + group: test-linux-${{ github.ref }} + cancel-in-progress: true + +env: + ENABLE_SSH: ${{ github.event_name == 'workflow_dispatch' && inputs.ssh }} + +jobs: + build: + # Use this to disable the workflow + # if: false + name: Linux - Py${{ matrix.PYTHON_VERSION }}, ${{ matrix.SPYDER_QT_BINDING }}, ${{ matrix.INSTALL_TYPE }}, ${{ matrix.TEST_TYPE }} + runs-on: ubuntu-20.04 + env: + CI: 'true' + QTCONSOLE_TESTING: 'true' + CODECOV_TOKEN: "56731c25-9b1f-4340-8b58-35739bfbc52d" + OS: 'linux' + PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} + RUN_SLOW: ${{ matrix.TEST_TYPE == 'slow' }} + USE_CONDA: ${{ matrix.INSTALL_TYPE == 'conda' }} + USE_GDB: 'false' + SPYDER_QT_BINDING: ${{ matrix.SPYDER_QT_BINDING }} + strategy: + fail-fast: false + matrix: + INSTALL_TYPE: ['pip'] # conda has no PyQt6 package + PYTHON_VERSION: ['3.10'] + TEST_TYPE: ['fast', 'slow'] + SPYDER_QT_BINDING: ['pyqt6', 'pyside6'] + timeout-minutes: 90 + steps: + - name: Setup Remote SSH Connection + if: env.ENABLE_SSH == 'true' + uses: mxschmitt/action-tmate@v3 + timeout-minutes: 60 + with: + detached: true + - name: Checkout Pull Requests + if: github.event_name == 'pull_request' + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Checkout Push + if: github.event_name != 'pull_request' + uses: actions/checkout@v4 + - name: Fetch branches + run: git fetch --prune --unshallow + - name: Install dependencies + shell: bash + run: | + sudo apt-get update --fix-missing + sudo apt-get install -qq pyqt5-dev-tools libxcb-xinerama0 libxcb-cursor0 xterm --fix-missing + - name: Cache conda + uses: actions/cache@v4 + env: + # Increase this value to reset cache if requirements/*.txt has not changed + CACHE_NUMBER: 1 + with: + path: ~/conda_pkgs_dir + key: ${{ runner.os }}-cacheconda-install${{ matrix.INSTALL_TYPE }}-${{ matrix.PYTHON_VERSION }}-${{ env.CACHE_NUMBER }}-${{ hashFiles('requirements/*.yml') }} + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-cachepip-install${{ matrix.INSTALL_TYPE }}-${{ env.CACHE_NUMBER }}-${{ hashFiles('setup.py') }} + - name: Create conda test environment + if: env.USE_CONDA == 'true' + uses: mamba-org/setup-micromamba@v1 + with: + micromamba-version: '1.5.10-0' + environment-file: requirements/main.yml + environment-name: test + cache-downloads: true + create-args: python=${{ matrix.PYTHON_VERSION }} + - name: Create pip test environment + if: env.USE_CONDA != 'true' + uses: mamba-org/setup-micromamba@v1 + with: + micromamba-version: '1.5.10-0' + environment-name: test + cache-downloads: true + create-args: python=${{ matrix.PYTHON_VERSION }} + condarc: | + channels: + - conda-forge + - name: Install additional dependencies + shell: bash -l {0} + run: bash -l .github/scripts/install.sh + - name: Show conda test environment + if: env.USE_CONDA == 'true' + shell: bash -l {0} + run: | + micromamba info + micromamba list + - name: Show pip test environment + if: env.USE_CONDA != 'true' + shell: bash -l {0} + run: | + micromamba info + micromamba list + pip list + - name: Run manifest checks + shell: bash -l {0} + run: check-manifest + - name: Run tests with gdb + if: env.USE_GDB == 'true' + shell: bash -l {0} + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: xvfb-run --auto-servernum gdb -return-child-result -batch -ex r -ex py-bt --args python runtests.py -s + - name: Run tests + shell: bash -l {0} + env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + QT_API: ${{ matrix.SPYDER_QT_BINDING }} + PYTEST_QT_API: ${{ matrix.SPYDER_QT_BINDING }} + run: | + rm -f pytest_log.txt # Must remove any log file from a previous run + # .github/scripts/run_tests.sh || \ + # .github/scripts/run_tests.sh || \ + # .github/scripts/run_tests.sh || \ + .github/scripts/run_tests.sh + - name: Coverage + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: false + verbose: true diff --git a/setup.py b/setup.py index 06039b9d6f2..36d70cf53ae 100644 --- a/setup.py +++ b/setup.py @@ -255,6 +255,10 @@ def run(self): 'pyqt6-webengine>=6.5,<7', 'qtconsole>=5.6.1,<5.7.0', ], + 'pyside6': [ + 'pyside6>=6.5,<7', + 'qtconsole>=5.6.1,<5.7.0', + ], 'conda-forge': [ 'qtconsole>=5.6.1,<5.7.0', ] From cf811cfff8c481966bb1231f7cc66392ee409864 Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Mon, 25 Nov 2024 14:41:23 +0100 Subject: [PATCH 02/10] Fix SetuptoolsDeprecationWarning issued by 'python -m build' Fix 'extras_require' in setup.py to fix the warning. This requires fixes for the parsing of dependencies in test_dependencies_in_sync.py: Use the 'packaging' module which simplifies the code. --- setup.py | 2 +- spyder/tests/test_dependencies_in_sync.py | 69 +++++++++-------------- 2 files changed, 28 insertions(+), 43 deletions(-) diff --git a/setup.py b/setup.py index 36d70cf53ae..51ad92f9ab6 100644 --- a/setup.py +++ b/setup.py @@ -332,7 +332,6 @@ def run(self): install_requires.append('qtconsole>=5.5.1,<5.7.0') extras_require = { - 'test:platform_system == "Windows"': ['pywin32'], 'test': [ 'coverage', 'cython', @@ -347,6 +346,7 @@ def run(self): 'pytest-order', 'pytest-qt', 'pytest-timeout', + 'pywin32;platform_system=="Windows"', 'pyyaml', 'scipy', 'sympy', diff --git a/spyder/tests/test_dependencies_in_sync.py b/spyder/tests/test_dependencies_in_sync.py index f12bc449bcb..caa85b3ac6a 100644 --- a/spyder/tests/test_dependencies_in_sync.py +++ b/spyder/tests/test_dependencies_in_sync.py @@ -9,6 +9,8 @@ import os.path as osp # Third party imports +from packaging.requirements import Requirement +from packaging.specifiers import SpecifierSet import pytest import yaml @@ -42,15 +44,11 @@ def parse_environment_yaml(fpath): elif dep == 'websockify': continue else: - parts = dep.split(' ') - if len(parts) > 1: - ver = parts[-1] - if ver[0] == '=': - ver = '=' + ver - - deps[parts[0]] = ver + req = Requirement(dep) + if req.specifier: + deps[req.name] = str(req.specifier) else: - deps[parts[0]] = None + deps[req.name] = None return deps @@ -66,10 +64,7 @@ def parse_spyder_dependencies(): ver = dep['required_version'] if ver: - if ';' in ver: - ver = ver.replace(';', ',') - elif ver[0] == '=': - ver = '=' + ver + ver = str(SpecifierSet(ver)) deps[dep['package_name'].lower()] = ver @@ -105,21 +100,16 @@ def parse_setup_install_requires(fpath): # Process dependencies deps = {} for dep in deps_list: - dep = dep.split(';')[0] - name, ver = None, None - - for sep in ['>=', '==', '<=', '<', '>']: - if sep in dep: - name, ver = dep.split(sep) - name = name.split('[')[0] # Discard e.g. [all] - ver = sep + ver # Include comparator - break + req = Requirement(dep) # Transform pypi to conda name - if name == 'pyqt5': - name = 'pyqt' + if req.name == 'pyqt5': + req.name = 'pyqt' - deps[name] = ver + if req.specifier: + deps[req.name] = str(req.specifier) + else: + deps[req.name] = None return deps @@ -147,27 +137,22 @@ def parse_setup_extra_requires(fpath): dep_list = dep_dict.get('test') dep_list = [item for item in dep_list if item[0] != '#'] for dep in dep_list: - dep = dep.split(';')[0] - name, ver = None, None - - for sep in ['>=', '==', '<=', '<', '>']: - if sep in dep: - idx = dep.index(sep) - name = dep[:idx] - ver = dep[idx:] - break - - if name is not None: - name = name.split('[')[0] - else: - name = dep.split('[')[0] + req = Requirement(dep) # Transform pypi to conda name - if name == 'pyqt5': - name = 'pyqt' + if req.name == 'pyqt5': + req.name = 'pyqt' + + if req.marker and not req.marker.evaluate(): + # Skip dependencies which are not required for current environment + # (e.g. skip 'platform_system=="Windows"' on Linux) + continue + + if req.specifier: + deps[req.name] = str(req.specifier) + else: + deps[req.name] = None - deps[name] = ver - print(deps) return deps From ddc4d669de468c524866c2750f7b4aa5b373bf03 Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Tue, 7 Jan 2025 11:39:27 +0100 Subject: [PATCH 03/10] editor: Fix object lifetime issue detected with PyQt6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set parent object for QTimer and don’t use singleShot() static method. This prevents the timer from being fired after the parent object is destroyed. The issue has been discovered with PyQt6 by test [1]. [1] spyder/plugins/editor/extensions/tests/test_closequotes.py::test_close_quotes --- spyder/plugins/editor/widgets/codeeditor/codeeditor.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spyder/plugins/editor/widgets/codeeditor/codeeditor.py b/spyder/plugins/editor/widgets/codeeditor/codeeditor.py index 8f40f35389a..8c03ea0db80 100644 --- a/spyder/plugins/editor/widgets/codeeditor/codeeditor.py +++ b/spyder/plugins/editor/widgets/codeeditor/codeeditor.py @@ -4585,8 +4585,11 @@ def delayed_popup_docstring(self): line_text = self.textCursor().block().text() pos = self.textCursor().position() - timer = QTimer() - timer.singleShot(300, lambda: self.popup_docstring(line_text, pos)) + timer = QTimer(self) + timer.setInterval(300) + timer.setSingleShot(True) + timer.timeout.connect(lambda: self.popup_docstring(line_text, pos)) + timer.start() def set_current_project_path(self, root_path=None): """ From 6ab0f8cf1cd0b43ecb7fda8924ddfe8d19fbcad1 Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Tue, 7 Jan 2025 13:43:37 +0100 Subject: [PATCH 04/10] editor: tests: Fix object lifetime issue detected with PyQt6 Some tests explicitly close the editorstack widget. qtbot also closes the widget after the test finishes. The latter action may fail when "delete on close" flag is set => reset the flag. --- .../editorstack/tests/test_editorstack_and_outline.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spyder/plugins/editor/widgets/editorstack/tests/test_editorstack_and_outline.py b/spyder/plugins/editor/widgets/editorstack/tests/test_editorstack_and_outline.py index 273f4f6a118..60a71ce390d 100644 --- a/spyder/plugins/editor/widgets/editorstack/tests/test_editorstack_and_outline.py +++ b/spyder/plugins/editor/widgets/editorstack/tests/test_editorstack_and_outline.py @@ -16,6 +16,7 @@ # Third party imports import pytest +from qtpy.QtCore import Qt # Local imports from spyder.config.base import running_in_ci @@ -75,6 +76,11 @@ def _create_editorstack(files): editorstack.save_dialog_on_tests = True editorstack.set_outlineexplorer(outlineexplorer) + # Some tests explicitly close the editorstack widget. qtbot also + # closes the widget after the test finishes. The latter action + # may fail when "delete on close" flag is set => clear the flag. + editorstack.setAttribute(Qt.WA_DeleteOnClose, False) + qtbot.addWidget(editorstack) editorstack.show() From e7e343380ccd9501cce34d5dfff24b3bb3b2ff40 Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Thu, 9 Jan 2025 17:07:08 +0100 Subject: [PATCH 05/10] fixup! Fix SetuptoolsDeprecationWarning issued by 'python -m build' --- spyder/tests/test_dependencies_in_sync.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spyder/tests/test_dependencies_in_sync.py b/spyder/tests/test_dependencies_in_sync.py index caa85b3ac6a..1286d03c637 100644 --- a/spyder/tests/test_dependencies_in_sync.py +++ b/spyder/tests/test_dependencies_in_sync.py @@ -143,9 +143,9 @@ def parse_setup_extra_requires(fpath): if req.name == 'pyqt5': req.name = 'pyqt' - if req.marker and not req.marker.evaluate(): - # Skip dependencies which are not required for current environment - # (e.g. skip 'platform_system=="Windows"' on Linux) + if req.marker: + # Skip dependencies with environment markers (e.g. + # 'pywin32;platform_system=="Windows"') continue if req.specifier: From 6face7a15d58aa189457ffbee2c046bb412d5988 Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Thu, 9 Jan 2025 17:27:27 +0100 Subject: [PATCH 06/10] tests: Skips tests failing with PyQt6 when PyQt6 is used --- spyder/app/tests/test_mainwindow.py | 13 ++++++++++++- .../ipythonconsole/tests/test_ipythonconsole.py | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index f977b90c2a0..445a41dd0af 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -36,7 +36,7 @@ from packaging.version import parse import pylint import pytest -from qtpy import PYQT_VERSION, PYQT5 +from qtpy import PYQT_VERSION, PYQT5, PYQT6 from qtpy.QtCore import QPoint, Qt, QTimer, QUrl from qtpy.QtGui import QImage, QTextCursor from qtpy.QtWidgets import QAction, QApplication, QInputDialog, QWidget @@ -423,6 +423,7 @@ def test_get_help_combo(main_window, qtbot): @pytest.mark.known_leak # Opens Spyder/QtWebEngine/Default/Cookies +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_get_help_ipython_console_dot_notation(main_window, qtbot, tmpdir): """ Test that Help works when called from the IPython console @@ -788,6 +789,7 @@ def test_runconfig_workdir(main_window, qtbot, tmpdir): @pytest.mark.no_new_console @flaky(max_runs=3) @pytest.mark.skipif(sys.platform == 'darwin', reason='Hangs sometimes on Mac') +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_dedicated_consoles(main_window, qtbot): """Test running code in dedicated consoles.""" @@ -1153,6 +1155,7 @@ def test_change_cwd_explorer(main_window, qtbot, tmpdir, test_directory): @flaky(max_runs=3) +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") @pytest.mark.skipif( (os.name == 'nt' or sys.platform == 'darwin' or parse(ipy_release.version) == parse('7.11.0')), @@ -1415,6 +1418,7 @@ def test_set_new_breakpoints(main_window, qtbot): @flaky(max_runs=3) +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") @pytest.mark.order(after="test_debug_unsaved_function") def test_run_code(main_window, qtbot, tmpdir): """Test all the different ways we have to run code""" @@ -1750,6 +1754,7 @@ def test_close_when_file_is_changed(main_window, qtbot): @flaky(max_runs=3) +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_maximize_minimize_plugins(main_window, qtbot): """Test that the maximize button is working as expected.""" # Wait until the window is fully up @@ -3656,6 +3661,7 @@ def data(cm, i, j): @flaky(max_runs=3) +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") @pytest.mark.order(after="test_debug_unsaved_function") def test_varexp_remove(main_window, qtbot, tmpdir): """ @@ -4393,6 +4399,7 @@ def test_post_mortem(main_window, qtbot, tmpdir): @flaky(max_runs=3) +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") @pytest.mark.order(after="test_debug_unsaved_function") def test_run_unsaved_file_multiprocessing(main_window, qtbot): """Test that we can run an unsaved file with multiprocessing.""" @@ -5600,6 +5607,7 @@ def test_func(): @flaky(max_runs=3) +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") @pytest.mark.skipif( os.name == 'nt', reason="ctypes.string_at(0) doesn't segfaults on Windows") @@ -5749,6 +5757,7 @@ def test_history_from_ipyconsole(main_window, qtbot): assert text.splitlines()[-1] == code +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_debug_unsaved_function(main_window, qtbot): """ Test that a breakpoint in an unsaved file is reached. @@ -5827,6 +5836,7 @@ def test_out_runfile_runcell(main_window, qtbot): @flaky(max_runs=3) +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") @pytest.mark.skipif( not sys.platform.startswith('linux'), reason="Does not work on Mac and Windows") @@ -6716,6 +6726,7 @@ def test_runfile_namespace(main_window, qtbot, tmpdir): @pytest.mark.skipif(os.name == "nt", reason="No quotes on Windows file paths") +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_quotes_rename_ipy(main_window, qtbot, tmp_path): """ Test that we can run files with quotes in name, renamed files, diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index 9313e74dc11..13244447985 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -27,6 +27,7 @@ import numpy as np from packaging.version import parse import pytest +from qtpy import PYQT6 from qtpy.QtCore import Qt from qtpy.QtGui import QTextCursor from qtpy.QtWebEngineWidgets import WEBENGINE @@ -160,6 +161,7 @@ def foo(x, y): @flaky(max_runs=3) @pytest.mark.auto_backend +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_auto_backend(ipyconsole, qtbot): """Test that the automatic backend was set correctly.""" # Wait until the window is fully up From 241fc9f12830f9ccdf745e0145436c17720b9603 Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Thu, 9 Jan 2025 17:28:45 +0100 Subject: [PATCH 07/10] squash! CI: Add workflow to run tests with PyQt6/PySide6 --- .github/workflows/test-linux-qt6.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-linux-qt6.yml b/.github/workflows/test-linux-qt6.yml index 94944d8469c..25bdeee9043 100644 --- a/.github/workflows/test-linux-qt6.yml +++ b/.github/workflows/test-linux-qt6.yml @@ -59,7 +59,7 @@ jobs: INSTALL_TYPE: ['pip'] # conda has no PyQt6 package PYTHON_VERSION: ['3.10'] TEST_TYPE: ['fast', 'slow'] - SPYDER_QT_BINDING: ['pyqt6', 'pyside6'] + SPYDER_QT_BINDING: ['pyqt6'] # TODO add 'pyside6' once Spyder supports it timeout-minutes: 90 steps: - name: Setup Remote SSH Connection From 0ea85221e4c0e2a11a5b72df5f94fb4c41664b28 Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Thu, 9 Jan 2025 17:36:29 +0100 Subject: [PATCH 08/10] fixup! tests: Skips tests failing with PyQt6 when PyQt6 is used --- .../plugins/editor/widgets/codeeditor/tests/test_codeeditor.py | 3 ++- .../editor/widgets/codeeditor/tests/test_decorations.py | 2 ++ spyder/plugins/editor/widgets/codeeditor/tests/test_goto.py | 3 +++ spyder/plugins/editor/widgets/editorstack/tests/test_save.py | 2 ++ spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py | 1 + spyder/plugins/run/tests/test_run.py | 2 ++ 6 files changed, 12 insertions(+), 1 deletion(-) diff --git a/spyder/plugins/editor/widgets/codeeditor/tests/test_codeeditor.py b/spyder/plugins/editor/widgets/codeeditor/tests/test_codeeditor.py index 0d6ca09635f..8e0a143001f 100644 --- a/spyder/plugins/editor/widgets/codeeditor/tests/test_codeeditor.py +++ b/spyder/plugins/editor/widgets/codeeditor/tests/test_codeeditor.py @@ -10,7 +10,7 @@ from unittest.mock import MagicMock # Third party imports -from qtpy import QT_VERSION +from qtpy import QT_VERSION, PYQT6 from qtpy.QtCore import Qt, QEvent, QPointF from qtpy.QtGui import QTextCursor, QMouseEvent from qtpy.QtWidgets import QApplication, QMainWindow, QTextEdit @@ -438,6 +438,7 @@ def test_editor_delete_selection(codeeditor, qtbot): @pytest.mark.skipif(QT_VERSION.startswith('5.15'), reason='Fixed on Qt 5.15') +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_qtbug35861(qtbot): """This test will detect if upstream QTBUG-35861 is fixed. If that happens, then the workarounds for spyder-ide/spyder#12663 diff --git a/spyder/plugins/editor/widgets/codeeditor/tests/test_decorations.py b/spyder/plugins/editor/widgets/codeeditor/tests/test_decorations.py index 4af4407dafe..b586edb0f6a 100644 --- a/spyder/plugins/editor/widgets/codeeditor/tests/test_decorations.py +++ b/spyder/plugins/editor/widgets/codeeditor/tests/test_decorations.py @@ -13,6 +13,7 @@ from flaky import flaky import pytest +from qtpy import PYTQ6 from qtpy.QtCore import Qt from qtpy.QtGui import QFont, QTextCursor @@ -93,6 +94,7 @@ def test_decorations(codeeditor, qtbot): @flaky(max_runs=10) +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_update_decorations_when_scrolling(qtbot): """ Test how many calls we're doing to update decorations when diff --git a/spyder/plugins/editor/widgets/codeeditor/tests/test_goto.py b/spyder/plugins/editor/widgets/codeeditor/tests/test_goto.py index aae5e9d0f14..5356060102b 100644 --- a/spyder/plugins/editor/widgets/codeeditor/tests/test_goto.py +++ b/spyder/plugins/editor/widgets/codeeditor/tests/test_goto.py @@ -10,6 +10,7 @@ import tempfile # Third party imports +from qtpy import PYQT6 from qtpy.QtCore import Qt, QPoint, QTimer from qtpy.QtGui import QDesktopServices, QTextCursor from qtpy.QtWidgets import QMessageBox @@ -28,6 +29,7 @@ TEST_FILE_REL = 'conftest.py' +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") @pytest.mark.parametrize('params', [ # Parameter, expected output 1, full file path, expected output 2 # ---------------------------------------------------------------- @@ -118,6 +120,7 @@ def test_goto_uri(qtbot, codeeditor, mocker, params): assert expected_output_2 == output_2 +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_goto_uri_project_root_path(qtbot, codeeditor, mocker, tmpdir): """Test that the uri search is working correctly.""" code_editor = codeeditor diff --git a/spyder/plugins/editor/widgets/editorstack/tests/test_save.py b/spyder/plugins/editor/widgets/editorstack/tests/test_save.py index 8315ee6a8e8..8face7b3c2d 100644 --- a/spyder/plugins/editor/widgets/editorstack/tests/test_save.py +++ b/spyder/plugins/editor/widgets/editorstack/tests/test_save.py @@ -17,6 +17,7 @@ # Third party imports from flaky import flaky import pytest +from qtpy import PYQT6 from qtpy.QtCore import Qt # Local imports @@ -211,6 +212,7 @@ def test_save(editor_bot, mocker): editor_stack.file_saved = save_file_saved +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_file_saved_in_other_editorstack(editor_splitter_layout_bot): """Test EditorStack.file_saved_in_other_editorstack().""" es = editor_splitter_layout_bot diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index 13244447985..779165c350c 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -1974,6 +1974,7 @@ def test_pdb_comprehension_namespace(ipyconsole, qtbot, tmpdir): @flaky(max_runs=10) @pytest.mark.auto_backend +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_restart_interactive_backend(ipyconsole, qtbot): """ Test that we ask for a restart or not after switching to different diff --git a/spyder/plugins/run/tests/test_run.py b/spyder/plugins/run/tests/test_run.py index 464db075b88..17e7878d881 100644 --- a/spyder/plugins/run/tests/test_run.py +++ b/spyder/plugins/run/tests/test_run.py @@ -18,6 +18,7 @@ # Third-part imports import pytest +from qtpy import PYQT6 from qtpy.QtCore import Signal, Qt from qtpy.QtWidgets import ( QAction, QWidget, QCheckBox, QLineEdit, QVBoxLayout, QHBoxLayout, QLabel) @@ -360,6 +361,7 @@ def run_mock(qtbot, tmpdir): return run, mock_main_window, temp_dir +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") def test_run_plugin(qtbot, run_mock): run, main_window, temp_cwd = run_mock From feb140e8eeedd9b6afee881fd27d89277b7c652b Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Thu, 9 Jan 2025 17:43:42 +0100 Subject: [PATCH 09/10] fixup! tests: Skips tests failing with PyQt6 when PyQt6 is used --- .../plugins/editor/widgets/codeeditor/tests/test_decorations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/plugins/editor/widgets/codeeditor/tests/test_decorations.py b/spyder/plugins/editor/widgets/codeeditor/tests/test_decorations.py index b586edb0f6a..38340029aa9 100644 --- a/spyder/plugins/editor/widgets/codeeditor/tests/test_decorations.py +++ b/spyder/plugins/editor/widgets/codeeditor/tests/test_decorations.py @@ -13,7 +13,7 @@ from flaky import flaky import pytest -from qtpy import PYTQ6 +from qtpy import PYQT6 from qtpy.QtCore import Qt from qtpy.QtGui import QFont, QTextCursor From 84a22a0c99d0ce3c2a97355885bd38731bb097c1 Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Thu, 9 Jan 2025 18:31:02 +0100 Subject: [PATCH 10/10] fixup! tests: Skips tests failing with PyQt6 when PyQt6 is used --- spyder/app/tests/test_mainwindow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 445a41dd0af..c00fb4055f1 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -3594,6 +3594,7 @@ def test_runcell_leading_indent(main_window, qtbot, tmpdir): @flaky(max_runs=3) +@pytest.mark.skipif(PYQT6, reason="Fails with PyQt6") @pytest.mark.order(after="test_debug_unsaved_function") def test_varexp_rename(main_window, qtbot, tmpdir): """