From 8187f953c34c95ca1b16f9b68582188d2a7e246a Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 18 Jan 2018 09:38:41 -0500 Subject: [PATCH 1/9] IPython console: Make command passed to %edit work on Windows and PY2 --- spyder/plugins/ipythonconsole.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index ec90e5c242d..31ec9896678 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -52,7 +52,7 @@ from spyder.utils.ipython.style import create_qss_style from spyder.utils.qthelpers import create_action, MENU_SEPARATOR from spyder.utils import icon_manager as ima -from spyder.utils import programs, sourcecode +from spyder.utils import encoding, programs, sourcecode from spyder.utils.programs import TEMPDIR from spyder.utils.misc import get_error_match, remove_backslashes from spyder.widgets.findreplace import FindReplace @@ -1073,21 +1073,28 @@ def connect_client_to_kernel(self, client): def set_editor(self): """Set the editor used by the %edit magic""" + # Get Python executable used by Spyder python = sys.executable + if PY2: + python = encoding.to_unicode_from_fs(python) + + # Compose command for %edit if DEV: - spyder_start_directory = get_module_path('spyder') - bootstrap_script = osp.join(osp.dirname(spyder_start_directory), - 'bootstrap.py') - editor = u'{0} {1} --'.format(python, bootstrap_script) + spy_dir = get_module_path('spyder') + bootstrap = osp.join(osp.dirname(spy_dir), 'bootstrap.py') + if PY2: + bootstrap = encoding.to_unicode_from_fs(bootstrap) + editor = u'"{0}" "{1}" --'.format(python, bootstrap) else: import1 = "import sys" import2 = "from spyder.app.start import send_args_to_spyder" code = "send_args_to_spyder([sys.argv[-1]])" - editor = u"{0} -c '{1}; {2}; {3}'".format(python, - import1, - import2, - code) - return to_text_string(editor) + editor = u"\"{0}\" -c '{1}; {2}; {3}'".format(python, + import1, + import2, + code) + + return editor def config_options(self): """ From 840f0650c0f37cbf187a61a360d4c2bf1290508c Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 18 Jan 2018 09:43:43 -0500 Subject: [PATCH 2/9] Lockfile: Make latest fixes to work on Windows too --- spyder/utils/external/lockfile.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/spyder/utils/external/lockfile.py b/spyder/utils/external/lockfile.py index c43ff19db5f..4fc88abc503 100644 --- a/spyder/utils/external/lockfile.py +++ b/spyder/utils/external/lockfile.py @@ -178,16 +178,14 @@ def lock(self): # Verify that the running process corresponds to # a Spyder one p = psutil.Process(int(pid)) - if os.name == 'nt': - conditions = ['spyder' in c.lower() - for c in p.cmdline()] - else: - # Valid names for main script - names = set(['spyder', 'spyder3', 'bootstrap.py']) - # Check the first three command line arguments - arguments = set(os.path.basename(arg) - for arg in p.cmdline()[:3]) - conditions = [names & arguments] + + # Valid names for main script + names = set(['spyder', 'spyder3', 'bootstrap.py']) + + # Check the first three command line arguments + arguments = set(os.path.basename(arg) + for arg in p.cmdline()[:3]) + conditions = [names & arguments] if not any(conditions): raise(OSError(errno.ESRCH, 'No such process')) except OSError as e: From 55844455b3ef848b37d638569fb3c7b547124bc9 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 19 Jan 2018 10:07:42 -0500 Subject: [PATCH 3/9] Testing: Use app/start to start the main window --- spyder/app/mainwindow.py | 5 +++++ spyder/app/start.py | 19 ++++++++++++++----- spyder/app/tests/test_mainwindow.py | 8 +++----- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/spyder/app/mainwindow.py b/spyder/app/mainwindow.py index d00ab8fd474..ade0ce57a50 100644 --- a/spyder/app/mainwindow.py +++ b/spyder/app/mainwindow.py @@ -3084,6 +3084,11 @@ def run_spyder(app, options, args): #============================================================================== def main(): """Main function""" + if PYTEST: + options, args = get_options() + app = initialize() + window = run_spyder(app, options, args) + return window # **** Collect command line options **** # Note regarding Options: diff --git a/spyder/app/start.py b/spyder/app/start.py index 542afde3420..10719b1c6bd 100644 --- a/spyder/app/start.py +++ b/spyder/app/start.py @@ -21,7 +21,7 @@ # Local imports from spyder.app.cli_options import get_options -from spyder.config.base import get_conf_path, running_in_mac_app +from spyder.config.base import PYTEST, get_conf_path, running_in_mac_app from spyder.config.main import CONF from spyder.utils.external import lockfile from spyder.py3compat import is_unicode @@ -135,13 +135,19 @@ def main(): # executing this script because it doesn't make # sense from spyder.app import mainwindow - mainwindow.main() - return + if PYTEST: + return mainwindow.main() + else: + mainwindow.main() + return if lock_created: # Start a new instance from spyder.app import mainwindow - mainwindow.main() + if PYTEST: + return mainwindow.main() + else: + mainwindow.main() else: # Pass args to Spyder or print an informative # message @@ -152,7 +158,10 @@ def main(): "instance, please pass to it the --new-instance option") else: from spyder.app import mainwindow - mainwindow.main() + if PYTEST: + mainwindow.main() + else: + return mainwindow.main() if __name__ == "__main__": diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 69f8c70a39b..12b082323bd 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -24,8 +24,8 @@ from qtpy.QtTest import QTest from qtpy.QtWidgets import QApplication, QFileDialog, QLineEdit -from spyder.app.cli_options import get_options -from spyder.app.mainwindow import initialize, run_spyder +from spyder.app.mainwindow import MainWindow # Tests fail without this import +from spyder.app import start from spyder.config.base import get_home_dir from spyder.config.main import CONF from spyder.plugins.runconfig import RunConfiguration @@ -126,9 +126,7 @@ def main_window(request): pass # Start the window - app = initialize() - options, args = get_options() - window = run_spyder(app, options, args) + window = start.main() def close_window(): window.close() request.addfinalizer(close_window) From 77eb24c2b5764acef83b3b053b7fe840b13fa870 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 20 Jan 2018 00:32:20 -0500 Subject: [PATCH 4/9] Testing: Add runtests.py script to list of names detected by lockfile --- spyder/utils/external/lockfile.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spyder/utils/external/lockfile.py b/spyder/utils/external/lockfile.py index 4fc88abc503..1af65235df6 100644 --- a/spyder/utils/external/lockfile.py +++ b/spyder/utils/external/lockfile.py @@ -20,6 +20,7 @@ from time import time as _uniquefloat import psutil +from spyder.config.base import PYTEST from spyder.py3compat import PY2, to_binary_string def unique(): @@ -181,6 +182,8 @@ def lock(self): # Valid names for main script names = set(['spyder', 'spyder3', 'bootstrap.py']) + if PYTEST: + names.add('runtests.py') # Check the first three command line arguments arguments = set(os.path.basename(arg) From 1339555a22843b384885c14c2f5b9d9c1cab1dac Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 20 Jan 2018 00:33:00 -0500 Subject: [PATCH 5/9] Testing: Fix error in start.py --- spyder/app/start.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spyder/app/start.py b/spyder/app/start.py index 10719b1c6bd..bf6f7b50362 100644 --- a/spyder/app/start.py +++ b/spyder/app/start.py @@ -159,9 +159,9 @@ def main(): else: from spyder.app import mainwindow if PYTEST: - mainwindow.main() - else: return mainwindow.main() + else: + mainwindow.main() if __name__ == "__main__": From 26bf85a8ac2a656d4f733cf2d2648dd913185196 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 20 Jan 2018 00:36:05 -0500 Subject: [PATCH 6/9] Testing: Add test for single instance mode and for %edit magic --- spyder/app/tests/test_mainwindow.py | 40 +++++++++++++++++++++++++++++ spyder/plugins/ipythonconsole.py | 11 +++++--- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index c0f75f5e416..9335da4ce4b 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -138,6 +138,13 @@ def main_window(request): except KeyError: pass + # Only use single_instance mode for tests that require it + single_instance = request.node.get_marker('single_instance') + if single_instance: + CONF.set('main', 'single_instance', True) + else: + CONF.set('main', 'single_instance', False) + # Start the window window = start.main() def close_window(): @@ -183,6 +190,39 @@ def test_calltip(main_window, qtbot): main_window.editor.close_file() +@pytest.mark.slow +@flaky(max_runs=3) +@pytest.mark.single_instance +def test_single_instance_and_edit_magic(main_window, qtbot, tmpdir): + """Test single instance mode and for %edit magic.""" + editorstack = main_window.editor.get_current_editorstack() + shell = main_window.ipyconsole.get_current_shellwidget() + qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT) + + lock_code = ("from spyder.config.base import get_conf_path\n" + "from spyder.utils.external import lockfile\n" + "lock_file = get_conf_path('spyder.lock')\n" + "lock = lockfile.FilesystemLock(lock_file)\n" + "lock_created = lock.lock()") + + # Test single instance + with qtbot.waitSignal(shell.executed): + shell.execute(lock_code) + assert not shell.get_value('lock_created') + + # Test %edit magic + n_editors = editorstack.get_stack_count() + p = tmpdir.mkdir("foo").join("bar.py") + p.write(lock_code) + + with qtbot.waitSignal(shell.executed): + shell.execute('%edit {}'.format(to_text_string(p))) + + qtbot.wait(3000) + assert editorstack.get_stack_count() == n_editors + 1 + assert editorstack.get_current_editor().toPlainText() == lock_code + + @pytest.mark.slow @flaky(max_runs=3) @pytest.mark.skipif(os.name == 'nt' or PY2 or PYQT4, diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index 31ec9896678..22300b0a367 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -43,7 +43,7 @@ # Local imports from spyder import dependencies from spyder.config.base import (_, DEV, get_conf_path, get_home_dir, - get_module_path) + get_module_path, PYTEST) from spyder.config.main import CONF from spyder.plugins import SpyderPluginWidget from spyder.plugins.configdialog import PluginConfigPage @@ -1079,14 +1079,17 @@ def set_editor(self): python = encoding.to_unicode_from_fs(python) # Compose command for %edit + spy_dir = osp.dirname(get_module_path('spyder')) if DEV: - spy_dir = get_module_path('spyder') - bootstrap = osp.join(osp.dirname(spy_dir), 'bootstrap.py') + bootstrap = osp.join(spy_dir, 'bootstrap.py') if PY2: bootstrap = encoding.to_unicode_from_fs(bootstrap) editor = u'"{0}" "{1}" --'.format(python, bootstrap) else: - import1 = "import sys" + if PYTEST: + import1 = 'import sys; sys.path.append("{}")'.format(spy_dir) + else: + import1 = "import sys" import2 = "from spyder.app.start import send_args_to_spyder" code = "send_args_to_spyder([sys.argv[-1]])" editor = u"\"{0}\" -c '{1}; {2}; {3}'".format(python, From ea0ff73fe9b3cf043996119c5a5d80f2854a3bba Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 20 Jan 2018 00:40:56 -0500 Subject: [PATCH 7/9] Testing: Remove unneeded constant --- spyder/app/tests/test_mainwindow.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 9335da4ce4b..1f5fb987775 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -64,9 +64,6 @@ # miliseconds) EVAL_TIMEOUT = 3000 -# Test for PyQt 5 wheels -PYQT_WHEEL = PYQT_VERSION > '5.6' - # Temporary directory TEMP_DIRECTORY = tempfile.gettempdir() From db97a06ff0878f53010573e224e3144e08c10b18 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 20 Jan 2018 01:57:18 -0500 Subject: [PATCH 8/9] Testing: Try to fix tests --- spyder/app/tests/test_mainwindow.py | 3 ++- spyder/plugins/ipythonconsole.py | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 1f5fb987775..57ea29591ae 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -188,7 +188,6 @@ def test_calltip(main_window, qtbot): @pytest.mark.slow -@flaky(max_runs=3) @pytest.mark.single_instance def test_single_instance_and_edit_magic(main_window, qtbot, tmpdir): """Test single instance mode and for %edit magic.""" @@ -219,6 +218,8 @@ def test_single_instance_and_edit_magic(main_window, qtbot, tmpdir): assert editorstack.get_stack_count() == n_editors + 1 assert editorstack.get_current_editor().toPlainText() == lock_code + main_window.editor.close_file() + @pytest.mark.slow @flaky(max_runs=3) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index 22300b0a367..0dfaaeb81b3 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -1087,15 +1087,15 @@ def set_editor(self): editor = u'"{0}" "{1}" --'.format(python, bootstrap) else: if PYTEST: - import1 = 'import sys; sys.path.append("{}")'.format(spy_dir) + import1 = 'import sys; sys.path.append(""{}"")'.format(spy_dir) else: import1 = "import sys" import2 = "from spyder.app.start import send_args_to_spyder" code = "send_args_to_spyder([sys.argv[-1]])" - editor = u"\"{0}\" -c '{1}; {2}; {3}'".format(python, - import1, - import2, - code) + editor = u"\"{0}\" -c \"{1}; {2}; {3}\"".format(python, + import1, + import2, + code) return editor From cc436b33575f6b4bb03ea3653aa9ea8391e11c53 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 21 Jan 2018 14:33:09 -0500 Subject: [PATCH 9/9] Testing: Fix a test on Linux --- spyder/plugins/ipythonconsole.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index 0dfaaeb81b3..e6c844977c3 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -1086,10 +1086,16 @@ def set_editor(self): bootstrap = encoding.to_unicode_from_fs(bootstrap) editor = u'"{0}" "{1}" --'.format(python, bootstrap) else: + import1 = "import sys" + # We need to add spy_dir to sys.path so this test can be + # run in our CIs if PYTEST: - import1 = 'import sys; sys.path.append(""{}"")'.format(spy_dir) - else: - import1 = "import sys" + if os.name == 'nt': + import1 = (import1 + + '; sys.path.append(""{}"")'.format(spy_dir)) + else: + import1 = (import1 + + "; sys.path.append('{}')".format(spy_dir)) import2 = "from spyder.app.start import send_args_to_spyder" code = "send_args_to_spyder([sys.argv[-1]])" editor = u"\"{0}\" -c \"{1}; {2}; {3}\"".format(python,