-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes #2306: Add a restart method #2312
Changes from all commits
4ded801
e580c08
2ecefcc
03603af
aa66feb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright © 2015 The Spyder Development Team | ||
# Licensed under the terms of the MIT License | ||
# (see spyderlib/__init__.py for details) | ||
|
||
""" | ||
Restart Spyder | ||
|
||
A helper script that allows Spyder to restart from within the application. | ||
""" | ||
|
||
import ast | ||
import os | ||
import os.path as osp | ||
import subprocess | ||
import sys | ||
import time | ||
|
||
|
||
PY2 = sys.version[0] == '2' | ||
|
||
|
||
def _is_pid_running_on_windows(pid): | ||
"""Check if a process is running on windows systems based on the pid.""" | ||
pid = str(pid) | ||
|
||
# Hide flashing command prompt | ||
startupinfo = subprocess.STARTUPINFO() | ||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW | ||
|
||
process = subprocess.Popen(r'tasklist /fi "PID eq {0}"'.format(pid), | ||
stdout=subprocess.PIPE, | ||
stderr=subprocess.STDOUT, | ||
startupinfo=startupinfo) | ||
stdoutdata, stderrdata = process.communicate() | ||
stdoutdata = to_text_string(stdoutdata) | ||
process.kill() | ||
check = pid in stdoutdata | ||
|
||
return check | ||
|
||
|
||
def _is_pid_running_on_unix(pid): | ||
"""Check if a process is running on unix systems based on the pid.""" | ||
try: | ||
# On unix systems os.kill with a 0 as second argument only pokes the | ||
# process (if it exists) and does not kill it | ||
os.kill(pid, 0) | ||
except OSError: | ||
return False | ||
else: | ||
return True | ||
|
||
|
||
def is_pid_running(pid): | ||
"""Check if a process is running based on the pid.""" | ||
# Select the correct function depending on the OS | ||
if os.name == 'nt': | ||
return _is_pid_running_on_windows(pid) | ||
else: | ||
return _is_pid_running_on_unix(pid) | ||
|
||
|
||
def to_text_string(obj, encoding=None): | ||
"""Convert `obj` to (unicode) text string""" | ||
if PY2: | ||
# Python 2 | ||
if encoding is None: | ||
return unicode(obj) | ||
else: | ||
return unicode(obj, encoding) | ||
else: | ||
# Python 3 | ||
if encoding is None: | ||
return str(obj) | ||
elif isinstance(obj, str): | ||
# In case this function is not used properly, this could happen | ||
return obj | ||
else: | ||
return str(obj, encoding) | ||
|
||
|
||
def main(): | ||
# Note: Variables defined in spyderlib\spyder.py 'restart()' method | ||
spyder_args = os.environ.pop('SPYDER_ARGS', None) | ||
pid = os.environ.pop('SPYDER_PID', None) | ||
is_bootstrap = os.environ.pop('SPYDER_IS_BOOTSTRAP', None) | ||
|
||
# Get the spyder base folder based on this file | ||
spyder_folder = osp.split(osp.dirname(osp.abspath(__file__)))[0] | ||
|
||
if any([not spyder_args, not pid, not is_bootstrap]): | ||
error = "This script can only be called from within a Spyder instance" | ||
raise RuntimeError(error) | ||
|
||
# Variables were stored as string literals in the environment, so to use | ||
# them we need to parse them in a safe manner. | ||
is_bootstrap = ast.literal_eval(is_bootstrap) | ||
pid = int(pid) | ||
args = ast.literal_eval(spyder_args) | ||
|
||
# Enforce the --new-instance flag when running spyder | ||
if '--new-instance' not in args: | ||
if is_bootstrap and not '--' in args: | ||
args = args + ['--', '--new-instance'] | ||
else: | ||
args.append('--new-instance') | ||
|
||
# Arrange arguments to be passed to the restarter subprocess | ||
args = ' '.join(args) | ||
|
||
# Get python excutable running this script | ||
python = sys.executable | ||
|
||
# Build the command | ||
if is_bootstrap: | ||
spyder = osp.join(spyder_folder, 'bootstrap.py') | ||
else: | ||
spyderlib = osp.join(spyder_folder, 'spyderlib') | ||
spyder = osp.join(spyderlib, 'start_app.py') | ||
|
||
command = '"{0}" "{1}" {2}'.format(python, spyder, args) | ||
|
||
# Adjust the command and/or arguments to subprocess depending on the OS | ||
shell = os.name != 'nt' | ||
|
||
# Wait for original process to end before launching the new instance | ||
while True: | ||
if not is_pid_running(pid): | ||
break | ||
time.sleep(0.2) # Throttling control | ||
|
||
env = os.environ.copy() | ||
try: | ||
subprocess.Popen(command, shell=shell, env=env) | ||
except Exception as error: | ||
print(command) | ||
print(error) | ||
time.sleep(15) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright © 2009-2013 Pierre Raybaut | ||
# Copyright © 2013-2015 The Spyder Development Team | ||
# Licensed under the terms of the MIT License | ||
# (see spyderlib/__init__.py for details) | ||
|
||
|
@@ -30,6 +31,7 @@ | |
import re | ||
import socket | ||
import shutil | ||
import subprocess | ||
import sys | ||
import threading | ||
|
||
|
@@ -118,7 +120,7 @@ | |
from spyderlib.baseconfig import (get_conf_path, get_module_data_path, | ||
get_module_source_path, STDERR, DEBUG, DEV, | ||
debug_print, TEST, SUBFOLDER, MAC_APP_NAME, | ||
running_in_mac_app) | ||
running_in_mac_app, get_module_path) | ||
from spyderlib.config import CONF, EDIT_EXT, IMPORT_EXT, OPEN_FILES_PORT | ||
from spyderlib.cli_options import get_options | ||
from spyderlib import dependencies | ||
|
@@ -853,10 +855,16 @@ def create_edit_action(text, tr_text, icon_name): | |
icon='exit.png', tip=_("Quit"), | ||
triggered=self.console.quit) | ||
self.register_shortcut(quit_action, "_", "Quit") | ||
restart_action = create_action(self, _("&Restart"), | ||
icon='restart.png', | ||
tip=_("Restart"), | ||
triggered=self.restart) | ||
self.register_shortcut(restart_action, "_", "Restart") | ||
|
||
self.file_menu_actions += [self.load_temp_session_action, | ||
self.load_session_action, | ||
self.save_session_action, | ||
None, quit_action] | ||
None, restart_action, quit_action] | ||
self.set_splash("") | ||
|
||
self.debug_print(" ..widgets") | ||
|
@@ -2653,6 +2661,59 @@ def start_open_files_server(self): | |
self.sig_open_external_file.emit(fname) | ||
req.sendall(b' ') | ||
|
||
# ---- Quit and restart | ||
def restart(self): | ||
"""Quit and Restart Spyder application""" | ||
# Get start path to use in restart script | ||
spyder_start_directory = get_module_path('spyderlib') | ||
restart_script = osp.join(spyder_start_directory, 'restart_app.py') | ||
|
||
# Get any initial argument passed when spyder was started | ||
# Note: Variables defined in bootstrap.py and spyderlib\start_app.py | ||
env = os.environ.copy() | ||
bootstrap_args = env.pop('SPYDER_BOOTSTRAP_ARGS', None) | ||
spyder_args = env.pop('SPYDER_ARGS') | ||
|
||
# Get current process and python running spyder | ||
pid = os.getpid() | ||
python = sys.executable | ||
|
||
# Check if started with bootstrap.py | ||
if bootstrap_args is not None: | ||
spyder_args = bootstrap_args | ||
is_bootstrap = True | ||
else: | ||
is_bootstrap = False | ||
|
||
# Pass variables as environment variables (str) to restarter subprocess | ||
env['SPYDER_ARGS'] = spyder_args | ||
env['SPYDER_PID'] = str(pid) | ||
env['SPYDER_IS_BOOTSTRAP'] = str(is_bootstrap) | ||
|
||
# Build the command and popen arguments depending on the OS | ||
if os.name == 'nt': | ||
# Hide flashing command prompt | ||
startupinfo = subprocess.STARTUPINFO() | ||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this line do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In windows it should hide the cmd prompt from flashing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will add a comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a comment about this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do |
||
shell = False | ||
else: | ||
startupinfo = None | ||
shell = True | ||
|
||
command = '"{0}" "{1}"' | ||
command = command.format(python, restart_script) | ||
|
||
try: | ||
if self.closing(True): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought that |
||
subprocess.Popen(command, shell=shell, env=env, | ||
startupinfo=startupinfo) | ||
self.console.quit() | ||
except Exception as error: | ||
# If there is an error with subprocess, Spyder should not quit and | ||
# the error can be inspected in the internal console | ||
print(error) | ||
print(command) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm good point :p, do we have an error logger for Spyder? where these things ca be written in case it shuts down? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm if there is an error, then I guess it would be in the subprocess... in which case it would not restart nor quit... so internal console is my bet? |
||
|
||
# ---- Interactive Tours | ||
def show_tour(self, index): | ||
""" """ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why using
pop
here instead ofget
with a default value?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clean the environment ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks mind reader++,
@ccordoba12 since I am passing the env to the new instance to be open, I want to clean it ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok