def get_git_revision(repopath): """ Return Git revision for the repository located at repopath Result is a tuple (latest commit hash, branch), with None values on error """ try: git = programs.find_program('git') assert git is not None and osp.isdir(osp.join(repopath, '.git')) commit = programs.run_program(git, ['rev-parse', '--short', 'HEAD'], cwd=repopath).communicate() commit = commit[0].strip() if PY3: commit = commit.decode(sys.getdefaultencoding()) # Branch branches = programs.run_program(git, ['branch'], cwd=repopath).communicate() branches = branches[0] if PY3: branches = branches.decode(sys.getdefaultencoding()) branches = branches.split('\n') active_branch = [b for b in branches if b.startswith('*')] if len(active_branch) != 1: branch = None else: branch = active_branch[0].split(None, 1)[1] return commit, branch except (subprocess.CalledProcessError, AssertionError, AttributeError): return None, None
def get_git_refs(repopath): """ Return Git active branch, state, branches (plus tags). """ tags = [] branches = [] branch = '' files_modifed = [] if os.path.isfile(repopath): repopath = os.path.dirname(repopath) try: git = programs.find_program('git') # Files modified out, err = programs.run_program( git, ['status', '-s'], cwd=repopath, ).communicate() if PY3: out = out.decode(sys.getdefaultencoding()) files_modifed = [line.strip() for line in out.split('\n') if line] # Tags out, err = programs.run_program( git, ['tag'], cwd=repopath, ).communicate() if PY3: out = out.decode(sys.getdefaultencoding()) tags = [line.strip() for line in out.split('\n') if line] # Branches out, err = programs.run_program( git, ['branch', '-a'], cwd=repopath, ).communicate() if PY3: out = out.decode(sys.getdefaultencoding()) lines = [line.strip() for line in out.split('\n') if line] for line in lines: if line.startswith('*'): line = line.replace('*', '').strip() branch = line branches.append(line) except (subprocess.CalledProcessError, AttributeError, OSError): pass return branches + tags, branch, files_modifed
def create_program_action(parent, text, name, icon=None, nt_name=None): """Create action to run a program""" if is_text_string(icon): icon = get_icon(icon) if os.name == 'nt' and nt_name is not None: name = nt_name path = programs.find_program(name) if path is not None: return create_action(parent, text, icon=icon, triggered=lambda: programs.run_program(name))
def create_program_action(parent, text, name, icon=None, nt_name=None): """Create action to run a program""" if is_text_string(icon): icon = ima.get_icon(icon) if os.name == 'nt' and nt_name is not None: name = nt_name path = programs.find_program(name) if path is not None: return create_action(parent, text, icon=icon, triggered=lambda: programs.run_program(name))
def validate(self): host_text = self.host_input.text() cmd_text = self.cmd_input.text() if not self.HOST_REGEX.match(host_text): self.button_ok.setEnabled(False) self.host_input.setStyleSheet(self.INVALID_CSS) if bool(host_text): self.host_input.setToolTip(_('Hostname must be valid')) else: self.host_input.setToolTip( _('Hostname or IP address of the host on which the server ' 'is running. Must be non empty.')) else: self.host_input.setStyleSheet(self.VALID_CSS) self.host_input.setToolTip(_('Hostname is valid')) self.button_ok.setEnabled(True) if not self.external: if not self.NON_EMPTY_REGEX.match(cmd_text): self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet(self.INVALID_CSS) self.cmd_input.setToolTip( _('Command used to start the LSP server locally. Must be ' 'non empty')) return if find_program(cmd_text) is None: self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet(self.INVALID_CSS) self.cmd_input.setToolTip( _('Program was not found ' 'on your system')) else: self.cmd_input.setStyleSheet(self.VALID_CSS) self.cmd_input.setToolTip( _('Program was found on your ' 'system')) self.button_ok.setEnabled(True) try: json.loads(self.conf_input.toPlainText()) try: self.json_label.setText(self.JSON_VALID) except Exception: pass except ValueError: try: self.json_label.setText(self.JSON_INVALID) self.button_ok.setEnabled(False) except Exception: pass
def run_vcs_tool(path, action): """If path is a valid VCS repository, run the corresponding VCS tool Supported VCS actions: 'commit', 'browse' Return False if the VCS tool is not installed""" info = get_vcs_info(get_vcs_root(path)) tools = info['actions'][action] for tool, args in tools: if programs.find_program(tool): programs.run_program(tool, args, cwd=path) return else: cmdnames = [name for name, args in tools] raise ActionToolNotFound(info['name'], action, cmdnames)
def validate(self): host_text = self.host_input.text() cmd_text = self.cmd_input.text() if not self.HOST_REGEX.match(host_text): self.button_ok.setEnabled(False) self.host_input.setStyleSheet("QLineEdit{border: 1px solid red;}") self.host_input.setToolTip('Hostname must be valid') return else: self.host_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.host_input.setToolTip('Hostname is valid') self.button_ok.setEnabled(True) if not self.external: if not self.NON_EMPTY_REGEX.match(cmd_text): self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Command must be non empty') return if find_program(cmd_text) is None: self.button_ok.setEnabled(False) self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid red;}") self.cmd_input.setToolTip('Program was not found ' 'on your system') return else: self.cmd_input.setStyleSheet( "QLineEdit{border: 1px solid green;}") self.cmd_input.setToolTip('Program was found on your system') self.button_ok.setEnabled(True) try: json.loads(self.conf_input.toPlainText()) try: self.json_label.setText(self.JSON_VALID) except: pass except (ValueError, json.decoder.JSONDecodeError): try: self.json_label.setText(self.JSON_INVALID) self.button_ok.setEnabled(False) except: pass
docs_dest = osp.join(app_python_lib, 'spyder', 'doc') shutil.copytree(docs_orig, docs_dest) # Create a minimal library inside Resources to add it to PYTHONPATH instead of # app_python_lib. This must be done when the user changes to an interpreter # that's not the one that comes with the app, to forbid importing modules # inside the app. minimal_lib = osp.join(app_python_lib, 'minimal-lib') os.mkdir(minimal_lib) minlib_pkgs = ['spyder', 'spyderplugins'] for p in minlib_pkgs: shutil.copytree(osp.join(app_python_lib, p), osp.join(minimal_lib, p)) # Add necessary Python programs to the app PROGRAMS = ['pylint', 'pep8'] system_progs = [find_program(p) for p in PROGRAMS] progs_dest = [resources + osp.sep + p for p in PROGRAMS] for i in range(len(PROGRAMS)): shutil.copy2(system_progs[i], progs_dest[i]) # Add deps needed for PROGRAMS to the app deps = [] for package in os.listdir(system_python_lib): for d in DEPS: if package.startswith(d): deps.append(package) for i in deps: if osp.isdir(osp.join(system_python_lib, i)): shutil.copytree(osp.join(system_python_lib, i), osp.join(app_python_lib, i))
def setup_page(self): """Create configuration page.""" options_layout = QGridLayout() # Custom shell options shell_group = QGroupBox(_("Terminal shell")) shell_layout = QVBoxLayout() if WINDOWS: self.shells = WINDOWS_SHELLS else: self.shells = UNIX_SHELLS valid_shells = [] for shell in self.shells: if find_program(shell) is not None: valid_shells.append(shell) valid_shells = zip(valid_shells, valid_shells) if WINDOWS: default_option = 'cmd' elif sys.platform.startswith('linux'): default_option = 'bash' else: mac_ver = LooseVersion(platform.mac_ver()[0]) if mac_ver >= LooseVersion('10.15.0'): # Catalina changed the default shell to zsh default_option = 'zsh' else: default_option = 'bash' shell_combo = self.create_combobox(_("Select the shell interpreter:"), valid_shells, 'shell', restart=True, default=default_option) shell_combo.combobox.setMinimumContentsLength(15) shell_layout.addWidget(shell_combo) shell_group.setLayout(shell_layout) shell_layout.addStretch(1) options_layout.addWidget(shell_group) # Display preferences display_group = QGroupBox(_("Terminal display preferences")) display_layout = QVBoxLayout() # Custom buffer limit self.buffer_sb = self.create_spinbox(_("Buffer limit: "), "", 'buffer_limit', min_=100, default=1000, max_=1000000, step=1) display_layout.addWidget(self.buffer_sb) display_group.setLayout(display_layout) display_layout.addStretch(1) options_layout.addWidget(display_group) # Style preferences terminal_group = QGroupBox(_("Terminal style preferences")) # Custom bar option cursor_choices = [(_("Block"), 0), (_("Underline"), 1), (_("Bar"), 2)] self.cursor_combo = self.create_combobox(_("Type of cursor:"), cursor_choices, 'cursor_type') self.cursor_combo.combobox.setMinimumContentsLength(15) options_layout.addWidget(self.cursor_combo) # Custom sound option self.sound_cb = self.create_checkbox( _("Enable bell sound"), 'sound', tip=_("Enable bell sound on terminal")) options_layout.addWidget(self.sound_cb) # Visual bell option self.cursor_blink_cb = self.create_checkbox( _("Enable cursor blink"), 'cursor_blink', tip=_("_Enable cursor blink on terminal")) options_layout.addWidget(self.cursor_blink_cb) terminal_group.setLayout(options_layout) layout = QVBoxLayout() layout.addWidget(shell_group) layout.addWidget(display_group) layout.addWidget(terminal_group) layout.addStretch(1) self.setLayout(layout)
from spyder.utils.qthelpers import create_toolbutton from spyder.widgets.comboboxes import (is_module_or_package, PythonModulesComboBox) from spyder.widgets.onecolumntree import OneColumnTree from spyder.widgets.variableexplorer.texteditor import TextEditor # This is needed for testing this module as a stand alone script try: _ = get_translation("pylint", "spyder_pylint") except KeyError as error: import gettext _ = gettext.gettext PYLINT = 'pylint' if PY3: if programs.find_program('pylint3'): PYLINT = 'pylint3' elif programs.find_program('python3-pylint'): PYLINT = 'python3-pylint' locale_codec = QTextCodec.codecForLocale() PYLINT_PATH = programs.find_program(PYLINT) def get_pylint_version(): """Return pylint version""" if PYLINT_PATH is None: return cwd = osp.dirname(PYLINT_PATH) args = ['--version'] if os.name == 'nt':
def is_lineprofiler_installed(): """ Checks if the program and the library for line_profiler is installed. """ return (programs.is_module_installed('line_profiler') and programs.find_program('kernprof') is not None)
def __init__(self, parent): """Widget constructor.""" SpyderPluginWidget.__init__(self, parent) self.tab_widget = None self.menu_actions = None self.server_retries = 0 self.server_ready = False self.port = select_port(default_port=8071) self.cmd = '/usr/bin/env bash' if WINDOWS: self.cmd = find_program('cmd.exe') self.server_stdout = subprocess.PIPE self.server_stderr = subprocess.PIPE self.stdout_file = osp.join(getcwd(), 'spyder_terminal_out.log') self.stderr_file = osp.join(getcwd(), 'spyder_terminal_err.log') if DEV: self.server_stdout = open(self.stdout_file, 'w') self.server_stderr = open(self.stderr_file, 'w') self.server = subprocess.Popen( [sys.executable, osp.join(LOCATION, 'server', 'main.py'), '--port', str(self.port), '--shell', self.cmd], stdout=self.server_stdout, stderr=self.server_stderr) self.main = parent self.terms = [] self.untitled_num = 0 self.project_path = None self.current_file_path = None self.current_cwd = getcwd() self.initialize_plugin() layout = QVBoxLayout() new_term_btn = create_toolbutton(self, icon=ima.icon('project_expanded'), tip=_('Open a new terminal'), triggered=self.create_new_term) menu_btn = create_toolbutton(self, icon=ima.icon('tooloptions'), tip=_('Options')) self.menu = QMenu(self) menu_btn.setMenu(self.menu) menu_btn.setPopupMode(menu_btn.InstantPopup) add_actions(self.menu, self.menu_actions) # if self.get_option('first_time', True): # self.setup_shortcuts() # self.shortcuts = self.create_shortcuts() corner_widgets = {Qt.TopRightCorner: [new_term_btn, menu_btn]} self.tabwidget = Tabs(self, menu=self.menu, actions=self.menu_actions, corner_widgets=corner_widgets, rename_tabs=True) if hasattr(self.tabwidget, 'setDocumentMode') \ and not sys.platform == 'darwin': # Don't set document mode to true on OSX because it generates # a crash when the console is detached from the main window # Fixes Issue 561 self.tabwidget.setDocumentMode(True) self.tabwidget.currentChanged.connect(self.refresh_plugin) self.tabwidget.move_data.connect(self.move_tab) self.tabwidget.set_close_function(self.close_term) layout.addWidget(self.tabwidget) self.setLayout(layout) new_term_shortcut = QShortcut(QKeySequence("Ctrl+Alt+Shift+T"), self, self.create_new_term) new_term_shortcut.setContext(Qt.WidgetWithChildrenShortcut) self.__wait_server_to_start()
def setup(self): """Perform the setup of plugin's main menu and signals.""" self.cmd = find_program(self.get_conf('shell')) server_args = [ sys.executable, '-m', 'spyder_terminal.server', '--port', str(self.port), '--shell', self.cmd] self.server = QProcess(self) env = self.server.processEnvironment() for var in os.environ: env.insert(var, os.environ[var]) self.server.setProcessEnvironment(env) self.server.errorOccurred.connect(self.handle_process_errors) self.server.setProcessChannelMode(QProcess.SeparateChannels) if self.stdout_file and self.stderr_file: self.server.setStandardOutputFile(self.stdout_file) self.server.setStandardErrorFile(self.stderr_file) self.server.start(server_args[0], server_args[1:]) self.color_scheme = self.get_conf('appearance', 'ui_theme') self.theme = self.get_conf('appearance', 'selected') # Menu menu = self.get_options_menu() # Actions new_terminal_toolbar_action = self.create_toolbutton( TerminalMainWidgetToolbarSections.New, text=_("Open a new terminal"), icon=self.create_icon('expand_selection'), triggered=lambda: self.create_new_term(), ) self.add_corner_widget( TerminalMainWidgetCornerToolbar.NewTerm, new_terminal_toolbar_action) new_terminal_cwd = self.create_action( TerminalMainWidgetActions.NewTerminalForCWD, text=_("New terminal in current working directory"), tip=_("Sets the pwd at the current working directory"), triggered=lambda: self.create_new_term(), shortcut_context='terminal', register_shortcut=True) self.new_terminal_project = self.create_action( TerminalMainWidgetActions.NewTerminalForProject, text=_("New terminal in current project"), tip=_("Sets the pwd at the current project directory"), triggered=lambda: self.create_new_term(path=self.project_path)) new_terminal_file = self.create_action( TerminalMainWidgetActions.NewTerminalForFile, text=_("New terminal in current Editor file"), tip=_("Sets the pwd at the directory that contains the current " "opened file"), triggered=lambda: self.create_new_term( path=self.current_file_path)) rename_tab_action = self.create_action( TerminalMainWidgetActions.RenameTab, text=_("Rename terminal"), triggered=lambda: self.tab_name_editor()) # Context menu actions self.create_action( TerminalMainWidgetActions.Copy, text=_('Copy text'), icon=self.create_icon('editcopy'), shortcut_context='terminal', triggered=lambda: self.copy(), register_shortcut=True) self.create_action( TerminalMainWidgetActions.Paste, text=_('Paste text'), icon=self.create_icon('editpaste'), shortcut_context='terminal', triggered=lambda: self.paste(), register_shortcut=True) self.create_action( TerminalMainWidgetActions.Clear, text=_('Clear terminal'), shortcut_context='terminal', triggered=lambda: self.clear(), register_shortcut=True) self.create_action( TerminalMainWidgetActions.ZoomIn, text=_('Zoom in'), shortcut_context='terminal', triggered=lambda: self.increase_font(), register_shortcut=True) self.create_action( TerminalMainWidgetActions.ZoomOut, text=_('Zoom out'), shortcut_context='terminal', triggered=lambda: self.decrease_font(), register_shortcut=True) # Create context menu self.create_menu(TermViewMenus.Context) # Add actions to options menu for item in [new_terminal_cwd, self.new_terminal_project, new_terminal_file]: self.add_item_to_menu( item, menu=menu, section=TerminalMainWidgetMenuSections.New) self.add_item_to_menu( rename_tab_action, menu=menu, section=TerminalMainWidgetMenuSections.TabActions)
def find_conda(): """Find conda executable.""" conda_exec = 'conda.bat' if WINDOWS else 'conda' conda = find_program(conda_exec) return conda
def is_hg_installed(): """Return True if Mercurial is installed""" return programs.find_program('hg') is not None
PythonModulesComboBox) from spyder.widgets.onecolumntree import OneColumnTree from spyder.widgets.variableexplorer.texteditor import TextEditor # This is needed for testing this module as a stand alone script try: _ = get_translation("pylint", "spyder_pylint") except KeyError as error: import gettext _ = gettext.gettext PYLINT = 'pylint' if PY3: if programs.find_program('pylint3'): PYLINT = 'pylint3' elif programs.find_program('python3-pylint'): PYLINT = 'python3-pylint' locale_codec = QTextCodec.codecForLocale() PYLINT_PATH = programs.find_program(PYLINT) def get_pylint_version(): """Return pylint version""" if PYLINT_PATH is None: return cwd = osp.dirname(PYLINT_PATH) args = ['--version']
shutil.copytree(docs_orig, docs_dest) # Create a minimal library inside Resources to add it to PYTHONPATH instead of # app_python_lib. This must be done when the user changes to an interpreter # that's not the one that comes with the app, to forbid importing modules # inside the app. minimal_lib = osp.join(app_python_lib, 'minimal-lib') os.mkdir(minimal_lib) minlib_pkgs = ['spyder', 'spyder_breakpoints', 'spyder_io_dcm', 'spyder_io_hdf5', 'spyder_profiler', 'spyder_pylint'] for p in minlib_pkgs: shutil.copytree(osp.join(app_python_lib, p), osp.join(minimal_lib, p)) # Add necessary Python programs to the app PROGRAMS = ['pylint', 'pycodestyle'] system_progs = [find_program(p) for p in PROGRAMS] progs_dest = [resources + osp.sep + p for p in PROGRAMS] for i in range(len(PROGRAMS)): shutil.copy2(system_progs[i], progs_dest[i]) # Add deps needed for PROGRAMS to the app deps = [] for package in os.listdir(system_python_lib): for d in DEPS: if package.startswith(d): deps.append(package) for i in deps: if osp.isdir(osp.join(system_python_lib, i)): shutil.copytree(osp.join(system_python_lib, i), osp.join(app_python_lib, i))
def start(self, wdir=None, args=None, pythonpath=None): filename = to_text_string(self.filecombo.currentText()) if wdir is None: wdir = self._last_wdir if wdir is None: wdir = osp.basename(filename) if args is None: args = self._last_args if args is None: args = [] if pythonpath is None: pythonpath = self._last_pythonpath self._last_wdir = wdir self._last_args = args self._last_pythonpath = pythonpath self.datelabel.setText(_('Profiling, please wait...')) self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.SeparateChannels) self.process.setWorkingDirectory(wdir) self.process.readyReadStandardOutput.connect(self.read_output) self.process.readyReadStandardError.connect( lambda: self.read_output(error=True)) self.process.finished.connect(self.finished) self.stop_button.clicked.connect(self.process.kill) if pythonpath is not None: env = [to_text_string(_pth) for _pth in self.process.systemEnvironment()] add_pathlist_to_PYTHONPATH(env, pythonpath) processEnvironment = QProcessEnvironment() for envItem in env: envName, separator, envValue = envItem.partition('=') processEnvironment.insert(envName, envValue) self.process.setProcessEnvironment(processEnvironment) self.output = '' self.error_output = '' if os.name == 'nt': # On Windows, one has to replace backslashes by slashes to avoid # confusion with escape characters (otherwise, for example, '\t' # will be interpreted as a tabulation): filename = osp.normpath(filename).replace(os.sep, '/') p_args = ['-lvb', '-o', '"' + self.DATAPATH + '"', '"' + filename + '"'] if args: p_args.extend(programs.shell_split(args)) executable = '"' + programs.find_program('kernprof') + '"' executable += ' ' + ' '.join(p_args) executable = executable.replace(os.sep, '/') self.process.start(executable) else: p_args = ['-lvb', '-o', self.DATAPATH, filename] if args: p_args.extend(programs.shell_split(args)) executable = 'kernprof' self.process.start(executable, p_args) running = self.process.waitForStarted() self.set_running_state(running) if not running: QMessageBox.critical(self, _("Error"), _("Process failed to start"))
def __init__(self, parent): """Widget constructor.""" SpyderPluginWidget.__init__(self, parent) self.tab_widget = None self.menu_actions = None self.server_retries = 0 self.server_ready = False self.port = select_port(default_port=8071) self.cmd = find_program(self.get_option('shell')) self.CONF = CONF self.server_stdout = subprocess.PIPE self.server_stderr = subprocess.PIPE self.stdout_file = osp.join(getcwd(), 'spyder_terminal_out.log') self.stderr_file = osp.join(getcwd(), 'spyder_terminal_err.log') if DEV: self.server_stdout = open(self.stdout_file, 'w') self.server_stderr = open(self.stderr_file, 'w') self.server = subprocess.Popen([ sys.executable, '-m', 'spyder_terminal.server', '--port', str(self.port), '--shell', self.cmd ], stdout=self.server_stdout, stderr=self.server_stderr) self.main = parent self.terms = [] self.untitled_num = 0 self.project_path = None self.current_file_path = None self.current_cwd = getcwd() try: # Spyder 3 self.initialize_plugin() except AttributeError: # Spyder 4 pass layout = QVBoxLayout() new_term_btn = create_toolbutton(self, icon=ima.icon('expand_selection'), tip=_('Open a new terminal'), triggered=self.create_new_term) corner_widgets = { Qt.TopRightCorner: [new_term_btn, self.options_button] } self.tabwidget = Tabs(self, menu=self._options_menu, actions=self.menu_actions, corner_widgets=corner_widgets, rename_tabs=True) if hasattr(self.tabwidget, 'setDocumentMode') \ and not sys.platform == 'darwin': # Don't set document mode to true on OSX because it generates # a crash when the console is detached from the main window # Fixes Issue 561 self.tabwidget.setDocumentMode(True) self.tabwidget.currentChanged.connect(self.refresh_plugin) self.tabwidget.move_data.connect(self.move_tab) self.tabwidget.set_close_function(self.close_term) layout.addWidget(self.tabwidget) self.setLayout(layout) new_term_shortcut = QShortcut( CONF.get_shortcut(CONF_SECTION, 'new_term'), self, self.create_new_term) new_term_shortcut.setContext(Qt.WidgetWithChildrenShortcut) self.color_scheme = CONF.get('appearance', 'ui_theme') self.theme = CONF.get('appearance', 'selected') self.__wait_server_to_start()
# # Copyright © Spyder Project Contributors # Licensed under the terms of the MIT License # """Tests for pyenv.py""" import sys import time import pytest from spyder.config.base import running_in_ci from spyder.utils.programs import find_program from spyder.utils.pyenv import get_list_pyenv_envs, get_list_pyenv_envs_cache if not find_program('pyenv'): pytest.skip("Requires pyenv to be installed", allow_module_level=True) @pytest.mark.skipif(not running_in_ci(), reason="Only meant for CIs") @pytest.mark.skipif(not sys.platform.startswith('linux'), reason="Only runs on Linux") def test_get_list_pyenv_envs(): output = get_list_pyenv_envs() expected_envs = ['pyenv: 3.8.1'] assert set(expected_envs) == set(output.keys()) @pytest.mark.skipif(not running_in_ci(), reason="Only meant for CIs") @pytest.mark.skipif(not sys.platform.startswith('linux'), reason="Only runs on Linux")
def test_find_program(): """Test if can find the program.""" assert find_program('git')
sys.path.append(osp.realpath(osp.dirname(__file__) + "/..")) from main import create_app LOCATION = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) LOCATION_SLASH = LOCATION.replace('\\', '/') LINE_END = '\n' SHELL = '/usr/bin/env bash' WINDOWS = os.name == 'nt' if WINDOWS: LINE_END = '\r\n' SHELL = find_program('cmd.exe') class TerminalServerTests(testing.AsyncHTTPTestCase): """Main server tests.""" def get_app(self): """Return HTTP/WS server.""" self.close_future = Future() return create_app(SHELL, self.close_future) def _mk_connection(self, pid): return websocket.websocket_connect( 'ws://127.0.0.1:{0}/terminals/{1}'.format(self.get_http_port(), pid)) @gen.coroutine