Skip to content

Commit 2268a81

Browse files
authored
Merge pull request spyder-ide#457 from jitseniesen/spy600b1
PR: Compatibility fixes for Spyder 6.0.0 beta 1
2 parents b171420 + e2fae39 commit 2268a81

File tree

9 files changed

+233
-89
lines changed

9 files changed

+233
-89
lines changed

.github/scripts/generate-without-spyder.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77
"""Script to generate requirements/without-spyder.txt"""
88

99
import re
10+
from pathlib import Path
1011

11-
with open('requirements/conda.txt') as infile:
12-
with open('requirements/without-spyder.txt', 'w') as outfile:
12+
rootdir = Path(__file__).parents[2]
13+
input_filename = rootdir / 'requirements' / 'conda.txt'
14+
output_filename = rootdir / 'requirements' / 'without-spyder.txt'
15+
16+
with open(input_filename) as infile:
17+
with open(output_filename, 'w') as outfile:
1318
for line in infile:
1419
package_name = re.match('[-a-z0-9_]*', line).group(0)
1520
if package_name != 'spyder':
1621
outfile.write(line)
17-

.github/workflows/run-tests.yml

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
fail-fast: false
1515
matrix:
1616
OS: ['ubuntu', 'macos', 'windows']
17-
PYTHON_VERSION: ['3.8', '3.9', '3.10']
17+
PYTHON_VERSION: ['3.9', '3.10', '3.11']
1818
SPYDER_SOURCE: ['git']
1919
name: ${{ matrix.OS }} py${{ matrix.PYTHON_VERSION }} spyder-from-${{ matrix.SPYDER_SOURCE }}
2020
runs-on: ${{ matrix.OS }}-latest
@@ -23,101 +23,97 @@ jobs:
2323
PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }}
2424
steps:
2525
- name: Checkout branch
26-
uses: actions/checkout@v3
26+
uses: actions/checkout@v4
27+
with:
28+
path: spyder-notebook
2729
- name: Install System Packages
2830
if: matrix.OS == 'ubuntu'
2931
run: |
3032
sudo apt-get update --fix-missing
3133
sudo apt-get install -qq pyqt5-dev-tools libxcb-xinerama0 xterm --fix-missing
3234
- name: Install Conda
33-
uses: conda-incubator/setup-miniconda@v2
35+
uses: conda-incubator/setup-miniconda@v3
3436
with:
35-
miniforge-variant: Mambaforge
36-
auto-update-conda: true
37-
python-version: ${{ matrix.PYTHON_VERSION }}
37+
auto-update-conda: true
38+
python-version: ${{ matrix.PYTHON_VERSION }}
3839
- name: Checkout Spyder from git
3940
if: matrix.SPYDER_SOURCE == 'git'
40-
uses: actions/checkout@v3
41+
uses: actions/checkout@v4
4142
with:
4243
repository: 'spyder-ide/spyder'
4344
path: 'spyder'
4445
- name: Install Spyder's dependencies (main)
4546
if: matrix.SPYDER_SOURCE == 'git'
4647
shell: bash -l {0}
47-
run: mamba env update --file spyder/requirements/main.yml
48+
run: conda env update --file spyder/requirements/main.yml
4849
- name: Install Spyder's dependencies (Linux)
4950
if: matrix.SPYDER_SOURCE == 'git' && matrix.OS == 'ubuntu'
5051
shell: bash -l {0}
51-
run: mamba env update --file spyder/requirements/linux.yml
52+
run: conda env update --file spyder/requirements/linux.yml
5253
- name: Install Spyder's dependencies (Mac / Windows)
5354
if: matrix.SPYDER_SOURCE == 'git' && matrix.OS != 'ubuntu'
5455
shell: bash -l {0}
55-
run: mamba env update --file spyder/requirements/${{ matrix.OS }}.yml
56+
run: conda env update --file spyder/requirements/${{ matrix.OS }}.yml
5657
- name: Install Spyder from source
5758
if: matrix.SPYDER_SOURCE == 'git'
5859
shell: bash -l {0}
5960
run: pip install --no-deps -e spyder
6061
- name: Install node.js
6162
shell: bash -l {0}
62-
run: mamba install nodejs -y
63+
run: conda install nodejs -y
6364
- name: Install plugin dependencies (without Spyder)
6465
if: matrix.SPYDER_SOURCE == 'git'
6566
shell: bash -l {0}
6667
run: |
67-
python .github/scripts/generate-without-spyder.py
68-
mamba install --file requirements/without-spyder.txt -y
68+
python spyder-notebook/.github/scripts/generate-without-spyder.py
69+
conda install --file spyder-notebook/requirements/without-spyder.txt -y
6970
- name: Install plugin dependencies
7071
if: matrix.SPYDER_SOURCE == 'conda'
7172
shell: bash -l {0}
72-
run: mamba install --file requirements/conda.txt -y
73+
run: conda install --file spyder-notebook/requirements/conda.txt -y
7374
- name: Install test dependencies
7475
shell: bash -l {0}
7576
run: |
76-
mamba install nomkl -y -q
77-
mamba install --file requirements/tests.txt -y
77+
conda install nomkl -y -q
78+
conda install --file spyder-notebook/requirements/tests.txt -y
7879
- name: Build JavaScript
7980
shell: bash -l {0}
8081
run: |
81-
cd spyder_notebook/server
82+
cd spyder-notebook/spyder_notebook/server
8283
jlpm install
8384
jlpm build
8485
- name: Install plugin
8586
shell: bash -l {0}
86-
run: pip install --no-deps -e .
87+
run: pip install --no-deps -e spyder-notebook
8788
- name: Show environment information
8889
shell: bash -l {0}
8990
run: |
90-
mamba info
91-
mamba list
91+
conda info
92+
conda list
9293
- name: Run tests (Linux)
9394
if: matrix.OS == 'ubuntu'
94-
uses: nick-fields/retry@v2
95+
uses: nick-fields/retry@v3
9596
with:
9697
timeout_minutes: 10
9798
max_attempts: 3
9899
shell: bash
99100
command: |
100101
. ~/.profile
101-
xvfb-run --auto-servernum pytest spyder_notebook --cov=spyder_notebook --cov-report=xml -vv
102+
xvfb-run --auto-servernum pytest spyder-notebook/spyder_notebook -vv
102103
- name: Run tests (MacOS)
103104
if: matrix.OS == 'macos'
104-
uses: nick-fields/retry@v2
105+
uses: nick-fields/retry@v3
105106
with:
106107
timeout_minutes: 10
107108
max_attempts: 3
108109
shell: bash
109110
command: |
110111
. ~/.profile
111-
pytest spyder_notebook -vv
112+
pytest spyder-notebook/spyder_notebook -vv
112113
- name: Run tests (Windows)
113114
if: matrix.OS == 'windows'
114-
uses: nick-fields/retry@v2
115+
uses: nick-fields/retry@v3
115116
with:
116117
timeout_minutes: 10
117118
max_attempts: 3
118-
command: pytest spyder_notebook -vv
119-
- name: Upload coverage to Codecov
120-
if: matrix.OS == 'ubuntu' && matrix.PYTHON_VERSION == '3.10'
121-
uses: codecov/codecov-action@v3
122-
with:
123-
token: ${{ secrets.CODECOV_TOKEN }}
119+
command: pytest spyder-notebook/spyder_notebook -vv

requirements/conda.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
spyder >=5.4.3,<6
1+
spyder >=6.0.0.dev0,<7
22
jupyter_core
33
jupyter_server
44
nbformat

spyder_notebook/tests/conftest.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# -*- coding: utf-8 -*-
2+
# ----------------------------------------------------------------------------
3+
# Copyright © Spyder Project Contributors
4+
#
5+
# Licensed under the terms of the MIT License
6+
# ----------------------------------------------------------------------------
7+
8+
"""
9+
Testing utilities to be used with pytest.
10+
"""
11+
12+
# Copied from conftest.py in main Spyder repo, with one change in
13+
# register_plugin() inside ConfigDialogTester.
14+
15+
# Standard library imports
16+
import sys
17+
import types
18+
from unittest.mock import Mock, MagicMock
19+
20+
# Third party imports
21+
from qtpy.QtGui import QIcon
22+
from qtpy.QtWidgets import QWidget, QMainWindow
23+
import pytest
24+
25+
# Local imports
26+
from spyder.api.plugins import Plugins
27+
from spyder.api.plugin_registration.registry import PLUGIN_REGISTRY
28+
from spyder.app.cli_options import get_options
29+
from spyder.config.manager import CONF
30+
from spyder.utils.icon_manager import ima
31+
32+
33+
class MainWindowMock(QMainWindow):
34+
register_shortcut = Mock()
35+
36+
def __init__(self, parent):
37+
# This import assumes that an QApplication is already running,
38+
# so we can not put it at the top of the file
39+
from spyder.plugins.preferences.plugin import Preferences
40+
41+
super().__init__(parent)
42+
self.default_style = None
43+
self.widgetlist = []
44+
self.thirdparty_plugins = []
45+
self.shortcut_data = []
46+
self.prefs_dialog_instance = None
47+
self._APPLICATION_TOOLBARS = MagicMock()
48+
49+
self.console = Mock()
50+
51+
# To provide command line options for plugins that need them
52+
sys_argv = [sys.argv[0]] # Avoid options passed to pytest
53+
self._cli_options = get_options(sys_argv)[0]
54+
55+
PLUGIN_REGISTRY.reset()
56+
PLUGIN_REGISTRY.sig_plugin_ready.connect(self.register_plugin)
57+
PLUGIN_REGISTRY.register_plugin(self, Preferences)
58+
59+
# Load shortcuts for tests
60+
for context, name, __ in CONF.iter_shortcuts():
61+
self.shortcut_data.append((None, context, name, None, None))
62+
63+
for attr in ['mem_status', 'cpu_status']:
64+
mock_attr = Mock()
65+
setattr(mock_attr, 'toolTip', lambda: '')
66+
setattr(mock_attr, 'setToolTip', lambda x: '')
67+
setattr(mock_attr, 'prefs_dialog_instance', lambda: '')
68+
setattr(self, attr, mock_attr)
69+
70+
def register_plugin(self, plugin_name, external=False):
71+
plugin = PLUGIN_REGISTRY.get_plugin(plugin_name)
72+
plugin._register(omit_conf=True)
73+
74+
def get_plugin(self, plugin_name, error=True):
75+
if plugin_name in PLUGIN_REGISTRY:
76+
return PLUGIN_REGISTRY.get_plugin(plugin_name)
77+
78+
79+
class ConfigDialogTester(QWidget):
80+
def __init__(self, parent, main_class,
81+
general_config_plugins, plugins):
82+
# This import assumes that an QApplication is already running,
83+
# so we can not put it at the top of the file
84+
from spyder.plugins.preferences.plugin import Preferences
85+
86+
super().__init__(parent)
87+
self._main = main_class(self) if main_class else None
88+
if self._main is None:
89+
self._main = MainWindowMock(self)
90+
91+
def register_plugin(self, plugin_name, external=False):
92+
plugin = PLUGIN_REGISTRY.get_plugin(plugin_name)
93+
94+
# If plugin.CONF_FILE is True, then plugin is registered in conf
95+
# in _instantiate_spyder5_plugin() so no need to go so here
96+
plugin._register(omit_conf=plugin.CONF_FILE)
97+
98+
def get_plugin(self, plugin_name, error=True):
99+
if plugin_name in PLUGIN_REGISTRY:
100+
return PLUGIN_REGISTRY.get_plugin(plugin_name)
101+
return None
102+
103+
setattr(self._main, 'register_plugin',
104+
types.MethodType(register_plugin, self._main))
105+
setattr(self._main, 'get_plugin',
106+
types.MethodType(get_plugin, self._main))
107+
108+
PLUGIN_REGISTRY.reset()
109+
PLUGIN_REGISTRY.sig_plugin_ready.connect(self._main.register_plugin)
110+
PLUGIN_REGISTRY.register_plugin(self._main, Preferences)
111+
112+
if plugins:
113+
for Plugin in plugins:
114+
if hasattr(Plugin, 'CONF_WIDGET_CLASS'):
115+
for required in (Plugin.REQUIRES or []):
116+
if required not in PLUGIN_REGISTRY:
117+
PLUGIN_REGISTRY.plugin_registry[required] = MagicMock()
118+
119+
PLUGIN_REGISTRY.register_plugin(self._main, Plugin)
120+
else:
121+
plugin = Plugin(self._main)
122+
preferences = self._main.get_plugin(Plugins.Preferences)
123+
preferences.register_plugin_preferences(plugin)
124+
125+
126+
@pytest.fixture
127+
def config_dialog(qtbot, request, mocker):
128+
mocker.patch.object(ima, 'icon', lambda x, *_: QIcon())
129+
main_class, general_config_plugins, plugins = request.param
130+
131+
main_ref = ConfigDialogTester(
132+
None, main_class, general_config_plugins, plugins)
133+
qtbot.addWidget(main_ref)
134+
135+
preferences = main_ref._main.get_plugin(Plugins.Preferences)
136+
preferences.open_dialog()
137+
container = preferences.get_container()
138+
dlg = container.dialog
139+
140+
yield dlg
141+
142+
dlg.close()

