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: Several fixes for the Variable Explorer #3675

Merged
merged 23 commits into from
Dec 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6aef9e3
Variable Explorer: Don't show empty editors when the value obtained f…
ccordoba12 Nov 11, 2016
73e96ec
IPython Console: Add more excluded names from the Variable Explorer
ccordoba12 Nov 11, 2016
8567308
Variable Explorer: Improve how arbitrary objects are shown
ccordoba12 Nov 11, 2016
f81eaad
Variable Explorer: Fix an error when trying to change values of read …
ccordoba12 Nov 11, 2016
3dd3310
Variable Explorer: Fix default size of CollectionsEditor dialog
ccordoba12 Nov 11, 2016
97b7416
Variable Explorer: Show object attrs if all of them are hidden
ccordoba12 Nov 11, 2016
2cabdfe
Variable Explorer: Add a new function called get_object_attrs in coll…
ccordoba12 Nov 11, 2016
34afb05
Variable Explorer: Improve size of CollectionsEditor dialog a bit
ccordoba12 Nov 11, 2016
2315bff
Variable Explorer: Show empty arrays and images
ccordoba12 Nov 11, 2016
490cf5d
Variable Explorer: Rezise rows to their contents
ccordoba12 Nov 11, 2016
cfaa829
Update with 3.x
ccordoba12 Dec 11, 2016
d6ef966
Update with 3.x
ccordoba12 Dec 19, 2016
672d66e
Variable Explorer: Avoid error because of no execution_count
ccordoba12 Dec 19, 2016
d7b85d1
Fix style issues
ccordoba12 Dec 19, 2016
470d3aa
More style fixes
ccordoba12 Dec 19, 2016
2db3ae4
Variable Explorer: Remove the remote_editing option
ccordoba12 Dec 28, 2016
ed36002
Variable Explorer: Remove code related to remote editing
ccordoba12 Dec 28, 2016
56e3a5a
Variable Explorer: Remove more code related to remote editing
ccordoba12 Dec 28, 2016
e201ebc
Fix style issues
ccordoba12 Dec 28, 2016
cf64801
Variable Explorer: Move get_object_attrs from collectionseditor to utils
ccordoba12 Dec 28, 2016
642a193
Variable Explorer: Use get_type_string instead of my poorer get_data_…
ccordoba12 Dec 28, 2016
3d80c2c
Variable Explorer: Use to_text_string instead of str in get_type_string
ccordoba12 Dec 28, 2016
a540f41
Variable Explorer: Add missing docstrings
ccordoba12 Dec 28, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions spyder/config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,8 @@
'exclude_capitalized': False,
'exclude_unsupported': True,
'truncate': True,
'minmax': False,
'remote_editing': False,
}),
'minmax': False
}),
('editor',
{
'printer_header/font/family': SANS_SERIF,
Expand Down Expand Up @@ -656,7 +655,7 @@
# or if you want to *rename* options, then you need to do a MAJOR update in
# version, e.g. from 3.0.0 to 4.0.0
# 3. You don't need to touch this value if you're just adding a new option
CONF_VERSION = '30.0.0'
CONF_VERSION = '31.0.0'

# Main configuration instance
try:
Expand Down
16 changes: 3 additions & 13 deletions spyder/plugins/variableexplorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from spyder.config.base import _
from spyder.plugins import SpyderPluginMixin
from spyder.plugins.configdialog import PluginConfigPage
from spyder.utils import programs
from spyder.utils import icon_manager as ima
from spyder.widgets.variableexplorer.namespacebrowser import NamespaceBrowser
from spyder.widgets.variableexplorer.utils import REMOTE_SETTINGS
Expand All @@ -40,24 +39,15 @@ def setup_page(self):
for option, text in filter_data]

display_group = QGroupBox(_("Display"))
display_data = []
if programs.is_module_installed('numpy'):
display_data.append(('minmax', _("Show arrays min/max"), ''))
display_data.append(
('remote_editing', _("Edit data in the remote process"),
_("Editors are opened in the remote process for NumPy "
"arrays, PIL images, lists, tuples and dictionaries.\n"
"This avoids transfering large amount of data between "
"the remote process and Spyder (through the socket)."))
)
display_data = [('minmax', _("Show arrays min/max"), '')]
display_boxes = [self.create_checkbox(text, option, tip=tip)
for option, text, tip in display_data]

ar_layout = QVBoxLayout()
ar_layout.addWidget(ar_box)
ar_layout.addWidget(ar_spin)
ar_group.setLayout(ar_layout)

filter_layout = QVBoxLayout()
for box in filter_boxes:
filter_layout.addWidget(box)
Expand Down
12 changes: 8 additions & 4 deletions spyder/utils/ipython/spyder_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
ipykernel.pickleutil.can_map.pop('numpy.ndarray')


# Excluded variables from the Variable Explorer (i.e. they are not
# shown at all there)
EXCLUDED_NAMES = ['In', 'Out', 'exit', 'get_ipython', 'quit']


class SpyderKernel(IPythonKernel):
"""Spyder kernel for Jupyter"""

Expand Down Expand Up @@ -96,8 +101,7 @@ def get_namespace_view(self):
settings = self.namespace_view_settings
if settings:
ns = self._get_current_namespace()
more_excluded_names = ['In', 'Out']
view = make_remote_view(ns, settings, more_excluded_names)
view = make_remote_view(ns, settings, EXCLUDED_NAMES)
return view

def get_var_properties(self):
Expand All @@ -109,7 +113,7 @@ def get_var_properties(self):
if settings:
ns = self._get_current_namespace()
data = get_remote_data(ns, settings, mode='editable',
more_excluded_names=['In', 'Out'])
more_excluded_names=EXCLUDED_NAMES)

