Skip to content
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

PR: Add disambiguation for dedicated IPython consoles #4700

Merged
merged 5 commits into from Jul 6, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 28 additions & 6 deletions spyder/plugins/ipythonconsole.py
Expand Up @@ -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 encoding, programs
from spyder.utils import encoding, programs, sourcecode
from spyder.utils.misc import get_error_match, remove_backslashes
from spyder.widgets.findreplace import FindReplace
from spyder.widgets.ipythonconsole import ClientWidget
Expand Down Expand Up @@ -607,6 +607,7 @@ def __init__(self, parent, testing=False):

self.master_clients = 0
self.clients = []
self.filenames = []
self.mainwindow_close = False
self.create_new_client_if_empty = True
self.testing = testing
Expand Down Expand Up @@ -740,6 +741,7 @@ def refresh_plugin(self):
sw = client.shellwidget
self.variableexplorer.set_shellwidget_from_id(id(sw))
self.help.set_shell(sw)
self.update_tabs_text()
self.update_plugin_title.emit()

def get_plugin_actions(self):
Expand Down Expand Up @@ -907,7 +909,7 @@ def write_to_stdin(self, line):

@Slot()
@Slot(bool)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add two more signatures below this line

@Slot(object)
@Slot(bool, object)

to cover all possible entries of create_new_client.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, give me a moment to think this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be

@Slot(str)
@Slot(bool, str)

and you need to change below

filename=''

instead of

filename=None

def create_new_client(self, give_focus=True):
def create_new_client(self, give_focus=True, filename=None):
"""Create a new client"""
self.master_clients += 1
client_id = dict(int_id=to_text_string(self.master_clients),
Expand All @@ -920,7 +922,7 @@ def create_new_client(self, give_focus=True):
interpreter_versions=self.interpreter_versions(),
connection_file=cf,
menu_actions=self.menu_actions)
self.add_tab(client, name=client.get_name())
self.add_tab(client, name=client.get_name(), filename=filename)

if cf is None:
error_msg = _("The directory {} is not writable and it is "
Expand Down Expand Up @@ -1192,8 +1194,10 @@ def close_client(self, index=None, client=None, force=False):
# Note: client index may have changed after closing related widgets
self.tabwidget.removeTab(self.tabwidget.indexOf(client))
self.clients.remove(client)
self.filenames.pop(index)
if not self.tabwidget.count() and self.create_new_client_if_empty:
self.create_new_client()
self.update_tabs_text()
self.update_plugin_title.emit()

def get_client_index_from_id(self, client_id):
Expand Down Expand Up @@ -1265,15 +1269,16 @@ def create_client_from_path(self, path):
def create_client_for_file(self, filename):
"""Create a client to execute code related to a file."""
# Create client
self.create_new_client()
self.create_new_client(filename=filename)

# Don't increase the count of master clients
self.master_clients -= 1

# Rename client tab with filename
client = self.get_current_client()
client.allow_rename = False
self.rename_client_tab(client, filename)
tab_text = self.disambiguate_fname(filename)
self.rename_client_tab(client, tab_text)

def get_client_for_file(self, filename):
"""Get client associated with a given file."""
Expand Down Expand Up @@ -1355,25 +1360,42 @@ def restart_kernel(self):
client.restart_kernel()

#------ Public API (for tabs) ---------------------------------------------
def add_tab(self, widget, name):
def add_tab(self, widget, name, filename=None):
"""Add tab"""
self.clients.append(widget)
index = self.tabwidget.addTab(widget, name)
self.filenames.insert(index, filename)
self.tabwidget.setCurrentIndex(index)
if self.dockwidget and not self.ismaximized:
self.dockwidget.setVisible(True)
self.dockwidget.raise_()
self.activateWindow()
widget.get_control().setFocus()
self.update_tabs_text()

def move_tab(self, index_from, index_to):
"""
Move tab (tabs themselves have already been moved by the tabwidget)
"""
filename = self.filenames.pop(index_from)
client = self.clients.pop(index_from)
self.filenames.insert(index_to, filename)
self.clients.insert(index_to, client)
self.update_plugin_title.emit()

def disambiguate_fname(self, fname):
"""Generate a file name without ambiguation."""
files_path_list = [filename for filename in self.filenames
if filename is not None]
Copy link
Member

@ccordoba12 ccordoba12 Jul 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With my last suggested change, you need to change this line to

if filename]

instead of

if filename is not None]

return sourcecode.disambiguate_fname(files_path_list, fname)

def update_tabs_text(self):
"""Update the text from the tabs."""
for index, fname in enumerate(self.filenames):
if fname is not None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this also needs to change to

if fname:

client = self.clients[index]
self.rename_client_tab(client, self.disambiguate_fname(fname))

def rename_client_tab(self, client, given_name):
"""Rename client's tab"""
index = self.get_client_index_from_id(id(client))
Expand Down
36 changes: 36 additions & 0 deletions spyder/plugins/tests/test_ipythonconsole.py
Expand Up @@ -35,6 +35,7 @@
#==============================================================================
SHELL_TIMEOUT = 20000
PYQT_WHEEL = PYQT_VERSION > '5.6'
TEMP_DIRECTORY = tempfile.gettempdir()


#==============================================================================
Expand Down Expand Up @@ -76,6 +77,40 @@ def close_console():
#==============================================================================
# Tests
#==============================================================================
@flaky(max_runs=3)
def test_console_disambiguation(ipyconsole, qtbot):
"""Test the disambiguation of dedicated consoles."""
# Create directories and file for TEMP_DIRECTORY/a/b/c.py
# and TEMP_DIRECTORY/a/d/c.py
dir_b = osp.join(TEMP_DIRECTORY, 'a', 'b')
filename_b = osp.join(dir_b, 'c.py')
if not osp.isdir(dir_b):
os.makedirs(dir_b)
if not osp.isfile(filename_b):
file_c = open(filename_b, 'w+')
file_c.close()
dir_d = osp.join(TEMP_DIRECTORY, 'a', 'd')
filename_d = osp.join(dir_d, 'c.py')
if not osp.isdir(dir_d):
os.makedirs(dir_d)
if not osp.isfile(filename_d):
file_e = open(filename_d, 'w+')
file_e.close()

# Create new client and assert name without disambiguation
ipyconsole.create_client_for_file(filename_b)
client = ipyconsole.get_current_client()
assert client.get_name() == 'c.py/A'

# Create new client and assert name with disambiguation
ipyconsole.create_client_for_file(filename_d)
client = ipyconsole.get_current_client()
assert client.get_name() == 'c.py - d/A'
ipyconsole.tabwidget.setCurrentIndex(1)
client = ipyconsole.get_current_client()
assert client.get_name() == 'c.py - b/A'


@flaky(max_runs=3)
def test_console_coloring(ipyconsole, qtbot):

Expand All @@ -99,6 +134,7 @@ def test_console_coloring(ipyconsole, qtbot):
assert console_background_color.strip() == editor_background_color.strip()
assert console_font_color.strip() == editor_font_color.strip()


@flaky(max_runs=3)
@pytest.mark.skipif(os.name == 'nt', reason="It doesn't work on Windows")
def test_get_env(ipyconsole, qtbot):
Expand Down
2 changes: 1 addition & 1 deletion spyder/utils/sourcecode.py
Expand Up @@ -183,7 +183,7 @@ def differentiate_prefix(path_components0, path_components1):
path_0 = '/'
return path_0

def get_file_title(files_path_list, filename):
def disambiguate_fname(files_path_list, filename):
"""Get tab title without ambiguation."""
fname = os.path.basename(filename)
same_name_files = get_same_name_files(files_path_list, fname)
Expand Down
6 changes: 3 additions & 3 deletions spyder/utils/tests/test_sourcecode.py
Expand Up @@ -84,7 +84,7 @@ def test_shortest_path():
shortest_path = os.path.join(*['c:','','documents','test','test.py'])
assert sourcecode.shortest_path(files_path_list) == shortest_path

def test_get_file_title():
def test_disambiguate_fname():
files_path_list = []
if sys.platform.startswith('linux'):
fname0 = os.path.join(*['','','documents','test','test.py'])
Expand All @@ -98,8 +98,8 @@ def test_get_file_title():
files_path_list.append(fname1)
title0 = 'test.py - ' + os.path.join(*['test'])
title1 = 'test.py - ' + os.path.join(*['projects','test'])
assert sourcecode.get_file_title(files_path_list, fname0) == title0
assert sourcecode.get_file_title(files_path_list, fname1) == title1
assert sourcecode.disambiguate_fname(files_path_list, fname0) == title0
assert sourcecode.disambiguate_fname(files_path_list, fname1) == title1

if __name__ == '__main__':
pytest.main()
Expand Down
2 changes: 1 addition & 1 deletion spyder/widgets/editor.py
Expand Up @@ -1135,7 +1135,7 @@ def get_tab_text(self, index, is_modified=None, is_readonly=None):
"""Return tab title."""
files_path_list = [finfo.filename for finfo in self.data]
fname = self.data[index].filename
fname = sourcecode.get_file_title(files_path_list, fname)
fname = sourcecode.disambiguate_fname(files_path_list, fname)
return self.__modified_readonly_title(fname,
is_modified, is_readonly)

Expand Down