spyder_notebook/tests/test_config.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,32 @@
44
# Licensed under the terms of the MIT License
55
#
66

7-
"""Tests for config.py"""
7+
"""Tests for plugin config dialog."""
88

9-
# Third-party library imports
10-
import pytest
9+
from unittest.mock import MagicMock
1110

12-
# Spyder imports
13-
from spyder.plugins.preferences.widgets.configdialog import ConfigDialog
11+
# Test library imports
12+
import pytest
13+
from qtpy.QtWidgets import QMainWindow
1414

1515
# Local imports
16-
from spyder_notebook.confpage import NotebookConfigPage
17-
from spyder_notebook.tests.test_plugin import plugin_no_server
16+
from spyder_notebook.notebookplugin import NotebookPlugin
17+
1818

19+
class MainWindowMock(QMainWindow):
20+
register_shortcut = MagicMock()
21+
editor = MagicMock()
1922

20-
def test_config(plugin_no_server, qtbot):
21-
"""Test that config page can be created and shown."""
22-
dlg = ConfigDialog()
23-
page = NotebookConfigPage(plugin_no_server, parent=plugin_no_server.main)
24-
page.initialize()
25-
dlg.add_page(page)
26-
dlg.show()
27-
qtbot.addWidget(dlg)
28-
# no assert, just check that the config page can be created
23+
def __getattr__(self, attr):
24+
return MagicMock()
2925

3026

31-
if __name__ == "__main__":
32-
pytest.main()
27+
@pytest.mark.parametrize(
28+
'config_dialog',
29+
# [[MainWindowMock, [ConfigPlugins], [Plugins]]]
30+
[[MainWindowMock, [], [NotebookPlugin]]],
31+
indirect=True)
32+
def test_config_dialog(config_dialog):
33+
configpage = config_dialog.get_page()
34+
assert configpage
35+
configpage.save_to_conf()

spyder_notebook/tests/test_plugin.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -119,35 +119,6 @@ def fake_get_server(filename, interpreter, start):
119119
# =============================================================================
120120
# Tests
121121
# =============================================================================
122-
# Teardown sometimes fails on Mac with Python 3.8 due to NoProcessException
123-
# in shutdown_server() in notebookapp.py in external notebook library
124-
@flaky
125-
def test_open_console_when_no_kernel(notebook, qtbot, mocker):
126-
"""Test that open_console() handles the case when there is no kernel."""
127-
# Create mock IPython console plugin and QMessageBox
128-
MockMessageBox = mocker.patch(
129-
'spyder_notebook.widgets.main_widget.QMessageBox')
130-
131-
# Wait for prompt
132-
nbwidget = notebook.get_widget().tabwidget.currentWidget().notebookwidget
133-
qtbot.waitUntil(lambda: prompt_present(nbwidget, qtbot),
134-
timeout=NOTEBOOK_UP)
135-
136-
# Shut the kernel down and check that this is successful
137-
client = notebook.get_widget().tabwidget.currentWidget()
138-
kernel_id = client.get_kernel_id()
139-
sessions_url = client.get_session_url()
140-
client.shutdown_kernel()
141-
assert not is_kernel_up(kernel_id, sessions_url)
142-
143-
# Try opening a console
144-
notebook.get_widget().open_console(client)
145-
146-
# Assert that a dialog is displayed and no console was opened
147-
MockMessageBox.critical.assert_called()
148-
ipyconsole = notebook.get_plugin(Plugins.IPythonConsole)
149-
ipyconsole._create_client_for_kernel.assert_not_called()
150-
151122

152123
def test_on_mainwindow_visible_with_opened_notebooks(plugin_no_server):
153124
"""

0 commit comments

Comments
 (0)