properties = {}
for name, value in list(data.items()):
Expand Down Expand Up @@ -180,7 +184,7 @@ def save_namespace(self, filename):
ns = self._get_current_namespace()
settings = self.namespace_view_settings
data = get_remote_data(ns, settings, mode='picklable',
more_excluded_names=['In', 'Out']).copy()
more_excluded_names=EXCLUDED_NAMES).copy()
return iofunctions.save(data, filename)

# --- For Pdb
Expand Down
2 changes: 1 addition & 1 deletion spyder/widgets/ipythonconsole/namespacebrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def _handle_execute_reply(self, msg):
self._reading = False

# Refresh namespacebrowser after the kernel starts running
exec_count = msg['content']['execution_count']
exec_count = msg['content'].get('execution_count', '')
if exec_count == 0 and self._kernel_is_starting:
if self.namespacebrowser is not None:
self.set_namespace_view_settings()
Expand Down
9 changes: 4 additions & 5 deletions spyder/widgets/variableexplorer/arrayeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def __init__(self, data, format="%.3f", xlabels=None, ylabels=None,
self.hue0 = huerange[0]
self.dhue = huerange[1]-huerange[0]
self.bgcolor_enabled = True
except TypeError:
except (TypeError, ValueError):
self.vmin = None
self.vmax = None
self.hue0 = None
Expand Down Expand Up @@ -606,11 +606,10 @@ def setup_and_check(self, data, title='', readonly=False,
self.data.flags.writeable = True
is_record_array = data.dtype.names is not None
is_masked_array = isinstance(data, np.ma.MaskedArray)
if data.size == 0:
self.error(_("Array is empty"))
return False

if data.ndim > 3:
self.error(_("Arrays with more than 3 dimensions are not supported"))
self.error(_("Arrays with more than 3 dimensions are not "
"supported"))
return False
if xlabels is not None and len(xlabels) != self.data.shape[1]:
self.error(_("The 'xlabels' argument length do no match array "
Expand Down
89 changes: 35 additions & 54 deletions spyder/widgets/variableexplorer/collectionseditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
array, DataFrame, display_to_value, FakeObject, get_color_name,
get_human_readable_type, get_size, Image, is_editable_type, is_known_type,
MaskedArray, ndarray, np_savetxt, Series, sort_against, try_to_eval,
unsorted_unique, value_to_display,)
unsorted_unique, value_to_display, get_object_attrs, get_type_string)

if ndarray is not FakeObject:
from spyder.widgets.variableexplorer.arrayeditor import ArrayEditor
Expand All @@ -62,18 +62,26 @@


class ProxyObject(object):
"""Dictionary proxy to an unknown object"""
"""Dictionary proxy to an unknown object."""

def __init__(self, obj):
"""Constructor."""
self.__obj__ = obj

def __len__(self):
return len(dir(self.__obj__))
"""Get len according to detected attributes."""
return len(get_object_attrs(self.__obj__))

def __getitem__(self, key):
"""Get attribute corresponding to key."""
return getattr(self.__obj__, key)

def __setitem__(self, key, value):
setattr(self.__obj__, key, value)
"""Set attribute corresponding to key with value."""
try:
setattr(self.__obj__, key, value)
except TypeError:
pass


class ReadOnlyCollectionsModel(QAbstractTableModel):
Expand Down Expand Up @@ -108,6 +116,7 @@ def get_data(self):
def set_data(self, data, coll_filter=None):
"""Set model data"""
self._data = data
data_type = get_type_string(data)

if coll_filter is not None and not self.remote and \
isinstance(data, (tuple, list, dict)):
Expand All @@ -129,13 +138,16 @@ def set_data(self, data, coll_filter=None):
if not self.names:
self.header0 = _("Key")
else:
self.keys = dir(data)
self.keys = get_object_attrs(data)
self._data = data = self.showndata = ProxyObject(data)
self.title += _("Object")
if not self.names:
self.header0 = _("Attribute")

self.title += ' ('+str(len(self.keys))+' '+ _("elements")+')'
if not isinstance(self._data, ProxyObject):
self.title += (' (' + str(len(self.keys)) + ' ' +
_("elements") + ')')
else:
self.title += data_type

self.total_rows = len(self.keys)
if self.total_rows > LARGE_NROWS:
Expand Down Expand Up @@ -414,6 +426,8 @@ def createEditor(self, parent, option, index):
return None
try:
value = self.get_value(index)
if value is None:
return None
except Exception as msg:
QMessageBox.critical(self.parent(), _("Error"),
_("Spyder was unable to retrieve the value of "
Expand All @@ -436,8 +450,6 @@ def createEditor(self, parent, option, index):
#---editor = ArrayEditor
elif isinstance(value, (ndarray, MaskedArray)) \
and ndarray is not FakeObject:
if value.size == 0:
return None
editor = ArrayEditor(parent)
if not editor.setup_and_check(value, title=key, readonly=readonly):
return
Expand All @@ -448,8 +460,6 @@ def createEditor(self, parent, option, index):
elif isinstance(value, Image) and ndarray is not FakeObject \
and Image is not FakeObject:
arr = array(value)
if arr.size == 0:
return None
editor = ArrayEditor(parent)
if not editor.setup_and_check(arr, title=key, readonly=readonly):
return
Expand Down Expand Up @@ -482,7 +492,7 @@ def createEditor(self, parent, option, index):
editor.setFont(get_font(font_size_delta=DEFAULT_SMALL_DELTA))
return editor
#---editor = TextEditor
elif is_text_string(value) and len(value)>40:
elif is_text_string(value) and len(value) > 40:
editor = TextEditor(value, key)
self.create_dialog(editor, dict(model=index.model(), editor=editor,
key=key, readonly=readonly))
Expand Down Expand Up @@ -1277,8 +1287,9 @@ def __init__(self, parent=None):
self.data_copy = None
self.widget = None

def setup(self, data, title='', readonly=False, width=500, remote=False,
def setup(self, data, title='', readonly=False, width=650, remote=False,
icon=None, parent=None):
"""Setup editor."""
if isinstance(data, dict):
# dictionnary
self.data_copy = data.copy()
Expand All @@ -1291,7 +1302,7 @@ def setup(self, data, title='', readonly=False, width=500, remote=False,
# unknown object
import copy
self.data_copy = copy.deepcopy(data)
datalen = len(dir(data))
datalen = len(get_object_attrs(data))
self.widget = CollectionsEditorWidget(self, self.data_copy, title=title,
readonly=readonly, remote=remote)

Expand All @@ -1311,8 +1322,8 @@ def setup(self, data, title='', readonly=False, width=500, remote=False,

constant = 121
row_height = 30
error_margin = 20
height = constant + row_height*min([15, datalen]) + error_margin
error_margin = 10
height = constant + row_height * min([10, datalen]) + error_margin
self.resize(width, height)

self.setWindowTitle(self.widget.get_title())
Expand Down Expand Up @@ -1364,13 +1375,11 @@ def __init__(self, parent, data, minmax=False,
copy_value_func=None, is_list_func=None, get_len_func=None,
is_array_func=None, is_image_func=None, is_dict_func=None,
get_array_shape_func=None, get_array_ndim_func=None,
oedit_func=None, plot_func=None, imshow_func=None,
plot_func=None, imshow_func=None,
is_data_frame_func=None, is_series_func=None,
show_image_func=None, remote_editing=False):
show_image_func=None):
BaseTableView.__init__(self, parent)

self.remote_editing_enabled = None

self.remove_values = remove_values_func
self.copy_value = copy_value_func
self.new_value = new_value_func
Expand All @@ -1384,7 +1393,6 @@ def __init__(self, parent, data, minmax=False,
self.is_dict = is_dict_func
self.get_array_shape = get_array_shape_func
self.get_array_ndim = get_array_ndim_func
self.oedit = oedit_func
self.plot = plot_func
self.imshow = imshow_func
self.show_image = show_image_func
Expand All @@ -1410,53 +1418,26 @@ def setup_menu(self, minmax):
menu = BaseTableView.setup_menu(self, minmax)
return menu

def oedit_possible(self, key):
if (self.is_list(key) or self.is_dict(key)
or self.is_array(key) or self.is_image(key)
or self.is_data_frame(key) or self.is_series(key)):
# If this is a remote dict editor, the following avoid
# transfering large amount of data through the socket
return True

def edit_item(self):
"""
Reimplement BaseTableView's method to edit item

Some supported data types are directly edited in the remote process,
thus avoiding to transfer large amount of data through the socket from
the remote process to Spyder
"""
if self.remote_editing_enabled:
index = self.currentIndex()
if not index.isValid():
return
key = self.model.get_key(index)
if self.oedit_possible(key):
# If this is a remote dict editor, the following avoid
# transfering large amount of data through the socket
self.oedit(key)
else:
BaseTableView.edit_item(self)
else:
BaseTableView.edit_item(self)


#==============================================================================
# =============================================================================
# Tests
#==============================================================================
# =============================================================================
def get_test_data():
"""Create test data"""
"""Create test data."""
import numpy as np
from spyder.pil_patch import Image
image = Image.fromarray(np.random.random_integers(255, size=(100, 100)),
mode='P')
testdict = {'d': 1, 'a': np.random.rand(10, 10), 'b': [1, 2]}
testdate = datetime.date(1945, 5, 8)

class Foobar(object):

def __init__(self):
self.text = "toto"
self.testdict = testdict
self.testdate = testdate

foobar = Foobar()
return {'object': foobar,
'str': 'kjkj kj k j j kj k jkj',
Expand Down
Loading