diff --git a/requirements/requirements.txt b/requirements/requirements.txt index a94aacd1b16..0ed40dff9ab 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -9,7 +9,7 @@ pycodestyle pylint psutil qtawesome>=0.4.1 -qtpy>=1.1.0 +qtpy>=1.2.0 pickleshare pyzmq chardet>=2.0.0 diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py index c4ee674842f..378f57dbd0c 100644 --- a/spyder/plugins/ipythonconsole.py +++ b/spyder/plugins/ipythonconsole.py @@ -602,7 +602,8 @@ class IPythonConsole(SpyderPluginWidget): "required to create IPython consoles. Please " "make it writable.") - def __init__(self, parent, testing=False, test_dir=TEMPDIR): + def __init__(self, parent, testing=False, test_dir=TEMPDIR, + test_no_stderr=False): """Ipython Console constructor.""" if PYQT5: SpyderPluginWidget.__init__(self, parent, main = parent) @@ -623,8 +624,11 @@ def __init__(self, parent, testing=False, test_dir=TEMPDIR): self.filenames = [] self.mainwindow_close = False self.create_new_client_if_empty = True + + # Attrs for testing self.testing = testing self.test_dir = test_dir + self.test_no_stderr = test_no_stderr # Initialize plugin if not self.testing: @@ -1021,12 +1025,11 @@ def create_client_for_kernel(self): def connect_client_to_kernel(self, client): """Connect a client to its kernel""" connection_file = client.connection_file - try: + + if self.test_no_stderr: + stderr_file = None + else: stderr_file = client.stderr_file - except PermissionError: - error_msg = self.permission_error_msg.format(TEMPDIR) - client.show_kernel_error(error_msg) - return km, kc = self.create_kernel_manager_and_kernel_client(connection_file, stderr_file) @@ -1430,7 +1433,10 @@ def create_kernel_manager_and_kernel_client(self, connection_file, kernel_manager._kernel_spec = kernel_spec # Save stderr in a file to read it later in case of errors - stderr = codecs.open(stderr_file, 'w', encoding='utf-8') + if stderr_file is not None: + stderr = codecs.open(stderr_file, 'w', encoding='utf-8') + else: + stderr = None kernel_manager.start_kernel(stderr=stderr) # Kernel client @@ -1559,7 +1565,7 @@ def _new_connection_file(self): if not osp.isdir(jupyter_runtime_dir()): try: os.makedirs(jupyter_runtime_dir()) - except PermissionError: + except (IOError, OSError): return None cf = '' while not cf: diff --git a/spyder/plugins/tests/test_ipythonconsole.py b/spyder/plugins/tests/test_ipythonconsole.py index 4560703c4bf..3c2aaf45ca5 100644 --- a/spyder/plugins/tests/test_ipythonconsole.py +++ b/spyder/plugins/tests/test_ipythonconsole.py @@ -26,6 +26,7 @@ KernelConnectionDialog) from spyder.utils.environ import listdict2envdict from spyder.utils.ipython.style import create_style_class +from spyder.utils.programs import TEMPDIR from spyder.utils.test import close_message_box from spyder.widgets.variableexplorer.collectionseditor import CollectionsEditor @@ -36,6 +37,7 @@ SHELL_TIMEOUT = 20000 PYQT_WHEEL = PYQT_VERSION > '5.6' TEMP_DIRECTORY = tempfile.gettempdir() +NON_ASCII_DIR = osp.join(TEMP_DIRECTORY, u'測試', u'اختبار') #============================================================================== @@ -64,41 +66,74 @@ def get_console_background_color(style_sheet): #============================================================================== @pytest.fixture def ipyconsole(request): - try: - console = IPythonConsole(None, testing=True, test_dir=request.param) - except AttributeError: - console = IPythonConsole(None, testing=True) + """IPython console fixture.""" + + # Test the console with a non-ascii temp dir + non_ascii_dir = request.node.get_marker('non_ascii_dir') + if non_ascii_dir: + test_dir = NON_ASCII_DIR + else: + test_dir = TEMPDIR + + # Instruct the console to not use a stderr file + no_stderr_file = request.node.get_marker('no_stderr_file') + if no_stderr_file: + test_no_stderr = True + else: + test_no_stderr = False + + # Create the console and a new client + console = IPythonConsole(parent=None, + testing=True, + test_dir=test_dir, + test_no_stderr=test_no_stderr) console.create_new_client() + def close_console(): console.closing_plugin() console.close() request.addfinalizer(close_console) console.show() + return console #============================================================================== # Tests #============================================================================== -@pytest.mark.parametrize('ipyconsole', [osp.join(TEMP_DIRECTORY, u'測試', - u'اختبار')], indirect=True) +@flaky(max_runs=3) @pytest.mark.skipif(os.name == 'nt', reason="It times out sometimes on Windows") -def test_console_stderr_file(ipyconsole, qtbot): - """Test a the creation of a console with a stderr file in ascii dir.""" +@pytest.mark.no_stderr_file +def test_no_stderr_file(ipyconsole, qtbot): + """Test that consoles can run without an stderr.""" # Wait until the window is fully up shell = ipyconsole.get_current_shellwidget() qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT) - # Create a new client with s stderr file in a non-ascii dir - ipyconsole.create_new_client() + # Execute a simple assignment + with qtbot.waitSignal(shell.executed): + shell.execute('a = 1') + + # Assert we get the assigned value correctly + assert shell.get_value('a') == 1 + + +@flaky(max_runs=3) +@pytest.mark.skipif(os.name == 'nt', reason="It times out sometimes on Windows") +@pytest.mark.non_ascii_dir +def test_non_ascii_stderr_file(ipyconsole, qtbot): + """Test the creation of a console with a stderr file in a non-ascii dir.""" + # Wait until the window is fully up shell = ipyconsole.get_current_shellwidget() qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT) + + # Execute a simple assignment with qtbot.waitSignal(shell.executed): shell.execute('a = 1') - # Assert we get the a value correctly + # Assert we get the assigned value assert shell.get_value('a') == 1 @@ -531,7 +566,7 @@ def test_restart_kernel(ipyconsole, qtbot): client = ipyconsole.get_current_client() qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT) - # Do an assigment to verify that it's not there after restarting + # Do an assignment to verify that it's not there after restarting with qtbot.waitSignal(shell.executed): shell.execute('a = 10') diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py index ffa9d632946..39b838206f7 100644 --- a/spyder/widgets/ipythonconsole/client.py +++ b/spyder/widgets/ipythonconsole/client.py @@ -156,15 +156,19 @@ def kernel_id(self): @property def stderr_file(self): """Filename to save kernel stderr output.""" + stderr_file = None if self.connection_file is not None: stderr_file = self.kernel_id + '.stderr' if self.stderr_dir is not None: stderr_file = osp.join(self.stderr_dir, stderr_file) else: - if not osp.isdir(TEMPDIR): - os.makedirs(TEMPDIR) - stderr_file = osp.join(TEMPDIR, stderr_file) - return stderr_file + try: + if not osp.isdir(TEMPDIR): + os.makedirs(TEMPDIR) + stderr_file = osp.join(TEMPDIR, stderr_file) + except (IOError, OSError): + stderr_file = None + return stderr_file def configure_shellwidget(self, give_focus=True): """Configure shellwidget after kernel is started"""