Skip to content

Commit

Permalink
Fixes for Darwin related environment encoding problem and failure to …
Browse files Browse the repository at this point in the history
…stop web server.
  • Loading branch information
ntoll committed Jul 5, 2019
1 parent c6f0b65 commit efb7c70
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 6 deletions.
9 changes: 9 additions & 0 deletions mu/interface/panes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from PyQt5.QtGui import (QKeySequence, QTextCursor, QCursor, QPainter,
QDesktopServices, QStandardItem)
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from mu import language_code
from mu.interface.themes import Font
from mu.interface.themes import DEFAULT_FONT_SIZE

Expand Down Expand Up @@ -699,6 +700,14 @@ def start_process(self, script_name, working_directory, interactive=True,
env = QProcessEnvironment.systemEnvironment()
env.insert('PYTHONUNBUFFERED', '1')
env.insert('PYTHONIOENCODING', 'utf-8')
if sys.platform == 'darwin':
# Ensure the correct encoding is set for the environment. If the
# following two lines are not set, then Flask will complain about
# Python 3 being misconfigured to use ASCII encoding.
# See: https://click.palletsprojects.com/en/7.x/python3/
encoding = "{}.utf-8".format(language_code)
env.insert('LC_ALL', encoding)
env.insert('LANG', encoding)
if sys.platform == 'win32' and 'pythonw.exe' in sys.executable:
# On Windows, if installed via NSIS then Python is always run in
# isolated mode via pythonw.exe so none of the expected directories
Expand Down
11 changes: 9 additions & 2 deletions mu/modes/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,15 @@ def stop_server(self):
"""
logger.debug('Stopping Flask app.')
if self.runner:
pid = self.runner.process.processId()
os.kill(pid, signal.SIGINT)
try:
pid = self.runner.process.processId()
os.kill(pid, signal.SIGINT)
except Exception as ex:
# Couldn't kill child process. Perhaps it's already finished
# because it encountered an error. In any case, log this for
# debugging purposes.
logger.error("Problem stopping local web server.")
logger.error(ex)
self.runner.process.waitForFinished()
self.runner = None
self.view.remove_python_runner()
Expand Down
21 changes: 17 additions & 4 deletions tests/interface/test_panes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,10 @@ def test_PythonProcessPane_start_process_user_enviroment_variables():
"""
Ensure that if environment variables are set, they are set in the context
of the new child Python process.
If running on Darwin, ensure that the correct encoding for the Python
environment is used (Flask stop and complain about a misconfigured
Python 3 using an ASCII encoding).
"""
mock_process = mock.MagicMock()
mock_process_class = mock.MagicMock(return_value=mock_process)
Expand All @@ -1198,21 +1202,30 @@ def test_PythonProcessPane_start_process_user_enviroment_variables():
mock_environment = mock.MagicMock()
mock_environment_class = mock.MagicMock()
mock_environment_class.systemEnvironment.return_value = mock_environment
pypath = sys.path
with mock.patch('mu.interface.panes.QProcess', mock_process_class), \
mock.patch('mu.interface.panes.sys') as mock_sys, \
mock.patch('mu.interface.panes.QProcessEnvironment',
mock_environment_class):
mock_sys.platform = "darwin"
mock_sys.path = pypath
ppp = mu.interface.panes.PythonProcessPane()
envars = [['name', 'value'], ]
ppp.start_process('script.py', 'workspace', interactive=False,
envars=envars, runner='foo')
assert mock_environment.insert.call_count == 4
expected_encoding = "{}.utf-8".format(mu.language_code)
assert mock_environment.insert.call_count == 6
assert mock_environment.insert.call_args_list[0][0] == ('PYTHONUNBUFFERED',
'1')
assert mock_environment.insert.call_args_list[1][0] == ('PYTHONIOENCODING',
'utf-8')
assert mock_environment.insert.call_args_list[2][0] == ('name', 'value')
expected_path = os.pathsep.join(sys.path)
assert mock_environment.insert.call_args_list[3][0] == ('PYTHONPATH',
assert mock_environment.insert.call_args_list[2][0] == ('LC_ALL',
expected_encoding)
assert mock_environment.insert.call_args_list[3][0] == ('LANG',
expected_encoding)
assert mock_environment.insert.call_args_list[4][0] == ('name', 'value')
expected_path = os.pathsep.join(pypath)
assert mock_environment.insert.call_args_list[5][0] == ('PYTHONPATH',
expected_path)


Expand Down
21 changes: 21 additions & 0 deletions tests/modes/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,27 @@ def test_stop_server():
view.remove_python_runner.assert_called_once_with()


def test_stop_server_with_error():
"""
If killing the server's child process encounters a problem (perhaps the
process is already dead), then log this and tidy up.
"""
editor = mock.MagicMock()
view = mock.MagicMock()
wm = WebMode(editor, view)
mock_runner = mock.MagicMock()
mock_runner.process.processId.return_value = 666 # ;-)
wm.runner = mock_runner
with mock.patch("os.kill", side_effect=Exception("Bang")) as mock_kill, \
mock.patch("mu.modes.web.logger.error") as mock_log:
wm.stop_server()
mock_kill.assert_called_once_with(666, signal.SIGINT)
assert mock_log.call_count == 2
mock_runner.process.waitForFinished.assert_called_once_with()
assert wm.runner is None
view.remove_python_runner.assert_called_once_with()


def test_stop():
"""
Ensure that this method, called when Mu is quitting, stops the local
Expand Down

0 comments on commit efb7c70

Please sign in to comment.