From f9e38128e12482627d69233976bfa1abe8acf620 Mon Sep 17 00:00:00 2001 From: jlstevens Date: Sun, 11 May 2014 18:17:08 +0100 Subject: [PATCH 01/13] Initial commit of py3 branch --- dataviews/__init__.py | 8 ++-- dataviews/dataviews.py | 10 ++--- dataviews/ipython.py | 40 +++++++++---------- dataviews/ndmapping.py | 24 ++++++------ dataviews/operation.py | 10 ++--- dataviews/options.py | 16 ++++---- dataviews/plots.py | 56 ++++++++++++++------------- dataviews/sheetcoords.py | 2 +- dataviews/sheetviews.py | 22 +++++------ dataviews/tests/testboundingregion.py | 2 +- dataviews/tests/testcomparisons.py | 2 +- dataviews/tests/testndmapping.py | 12 +++--- dataviews/tests/testsheetviews.py | 2 +- dataviews/tests/testviews.py | 2 +- dataviews/views.py | 24 ++++++------ 15 files changed, 118 insertions(+), 114 deletions(-) diff --git a/dataviews/__init__.py b/dataviews/__init__.py index 536b1d9a03..94f43f4ff2 100644 --- a/dataviews/__init__.py +++ b/dataviews/__init__.py @@ -4,10 +4,10 @@ cwd = os.path.abspath(os.path.split(__file__)[0]) sys.path.insert(0, os.path.join(cwd, '..', 'param')) -from views import * # pyflakes:ignore (API import) -from dataviews import * # pyflakes:ignore (API import) -from sheetviews import * # pyflakes:ignore (API import) -from ndmapping import * # pyflakes:ignore (API import) +from .views import * # pyflakes:ignore (API import) +from .dataviews import * # pyflakes:ignore (API import) +from .sheetviews import * # pyflakes:ignore (API import) +from .ndmapping import * # pyflakes:ignore (API import) def public(obj): diff --git a/dataviews/dataviews.py b/dataviews/dataviews.py index ceb1e4b1a7..172b36d71f 100644 --- a/dataviews/dataviews.py +++ b/dataviews/dataviews.py @@ -3,8 +3,8 @@ import param -from ndmapping import NdMapping, map_type -from views import View, Overlay, Annotation, Layout, GridLayout +from .ndmapping import NdMapping, map_type +from .views import View, Overlay, Annotation, Layout, GridLayout def find_minmax(lims, olims): """ @@ -378,7 +378,7 @@ def split_axis(self, x_axis): self._check_key_type = False # Speed optimization x_ndim = self.dim_index(x_axis) - keys = self._data.keys() + keys = list(self._data.keys()) x_vals, dim_values = self._split_keys_by_axis(keys, x_axis) split_data = map_type() @@ -466,7 +466,7 @@ def sample(self, samples=[], x_axis=None, group_by=[]): # Generate labels legend_label = ', '.join(self.dim_dict[name].pprint_value(val) for name, val in overlay_items) - ylabel = x_axis_data.values()[0].label + ylabel = list(x_axis_data.values())[0].label label, xlabel, ylabel = self._curve_labels(x_axis, samples[sample_ind], ylabel) @@ -563,7 +563,7 @@ def __init__(self, data, **kwargs): super(Table, self).__init__(data=data, **kwargs) # Assume OrderedDict if not a vanilla Python dict - self.headings = self.data.keys() + self.headings = list(self.data.keys()) if type(self.data) == dict: self.headings = sorted(self.headings) diff --git a/dataviews/ipython.py b/dataviews/ipython.py index 8fd603b8b3..0c30abfb69 100644 --- a/dataviews/ipython.py +++ b/dataviews/ipython.py @@ -15,11 +15,11 @@ from functools import wraps import traceback, itertools, string -from dataviews import Stack -from plots import Plot, GridLayoutPlot, viewmap, channel_modes -from sheetviews import GridLayout, CoordinateGrid -from views import View, Overlay, Annotation, Layout -from options import options, channels, PlotOpts, StyleOpts, ChannelOpts +from .dataviews import Stack +from .plots import Plot, GridLayoutPlot, viewmap, channel_modes +from .sheetviews import GridLayout, CoordinateGrid +from .views import View, Overlay, Annotation, Layout +from .options import options, channels, PlotOpts, StyleOpts, ChannelOpts # Variables controlled via the %view magic PERCENTAGE_SIZE, FPS, FIGURE_FORMAT = 100, 20, 'png' @@ -88,7 +88,7 @@ def _set_animation_options(self, anim_spec): format_choice, fps_str = ((anim_spec, None) if (':' not in anim_spec) else anim_spec.rsplit(':')) if format_choice not in self.anim_formats: - print "Valid animations types: %s" % ', '.join(self.anim_formats) + print("Valid animations types: %s" % ', '.join(self.anim_formats)) return False elif fps_str is None: VIDEO_FORMAT = format_choice @@ -96,7 +96,7 @@ def _set_animation_options(self, anim_spec): try: fps = int(fps_str) except: - print "Invalid frame rate: '%s'" % fps_str + print("Invalid frame rate: '%s'" % fps_str) return False VIDEO_FORMAT, FPS = format_choice, fps @@ -111,7 +111,7 @@ def _set_size(self, size_spec): except: size = None if (size is None) or (size < 0): - print "Percentage size must be an integer larger than zero." + print("Percentage size must be an integer larger than zero.") return False else: PERCENTAGE_SIZE = size @@ -123,7 +123,7 @@ def _parse_settings(self, opts): fig_fmt = [('svg' in opts), ('png' in opts)] if all(fig_fmt): success = False - print "Please select either png or svg for static output" + print("Please select either png or svg for static output") elif True in fig_fmt: figure_format = ['svg', 'png'][fig_fmt.index(True)] FIGURE_FORMAT= figure_format @@ -156,12 +156,12 @@ def view(self, line, cell=None): if cell is None and success: info = (VIDEO_FORMAT.upper(), FIGURE_FORMAT.upper(), PERCENTAGE_SIZE, FPS) - print "Displaying %s animation and %s figures [%d%% size, %s FPS]" % info + print("Displaying %s animation and %s figures [%d%% size, %s FPS]" % info) elif cell and success: self.shell.run_cell(cell) [FIGURE_FORMAT, VIDEO_FORMAT, PERCENTAGE_SIZE, FPS] = start_opts else: - print self.usage_info + print(self.usage_info) @@ -258,9 +258,9 @@ def option_completer(cls, k,v): line_tail = line[len('%%channels'):] op_name = line_tail[::-1].rsplit('[')[1][::-1].strip().split()[-1] if op_name in channel_modes: - return channel_modes[op_name].params().keys() + return list(channel_modes[op_name].params().keys()) else: - return channel_modes.keys() + return list(channel_modes.keys()) @magics_class @@ -308,7 +308,7 @@ def collect(cls, obj, attr='style'): for subview in obj: group.update(cls.collect(subview, attr)) elif isinstance(obj, Stack) and not issubclass(obj.type, Overlay): - key_lists = [cls.collect(el, attr).keys() for el in obj] + key_lists = [list(cls.collect(el, attr).keys()) for el in obj] values = set(el for els in key_lists for el in els) for val in values: group.update({val:obj.type}) @@ -363,7 +363,7 @@ def set_view_options(cls, obj): # Implements the %%labels magic if cls.show_labels: - labels = cls.collect(obj, 'label').keys() + labels = list(cls.collect(obj, 'label').keys()) info = (len(labels), labels.count('')) summary = ("%d objects inspected, %d without labels. " "The set of labels found:

 " % info) @@ -576,10 +576,10 @@ def _line_magic(self, line): if not kwarg_map: info = (len(options.style.keys()), len([k for k in options.style.keys() if k.startswith('Custom')])) - print "There are %d style options defined (%d custom object styles)." % info + print("There are %d style options defined (%d custom object styles)." % info) info = (len(options.plotting.keys()), len([k for k in options.plotting.keys() if k.startswith('Custom')])) - print "There are %d plot options defined (%d custom object plot settings)." % info + print("There are %d plot options defined (%d custom object plot settings)." % info) return self._define_options(kwarg_map, verbose=verbose) @@ -662,7 +662,7 @@ def animate(anim, writer, mime_type, anim_kwargs, extra_args, tag): def HTML_video(plot): anim = plot.anim(fps=FPS) writers = animation.writers.avail - for fmt in [VIDEO_FORMAT] + ANIMATION_OPTS.keys(): + for fmt in [VIDEO_FORMAT] + list(ANIMATION_OPTS.keys()): if ANIMATION_OPTS[fmt][0] in writers: try: return animate(anim, *ANIMATION_OPTS[fmt]) @@ -778,7 +778,7 @@ def projection_display(grid, size=256): size_factor*grid.shape[0]*get_plot_size()[0]) magic_info = process_view_magics(grid) if magic_info: return magic_info - opts = dict(options.plotting(grid.values()[-1]).opts, size=grid_size) + opts = dict(options.plotting(list(grid.values())[-1]).opts, size=grid_size) gridplot = viewmap[grid.__class__](grid, **opts) if len(gridplot)==1: fig = gridplot() @@ -829,7 +829,7 @@ def update_matplotlib_rc(): def load_ipython_extension(ip, verbose=True): - if verbose: print message + if verbose: print(message) global _loaded if not _loaded: diff --git a/dataviews/ndmapping.py b/dataviews/ndmapping.py index 751339f870..3bce4a396b 100644 --- a/dataviews/ndmapping.py +++ b/dataviews/ndmapping.py @@ -11,7 +11,7 @@ try: from collections import OrderedDict except: - from odict import OrderedDict # pyflakes:ignore (try/except import) + from .odict import OrderedDict # pyflakes:ignore (try/except import) map_type = OrderedDict @@ -153,9 +153,9 @@ def write_metadata(self, kwargs): Acts as a param override, placing any kwargs, which are not parameters into the metadata dictionary. """ - items = kwargs.items() + items = list(kwargs.items()) if 'metadata' in kwargs: - items = kwargs.pop('metadata').items() + items + items = list(kwargs.pop('metadata').items()) + items metadata = AttrDict(self.metadata, **dict([(k, v) for k, v in items if k not in self.params()])) for key in metadata: @@ -278,7 +278,7 @@ def clone(self, items=None, **kwargs): return self.__class__(initial_items=items, **settings) def copy(self): - return self.clone(self.items()) + return self.clone(list(self.items())) def dframe(self, value_label='data'): @@ -370,7 +370,7 @@ def last(self): """" Returns the item highest data item along the map dimensions. """ - return self._data.values()[-1] if len(self) else None + return list(self._data.values())[-1] if len(self) else None @property @@ -378,7 +378,7 @@ def last_key(self): """" Returns the last key. """ - return self.keys()[-1] if len(self) else None + return list(self.keys())[-1] if len(self) else None def dim_index(self, dimension_label): @@ -403,15 +403,15 @@ def keys(self): if self.ndims == 1: return [k[0] for k in self._data.keys()] else: - return self._data.keys() + return list(self._data.keys()) def values(self): - return self._data.values() + return list(self._data.values()) def items(self): - return zip(self.keys(), self.values()) + return list(zip(list(self.keys()), list(self.values()))) def get(self, key, default=None): @@ -432,14 +432,16 @@ def pop(self, *args): def __iter__(self): return self + def next(self): # For Python 2 and 3 compatibility + return self.__next__() - def next(self): + def __next__(self): """ Implements the iterable interface, returning values unlike a standard dictionary. """ if self._next_ind < len(self): - val = self.values()[self._next_ind] + val = list(self.values())[self._next_ind] self._next_ind += 1 return val else: diff --git a/dataviews/operation.py b/dataviews/operation.py index 45bdb3f1c8..e383e3c04d 100644 --- a/dataviews/operation.py +++ b/dataviews/operation.py @@ -12,12 +12,12 @@ import param from param import ParamOverrides -from views import Overlay -from sheetviews import SheetView, SheetStack, SheetLayer, DataGrid -from dataviews import DataLayer, DataStack, Stack, Table, TableStack -from sheetviews import GridLayout, CoordinateGrid +from .views import Overlay +from .sheetviews import SheetView, SheetStack, SheetLayer, DataGrid +from .dataviews import DataLayer, DataStack, Stack, Table, TableStack +from .sheetviews import GridLayout, CoordinateGrid -from options import options, GrayNearest +from .options import options, GrayNearest rgb_to_hsv = np.vectorize(colorsys.rgb_to_hsv) hsv_to_rgb = np.vectorize(colorsys.hsv_to_rgb) diff --git a/dataviews/options.py b/dataviews/options.py index c5b4113c4b..aef49c6c0e 100644 --- a/dataviews/options.py +++ b/dataviews/options.py @@ -11,7 +11,7 @@ try: from collections import OrderedDict except: - from odict import OrderedDict # pyflakes:ignore (try/except import) + from .odict import OrderedDict # pyflakes:ignore (try/except import) class Cycle(object): @@ -57,17 +57,17 @@ def _expand_styles(self, kwargs): if not filter_cycles: return [kwargs] - filter_names, filter_values = zip(*filter_cycles) + filter_names, filter_values = list(zip(*filter_cycles)) if not all(len(c)==len(filter_values[0]) for c in filter_values): raise Exception("Cycle objects supplied with different lengths") - cyclic_tuples = zip(*[val.elements for val in filter_values]) + cyclic_tuples = list(zip(*[val.elements for val in filter_values])) return [ dict(zip(filter_names, tps), **filter_static) for tps in cyclic_tuples] def keys(self): "The keyword names defined in the options." - return self.items.keys() + return list(self.items.keys()) def __getitem__(self, index): @@ -166,7 +166,7 @@ def __getattr__(self, name): """ Provide attribute access for the Opts in the Options. """ - keys = self.__dict__['_items'].keys() + keys = list(self.__dict__['_items'].keys()) if name in keys: return self[name] raise AttributeError(name) @@ -189,14 +189,14 @@ def keys(self): The list of all options in the OptionMap, including options associated with individual view objects. """ - return self._items.keys() + return list(self._items.keys()) def values(self): """ All the Style objects in the OptionMap. """ - return self._items.values() + return list(self._items.values()) def __contains__(self, k): @@ -308,7 +308,7 @@ def __dir__(self): """ default_dir = dir(type(self)) + list(self.__dict__) names = [o.name for o in self._opttypes.values()] - return sorted(set(default_dir + self.keys() + names)) + return sorted(set(default_dir + list(self.keys()) + names)) diff --git a/dataviews/plots.py b/dataviews/plots.py index e975922d95..b4921579f3 100644 --- a/dataviews/plots.py +++ b/dataviews/plots.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import copy from itertools import groupby @@ -12,14 +14,14 @@ import param -from dataviews import NdMapping, Stack, Table, TableStack -from dataviews import DataStack, DataOverlay, DataLayer, Curve, Histogram -from sheetviews import SheetView, SheetOverlay, Contours, \ +from .dataviews import NdMapping, Stack, Table, TableStack +from .dataviews import DataStack, DataOverlay, DataLayer, Curve, Histogram +from .sheetviews import SheetView, SheetOverlay, Contours, \ SheetStack, Points, CoordinateGrid, DataGrid -from views import GridLayout, Layout, Overlay, View, Annotation +from .views import GridLayout, Layout, Overlay, View, Annotation -from options import options, channels -from operation import RGBA, HCS, AlphaOverlay +from .options import options, channels +from .operation import RGBA, HCS, AlphaOverlay class Plot(param.Parameterized): @@ -198,7 +200,7 @@ def anim(self, start=0, stop=None, fps=30): frames may be specified as well as the fps. """ figure = self() - frames = range(len(self))[slice(start, stop, 1)] + frames = list(range(len(self)))[slice(start, stop, 1)] anim = animation.FuncAnimation(figure, self.update_frame, frames=frames, interval = 1000.0/fps) @@ -264,7 +266,7 @@ def __call__(self, axis=None, cyclic_index=0): def update_frame(self, n): n = n if n < len(self) else len(self) - 1 - contours = self._stack.values()[n] + contours = list(self._stack.values())[n] self.handles['line_segments'].set_paths(contours.data) self._update_title(contours) plt.draw() @@ -391,15 +393,15 @@ def __call__(self, axis=None, cyclic_index=0, lbrt=None): raise Exception("Annotations can only be plotted as part of overlays.") self.handles['axis'] = axis - handles = self._draw_annotations(self._stack.last, axis, self._stack.keys()[-1]) + handles = self._draw_annotations(self._stack.last, axis, list(self._stack.keys())[-1]) self.handles['annotations'] = handles return axis def update_frame(self, n, lbrt=None): n = n if n < len(self) else len(self) - 1 - annotation = self._stack.values()[n] - key = self._stack.keys()[n] + annotation = list(self._stack.values())[n] + key = list(self._stack.keys())[n] axis = self.handles['axis'] # Cear all existing annotations @@ -441,7 +443,7 @@ def __call__(self, axis=None, cyclic_index=0): def update_frame(self, n): n = n if n < len(self) else len(self) - 1 - points = self._stack.values()[n] + points = list(self._stack.values())[n] self.handles['scatter'].set_offsets(points.data) self._update_title(points) plt.draw() @@ -491,7 +493,7 @@ def update_frame(self, n): n = n if n < len(self) else len(self) - 1 im = self.handles.get('im', None) - sheetview = self._stack.values()[n] + sheetview = list(self._stack.values())[n] im.set_data(sheetview.data) if self.normalize_individually: @@ -587,7 +589,7 @@ def __call__(self, axis=None): in groupby(sorted_stacks, lambda s: s.style)) for zorder, stack in enumerate(stacks): - cyclic_index, _ = style_groups[stack.style].next() + cyclic_index, _ = next(style_groups[stack.style]) plotype = viewmap[stack.type] plot = plotype(stack, zorder=zorder, **options.plotting(stack).opts) @@ -992,7 +994,7 @@ def __call__(self, axis=None): def update_frame(self, n): n = n if n < len(self) else len(self) - 1 for i, plot in enumerate(self.handles['projs']): - view = self.grid.values()[i].values()[n] + view = list(self.grid.values())[i].values()[n] if isinstance(view, SheetOverlay): data = view[-1].data if self.situate else view[-1].roi.data else: @@ -1067,7 +1069,7 @@ def __call__(self, axis=None, lbrt=None, **kwargs): in groupby(stacks, lambda s: s.style)) for zorder, stack in enumerate(stacks): - cyclic_index, _ = style_groups[stack.style].next() + cyclic_index, _ = next(style_groups[stack.style]) plotopts = options.plotting(stack).opts if zorder == 0: @@ -1094,7 +1096,7 @@ def update_frame(self, n): n = n if n < len(self) else len(self) - 1 for zorder, plot in enumerate(self.plots): if zorder == 0: - lbrt = self._stack.values()[n].lbrt if self.rescale else self._stack.lbrt + lbrt = list(self._stack.values())[n].lbrt if self.rescale else self._stack.lbrt plot.update_frame(n, lbrt) @@ -1157,7 +1159,7 @@ def _rotate(self, seq, n=1): def _curve_values(self, coord, curve): """Return the x, y, and x ticks values for the specified curve from the curve_dict""" x, y = coord - x_values = curve.keys() + x_values = list(curve.keys()) y_values = [curve[k, x, y] for k in x_values] self.x_values = x_values return x_values, y_values, x_values @@ -1166,7 +1168,7 @@ def _curve_values(self, coord, curve): def _reduce_ticks(self, x_values): values = [x_values[0]] rangex = float(x_values[-1]) - x_values[0] - for i in xrange(1, self.num_ticks+1): + for i in range(1, self.num_ticks+1): values.append(values[-1]+rangex/(self.num_ticks)) return values, [self._format_x_tick_label(x) for x in values] @@ -1182,7 +1184,7 @@ def _cyclic_reduce_ticks(self, x_values): labels.append(x_values[0]) label_step = step values.append(x_values[0]) - for i in xrange(0, self.num_ticks - 1): + for i in range(0, self.num_ticks - 1): labels.append(labels[-1] + label_step) values.append(values[-1] + step) return values, [self._cyclic_format_x_tick_label(x) for x in labels] @@ -1249,7 +1251,7 @@ def __call__(self, axis=None, cyclic_index=0, lbrt=None): def update_frame(self, n, lbrt=None): n = n if n < len(self) else len(self) - 1 - curveview = self._stack.values()[n] + curveview = list(self._stack.values())[n] if lbrt is None: lbrt = curveview.lbrt if self.rescale_individually else self._stack.lbrt @@ -1287,7 +1289,7 @@ def __init__(self, grid, **kwargs): self.grid = grid self.subplots = [] - x, y = zip(*grid.keys()) + x, y = list(zip(*list(grid.keys()))) self.rows, self.cols = (len(set(x)), len(set(y))) self._gridspec = gridspec.GridSpec(self.rows, self.cols) extra_opts = options.plotting(self.grid).opts @@ -1435,7 +1437,7 @@ def __call__(self, axis=None): def update_frame(self, n): n = n if n < len(self) else len(self) - 1 - tableview = self._stack.values()[n] + tableview = list(self._stack.values())[n] table = self.handles['table'] for coords, cell in table.get_celld().items(): @@ -1549,10 +1551,10 @@ def _compute_ticks(self, edges, widths, lims): if self.cyclic: x0, x1, _, _ = lims xvals = np.linspace(x0, x1, self.num_ticks) - labels = ["%.0f" % np.rad2deg(x) + u'\N{DEGREE SIGN}' + labels = ["%.0f" % np.rad2deg(x) + '\N{DEGREE SIGN}' for x in xvals] else: - edge_inds = range(len(edges)) + edge_inds = list(range(len(edges))) step = len(edges)/float(self.num_ticks-1) inds = [0] + [edge_inds[int(i*step)-1] for i in range(1, self.num_ticks)] xvals = [edges[i]+widths[i]/2. for i in inds] @@ -1606,7 +1608,7 @@ def update_frame(self, n, lbrt=None): Update the plot for an animation. """ n = n if n < len(self) else len(self) - 1 - hist = self._stack.values()[n] + hist = list(self._stack.values())[n] # Process values, axes and style edges, hvals, widths, lims = self._process_hist(hist, lbrt) @@ -1658,7 +1660,7 @@ def _update_plot(self, n, bars, lims): individually = options.plotting(self.main).opts.get('normalize_individually', False) if isinstance(self.main, Stack): - main_range = self.main.values()[n].range if individually else self.main.range + main_range = list(self.main.values())[n].range if individually else self.main.range elif isinstance(self.main, View): main_range = self.main.range diff --git a/dataviews/sheetcoords.py b/dataviews/sheetcoords.py index f4540a1827..4620511196 100644 --- a/dataviews/sheetcoords.py +++ b/dataviews/sheetcoords.py @@ -79,7 +79,7 @@ from numpy import array,floor,ceil,round_,arange -from boundingregion import BoundingBox +from .boundingregion import BoundingBox diff --git a/dataviews/sheetviews.py b/dataviews/sheetviews.py index 3463947a86..02306d5b4f 100644 --- a/dataviews/sheetviews.py +++ b/dataviews/sheetviews.py @@ -2,12 +2,12 @@ import param -from boundingregion import BoundingBox, BoundingRegion -from dataviews import Stack, Histogram, DataStack, find_minmax -from ndmapping import NdMapping, Dimension -from options import options -from sheetcoords import SheetCoordinateSystem, Slice -from views import View, Overlay, Annotation, GridLayout +from .boundingregion import BoundingBox, BoundingRegion +from .dataviews import Stack, Histogram, DataStack, find_minmax +from .ndmapping import NdMapping, Dimension +from .options import options +from .sheetcoords import SheetCoordinateSystem, Slice +from .views import View, Overlay, Annotation, GridLayout class SheetLayer(View): @@ -429,13 +429,13 @@ def grid_sample(self, rows, cols, lbrt=None, **kwargs): l, b, r, t = lbrt x, y = np.meshgrid(np.linspace(l, r, cols), np.linspace(b, t, rows)) - coords = zip(x.flat, y.flat) + coords = list(zip(x.flat, y.flat)) shape = (rows, cols) bounds = BoundingBox(points=[(l, b), (r, t)]) grid = self.sample(coords, **kwargs) - return DataGrid(bounds, shape, initial_items=zip(coords, grid.values())) + return DataGrid(bounds, shape, initial_items=list(zip(coords, grid.values()))) def map(self, map_fn, **kwargs): @@ -607,7 +607,7 @@ def last(self): directly or use the items() method. """ - last_items = [(k, v.clone(items=(v.keys()[-1], v.last))) + last_items = [(k, v.clone(items=(list(v.keys())[-1], v.last))) for (k, v) in self.items()] return self.clone(last_items) @@ -644,7 +644,7 @@ def map(self, map_fn, **kwargs): @property def xlim(self): - xlim = self.values()[-1].xlim + xlim = list(self.values())[-1].xlim for data in self.values(): xlim = find_minmax(xlim, data.xlim) return xlim @@ -652,7 +652,7 @@ def xlim(self): @property def ylim(self): - ylim = self.values()[-1].ylim + ylim = list(self.values())[-1].ylim for data in self.values(): ylim = find_minmax(ylim, data.ylim) if ylim[0] == ylim[1]: ylim = (ylim[0], ylim[0]+1.) diff --git a/dataviews/tests/testboundingregion.py b/dataviews/tests/testboundingregion.py index b931d28f9d..208cac600c 100644 --- a/dataviews/tests/testboundingregion.py +++ b/dataviews/tests/testboundingregion.py @@ -4,7 +4,7 @@ import unittest -import utils # pyflakes:ignore (set sys.path) +from . import utils # pyflakes:ignore (set sys.path) from dataviews.boundingregion import BoundingBox, BoundingCircle, BoundingEllipse, AARectangle # Currently duplicating tests in topographica diff --git a/dataviews/tests/testcomparisons.py b/dataviews/tests/testcomparisons.py index d5d217d100..3b5c03ac68 100644 --- a/dataviews/tests/testcomparisons.py +++ b/dataviews/tests/testcomparisons.py @@ -4,7 +4,7 @@ import sys from nose.plugins.skip import SkipTest -from utils import ViewTestCase +from .utils import ViewTestCase from dataviews import SheetView, SheetStack from dataviews.boundingregion import BoundingBox from dataviews.ndmapping import Dimension diff --git a/dataviews/tests/testndmapping.py b/dataviews/tests/testndmapping.py index 66a372a559..05ca56fd9f 100644 --- a/dataviews/tests/testndmapping.py +++ b/dataviews/tests/testndmapping.py @@ -1,6 +1,6 @@ import unittest -import utils # pyflakes:ignore (set sys.path) +from . import utils # pyflakes:ignore (set sys.path) from dataviews.ndmapping import Dimension, NdIndexableMapping try: from collections import OrderedDict @@ -63,7 +63,7 @@ def test_idxmapping_dimension_labels(self): def test_idxmapping_dim_dict(self): idxmap = NdIndexableMapping(dimensions=[self.dim1, self.dim2]) - dim_labels, dim_objs = zip(*idxmap.dim_dict.items()) + dim_labels, dim_objs = list(zip(*list(idxmap.dim_dict.items()))) self.assertEqual(dim_labels, tuple(self.dimension_labels)) self.assertEqual(dim_objs, (self.dim1, self.dim2)) @@ -101,10 +101,10 @@ def test_idxmapping_nested_update(self): ndmap_list = [(0.5, ndmap1), (1.5, ndmap2)] nested_ndmap = NdIndexableMapping(ndmap_list, dimensions=[self.dim2]) nested_ndmap[(0.5,)].update(dict([(0, 'c'), (1, 'd')])) - self.assertEquals(nested_ndmap[0.5].values(), ['c', 'd']) + self.assertEquals(list(nested_ndmap[0.5].values()), ['c', 'd']) nested_ndmap[1.5] = ndmap3 - self.assertEquals(nested_ndmap[1.5].values(), ['e', 'f']) + self.assertEquals(list(nested_ndmap[1.5].values()), ['e', 'f']) def test_idxmapping_reindex(self): data = [((0, 0.5), 'a'), ((1, 0.5), 'b')] @@ -119,7 +119,7 @@ def test_idxmapping_adddimension(self): ndmap = NdIndexableMapping(self.init_items_1D_list, dimensions=[self.dim1]) ndmap2d = ndmap.add_dimension(self.dim2, 0, 0.5) - self.assertEqual(ndmap2d.keys(), [(0.5, 1), (0.5, 5)]) + self.assertEqual(list(ndmap2d.keys()), [(0.5, 1), (0.5, 5)]) self.assertEqual(ndmap2d.dimensions, [self.dim2, self.dim1]) def test_idxmapping_clone(self): @@ -132,7 +132,7 @@ def test_idxmapping_apply_key_type(self): data = dict([(0.5, 'a'), (1.5, 'b')]) ndmap = NdIndexableMapping(data, dimensions=[self.dim1]) - self.assertEqual(ndmap.keys(), [0, 1]) + self.assertEqual(list(ndmap.keys()), [0, 1]) if __name__ == "__main__": diff --git a/dataviews/tests/testsheetviews.py b/dataviews/tests/testsheetviews.py index 9e7f0784ed..57057196a8 100644 --- a/dataviews/tests/testsheetviews.py +++ b/dataviews/tests/testsheetviews.py @@ -1,7 +1,7 @@ import unittest import numpy as np -import utils # pyflakes:ignore (set sys.path) +from . import utils # pyflakes:ignore (set sys.path) from dataviews.boundingregion import BoundingBox from dataviews import SheetView diff --git a/dataviews/tests/testviews.py b/dataviews/tests/testviews.py index dc42f87d09..f691ba9042 100644 --- a/dataviews/tests/testviews.py +++ b/dataviews/tests/testviews.py @@ -1,6 +1,6 @@ import unittest -import utils # pyflakes:ignore (set sys.path) +from . import utils # pyflakes:ignore (set sys.path) from dataviews.boundingregion import BoundingBox from dataviews.views import View, Annotation, Layout, Overlay, GridLayout diff --git a/dataviews/views.py b/dataviews/views.py index 46686f456f..5c7b1d03f4 100644 --- a/dataviews/views.py +++ b/dataviews/views.py @@ -8,9 +8,9 @@ import math import param -from ndmapping import NdMapping, Dimension, AttrDict, map_type -from options import options -from boundingregion import BoundingBox +from .ndmapping import NdMapping, Dimension, AttrDict, map_type +from .options import options +from .boundingregion import BoundingBox class View(param.Parameterized): @@ -358,8 +358,8 @@ def style(self, styles): def __lshift__(self, other): - subviews = other.data.values() if isinstance(other, Layout) else [other] - return Layout(self.data.values() + subviews) + subviews = list(other.data.values()) if isinstance(other, Layout) else [other] + return Layout(list(self.data.values()) + subviews) @property @@ -389,7 +389,7 @@ def __iter__(self): def __add__(self, other): if isinstance(other, GridLayout): - elements = [self] + other.values() + elements = [self] + list(other.values()) else: elements = [self, other] return GridLayout(elements) @@ -411,7 +411,7 @@ def __init__(self, initial_items=[], **kwargs): @property def shape(self): - rows, cols = zip(*self.keys()) + rows, cols = list(zip(*list(self.keys()))) return max(rows)+1, max(cols)+1 @@ -421,8 +421,8 @@ def coords(self): Compute the list of (row,column,view) elements from the current set of items (i.e. tuples of form ((row, column), view)) """ - if self.keys() == []: return [] - return [(r, c, v) for ((r, c), v) in zip(self.keys(), self.values())] + if list(self.keys()) == []: return [] + return [(r, c, v) for ((r, c), v) in zip(list(self.keys()), list(self.values()))] @property @@ -458,7 +458,7 @@ def reorder(self, other, cols=None): Given a mapping or iterable of additional views, extend the grid in scanline order, obeying max_cols (if applicable). """ - values = other if isinstance(other, list) else other.values() + values = other if isinstance(other, list) else list(other.values()) grid = [[]] if self.coords == [] else self._grid(self.coords) new_grid = grid[:-1] + ([grid[-1]+ values]) cols = self.max_cols if cols is None else cols @@ -524,8 +524,8 @@ def _reshape_grid(self, grid, cols): def __add__(self, other): - new_values = other.values() if isinstance(other, GridLayout) else [other] - return self.clone(self.values()+new_values) + new_values = list(other.values()) if isinstance(other, GridLayout) else [other] + return self.clone(list(self.values())+new_values) @property From 3f151aaee8f6f3139ebd3c786e27fda8f7673c7e Mon Sep 17 00:00:00 2001 From: jlstevens Date: Sun, 11 May 2014 18:23:35 +0100 Subject: [PATCH 02/13] Updated travis configuration to test with Python 3 --- .travis.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 41a5e56135..c2b73eb2d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,14 @@ language: python python: - "2.6" - "2.7" - -before_install: - - time pip install ipython==1.0 jinja2 tornado pyzmq + - "3.3" + - "3.4" # no dependencies install: true -script: nosetests \ No newline at end of file +install: + - if [[ $TRAVIS_PYTHON_VERSION == 2* ]]; then pip install ipython==1.0 jinja2 tornado pyzmq; fi + - if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then pip install ipython jinja2 tornado pyzmq; fi + +script: nosetests From 451b3cda0adb9b51b13fedf209cc6afb38164fef Mon Sep 17 00:00:00 2001 From: jlstevens Date: Sun, 11 May 2014 18:29:34 +0100 Subject: [PATCH 03/13] Updated testcomparison tests to be Python3 compatible --- dataviews/tests/testcomparisons.py | 24 ++++++++++++------------ dataviews/tests/utils.py | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dataviews/tests/testcomparisons.py b/dataviews/tests/testcomparisons.py index 3b5c03ac68..0b0b1e3873 100644 --- a/dataviews/tests/testcomparisons.py +++ b/dataviews/tests/testcomparisons.py @@ -107,13 +107,13 @@ def test_unequal_arrays(self): self.assertEqual(self.sv1, self.sv2) raise AssertionError("Array mismatch not detected") except AssertionError as e: - assert e.message.startswith('SheetView: \nArrays are not almost equal to 6 decimals') + assert str(e).startswith('SheetView: \nArrays are not almost equal to 6 decimals') def test_bounds_mismatch(self): try: self.assertEqual(self.sv1, self.sv4) except AssertionError as e: - assert e.message.startswith('BoundingBoxes are mismatched.') + assert str(e).startswith('BoundingBoxes are mismatched.') @@ -123,19 +123,19 @@ def test_depth_mismatch(self): try: self.assertEqual(self.overlay1_depth2, self.overlay4_depth3) except AssertionError as e: - assert e.message.startswith("SheetOverlays have different lengths.") + assert str(e).startswith("SheetOverlays have different lengths.") def test_element_mismatch(self): try: self.assertEqual(self.overlay1_depth2, self.overlay2_depth2) except AssertionError as e: - assert e.message.startswith('SheetView: \nArrays are not almost equal to 6 decimals') + assert str(e).startswith('SheetView: \nArrays are not almost equal to 6 decimals') def test_bounds_mismatch(self): try: self.assertEqual(self.overlay1_depth2, self.overlay3_depth2) except AssertionError as e: - assert e.message.startswith('BoundingBoxes are mismatched.') + assert str(e).startswith('BoundingBoxes are mismatched.') @@ -146,14 +146,14 @@ def test_dimension_mismatch(self): self.assertEqual(self.stack1_1D, self.stack1_2D) raise AssertionError("Mismatch in dimension number not detected.") except AssertionError as e: - assert e.message.startswith("Stacks have different numbers of dimensions.") + assert str(e).startswith("Stacks have different numbers of dimensions.") def test_dimension_label_mismatch(self): try: self.assertEqual(self.stack1_1D, self.stack6_1D) raise AssertionError("Mismatch in dimension labels not detected.") except AssertionError as e: - assert e.message.startswith("Stacks have different dimension labels.") + assert str(e).startswith("Stacks have different dimension labels.") def test_key_len_mismatch(self): @@ -161,28 +161,28 @@ def test_key_len_mismatch(self): self.assertEqual(self.stack1_1D, self.stack3_1D) raise AssertionError("Mismatch in stack key number not detected.") except AssertionError as e: - assert e.message.startswith("Stacks have different numbers of keys.") + assert str(e).startswith("Stacks have different numbers of keys.") def test_key_mismatch(self): try: self.assertEqual(self.stack1_1D, self.stack2_1D) raise AssertionError("Mismatch in stack keys not detected.") except AssertionError as e: - assert e.message.startswith("Stacks have different sets of keys.") + assert str(e).startswith("Stacks have different sets of keys.") def test_bounds_mismatch(self): try: self.assertEqual(self.stack1_1D, self.stack5_1D) raise AssertionError("Mismatch in element bounding boxes.") except AssertionError as e: - assert e.message.startswith("BoundingBoxes are mismatched.") + assert str(e).startswith("BoundingBoxes are mismatched.") def test_element_mismatch(self): try: self.assertEqual(self.stack1_1D, self.stack4_1D) raise AssertionError("Element mismatch in array data not detected.") except AssertionError as e: - assert e.message.startswith('SheetView: \nArrays are not almost equal to 6 decimals') + assert str(e).startswith('SheetView: \nArrays are not almost equal to 6 decimals') def test_overlay_mismatch(self): @@ -190,7 +190,7 @@ def test_overlay_mismatch(self): self.assertEqual(self.stack7_1D, self.stack8_1D) raise AssertionError("Overlay element mismatch in array data not detected.") except AssertionError as e: - assert e.message.startswith('SheetView: \nArrays are not almost equal to 6 decimals') + assert str(e).startswith('SheetView: \nArrays are not almost equal to 6 decimals') if __name__ == "__main__": diff --git a/dataviews/tests/utils.py b/dataviews/tests/utils.py index 3da73d2379..878707b57a 100644 --- a/dataviews/tests/utils.py +++ b/dataviews/tests/utils.py @@ -53,7 +53,7 @@ def compare_arrays(self, arr1, arr2, name): try: assert_array_almost_equal(arr1, arr2) except AssertionError as e: - raise self.failureException(name+': '+e.message) + raise self.failureException(name+': '+str(e)) def bounds_check(self, view1, view2): From 58653b4206d8aab84e771747d9f6a85fb6136e55 Mon Sep 17 00:00:00 2001 From: jlstevens Date: Sun, 11 May 2014 18:47:06 +0100 Subject: [PATCH 04/13] Fixed bug in __lshift__ operator of Layout --- dataviews/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dataviews/views.py b/dataviews/views.py index 5c7b1d03f4..2be2fb8a8c 100644 --- a/dataviews/views.py +++ b/dataviews/views.py @@ -358,8 +358,10 @@ def style(self, styles): def __lshift__(self, other): - subviews = list(other.data.values()) if isinstance(other, Layout) else [other] - return Layout(list(self.data.values()) + subviews) + if isinstance(other, Layout): + raise Exception("Cannot adjoin two Layout objects.") + views = [self.data.get(k, None) for k in self.layout_order] + return Layout([v for v in views if v is not None] + [other]) @property From 716f8e7172601a70a4326c5d85146b4779c99238 Mon Sep 17 00:00:00 2001 From: philippjfr Date: Sun, 11 May 2014 19:24:22 +0100 Subject: [PATCH 05/13] Fixed syntax error in plots.py --- dataviews/plots.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dataviews/plots.py b/dataviews/plots.py index b4921579f3..3cbe773a8c 100644 --- a/dataviews/plots.py +++ b/dataviews/plots.py @@ -962,8 +962,7 @@ def __init__(self, grid, **kwargs): def __call__(self, axis=None): - grid_shape = [[v for (k,v) in col[1]] for col in groupby(self.grid.items(), - lambda (k,v): k[0])] + grid_shape = [[v for (k,v) in col[1]] for col in groupby(self.grid.items(), lambda k,v: k[0])] width, height, b_w, b_h = self._compute_borders(grid_shape) ax = self._axis(axis, lbrt=(0, 0, width, height)) From 0f21f7b5aaaf2c944b710370f61e1ad67c7fd3d0 Mon Sep 17 00:00:00 2001 From: philippjfr Date: Sun, 11 May 2014 19:25:38 +0100 Subject: [PATCH 06/13] Fixed display hook return value when traceback occurs --- dataviews/ipython.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dataviews/ipython.py b/dataviews/ipython.py index 0c30abfb69..e22ed784af 100644 --- a/dataviews/ipython.py +++ b/dataviews/ipython.py @@ -723,11 +723,10 @@ def display_hook(fn): @wraps(fn) def wrapped(view, **kwargs): try: - retval = fn(view, **kwargs) + return fn(view, **kwargs) except: if ENABLE_TRACEBACKS: traceback.print_exc() - return retval return wrapped From 4440bb964eb14603249d2f3a9c067755c08e5c03 Mon Sep 17 00:00:00 2001 From: philippjfr Date: Sun, 11 May 2014 19:26:03 +0100 Subject: [PATCH 07/13] Fixed base64 encoding to be Python 2 and 3 compatible --- dataviews/ipython.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dataviews/ipython.py b/dataviews/ipython.py index e22ed784af..fe321b398e 100644 --- a/dataviews/ipython.py +++ b/dataviews/ipython.py @@ -14,6 +14,7 @@ from tempfile import NamedTemporaryFile from functools import wraps import traceback, itertools, string +import base64 from .dataviews import Stack from .plots import Plot, GridLayoutPlot, viewmap, channel_modes @@ -654,7 +655,7 @@ def animate(anim, writer, mime_type, anim_kwargs, extra_args, tag): with NamedTemporaryFile(suffix='.%s' % mime_type) as f: anim.save(f.name, writer=writer, **anim_kwargs) video = open(f.name, "rb").read() - anim._encoded_video = video.encode("base64") + anim._encoded_video = base64.b64encode(video).decode("utf-8") return tag.format(b64=anim._encoded_video, mime_type=mime_type) @@ -691,7 +692,7 @@ def figure_display(fig, size=None, message=None): mime_type = 'svg+xml' if FIGURE_FORMAT.lower()=='svg' else 'png' prefix = 'data:image/%s;base64,' % mime_type - b64 = prefix + print_figure(fig, FIGURE_FORMAT).encode("base64") + b64 = prefix + base64.b64encode(print_figure(fig, FIGURE_FORMAT)).decode("utf-8") if size is not None: html = "
" % (size, size, b64) else: From 560997c7e17df775d1a4d7176c8adf77c828e3a7 Mon Sep 17 00:00:00 2001 From: philippjfr Date: Mon, 12 May 2014 13:29:47 +0100 Subject: [PATCH 08/13] Curve objects now support iterable input --- dataviews/dataviews.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dataviews/dataviews.py b/dataviews/dataviews.py index 172b36d71f..9e51f9b3bc 100644 --- a/dataviews/dataviews.py +++ b/dataviews/dataviews.py @@ -60,7 +60,8 @@ class Curve(DataLayer): def __init__(self, data, **kwargs): - super(Curve, self).__init__(np.array(data), **kwargs) + data = data if isinstance(data, np.ndarray) else np.array(list(data)) + super(Curve, self).__init__(data, **kwargs) @property From eae8559f482fa48f5482c1c17c4c6f1bf9192749 Mon Sep 17 00:00:00 2001 From: philippjfr Date: Mon, 12 May 2014 13:30:51 +0100 Subject: [PATCH 09/13] Python 3 fix casting zip to list --- dataviews/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dataviews/options.py b/dataviews/options.py index aef49c6c0e..a3c84abcf1 100644 --- a/dataviews/options.py +++ b/dataviews/options.py @@ -264,7 +264,7 @@ def fuzzy_match_keys(self, name): reversed_matches = sorted((len(key), key) for key in self.keys() if name.endswith(key))[::-1] if reversed_matches: - return zip(*reversed_matches)[1] + return list(zip(*reversed_matches))[1] else: return [] From aec6d11e5cf77f3309db462a72793ebb13736fda Mon Sep 17 00:00:00 2001 From: philippjfr Date: Mon, 12 May 2014 13:33:11 +0100 Subject: [PATCH 10/13] Fixed hist method on SheetView to avoid sorting heterogeneous types --- dataviews/sheetviews.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dataviews/sheetviews.py b/dataviews/sheetviews.py index 02306d5b4f..70b2d6046d 100644 --- a/dataviews/sheetviews.py +++ b/dataviews/sheetviews.py @@ -207,7 +207,7 @@ def hist(self, num_bins=20, bin_range=None, individually=True, style_prefix=None can additionally be specified to set a common cmap when viewing a Stack or Overlay. """ - range = find_minmax(self.range, (0, None)) if bin_range is None else bin_range + range = find_minmax(self.range, (0, -float('inf'))) if bin_range is None else bin_range # Avoids range issues including zero bin range and empty bins if range == (0, 0): From cb721c3f2e22d31a006ca737e66153822aeda209 Mon Sep 17 00:00:00 2001 From: philippjfr Date: Mon, 12 May 2014 13:34:54 +0100 Subject: [PATCH 11/13] IPython Extension now loads param extension with verbose = False --- dataviews/ipython.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dataviews/ipython.py b/dataviews/ipython.py index fe321b398e..83cacdb7fe 100644 --- a/dataviews/ipython.py +++ b/dataviews/ipython.py @@ -835,7 +835,7 @@ def load_ipython_extension(ip, verbose=True): if not _loaded: _loaded = True - param_ext.load_ipython_extension(ip) + param_ext.load_ipython_extension(ip, verbose=False) ip.register_magics(ViewMagic) ip.register_magics(OptsMagic) From 042a025b1082fd0e596ea42a4b8a0e1cfe17f5a3 Mon Sep 17 00:00:00 2001 From: philippjfr Date: Mon, 12 May 2014 13:35:39 +0100 Subject: [PATCH 12/13] Simplified figure fallback and added Python 3 animation warning --- dataviews/ipython.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/dataviews/ipython.py b/dataviews/ipython.py index 83cacdb7fe..dec9468fff 100644 --- a/dataviews/ipython.py +++ b/dataviews/ipython.py @@ -1,3 +1,4 @@ +import matplotlib as mpl import matplotlib.pyplot as plt try: from matplotlib import animation except: animation = None @@ -15,6 +16,7 @@ from functools import wraps import traceback, itertools, string import base64 +import sys from .dataviews import Stack from .plots import Plot, GridLayoutPlot, viewmap, channel_modes @@ -668,7 +670,10 @@ def HTML_video(plot): try: return animate(anim, *ANIMATION_OPTS[fmt]) except: pass - return "Could not generate %s animation" % VIDEO_FORMAT + msg = "Could not generate %s animation" % VIDEO_FORMAT + if sys.version_info[0] == 3 and mpl.__version__[:-2] in ['1.2', '1.3']: + msg = "Python 3 Matplotlib animation support broken <= 1.3" + raise Exception(msg) def first_frame(plot): @@ -701,11 +706,6 @@ def figure_display(fig, size=None, message=None): return html if (message is None) else '%s
%s' % (message, html) -def figure_fallback(plotobj): - message = ('Cannot import matplotlib.animation' if animation is None - else 'Failed to generate matplotlib animation') - fig = plotobj() - return figure_display(fig, message=message) #===============# @@ -730,6 +730,11 @@ def wrapped(view, **kwargs): traceback.print_exc() return wrapped +def render(plot): + try: + return render_anim(plot) + except Exception as e: + return str(e)+'
'+figure_display(plot()) @display_hook def animation_display(anim): @@ -748,9 +753,7 @@ def stack_display(stack, size=256): fig = stackplot() return figure_display(fig) - try: return render_anim(stackplot) - except: return figure_fallback(stackplot) - + return render(stackplot) @display_hook def layout_display(grid, size=256): @@ -767,8 +770,7 @@ def layout_display(grid, size=256): fig = gridplot() return figure_display(fig) - try: return render_anim(gridplot) - except: return figure_fallback(gridplot) + return render(gridplot) @display_hook def projection_display(grid, size=256): @@ -784,8 +786,7 @@ def projection_display(grid, size=256): fig = gridplot() return figure_display(fig) - try: return render_anim(gridplot) - except: return figure_fallback(gridplot) + return render(gridplot) @display_hook def view_display(view, size=256): From 74ec6692fe5fba5ade04d874704fc73771f3be88 Mon Sep 17 00:00:00 2001 From: philippjfr Date: Mon, 12 May 2014 13:37:43 +0100 Subject: [PATCH 13/13] Fixed plotting aspect ratio if lbrt is None --- dataviews/plots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dataviews/plots.py b/dataviews/plots.py index 3cbe773a8c..745d77a2ec 100644 --- a/dataviews/plots.py +++ b/dataviews/plots.py @@ -162,11 +162,11 @@ def _axis(self, axis, title=None, xlabel=None, ylabel=None, if b == t: t += 1. # Arbitrary y-extent if zero range axis.set_ylim((b, t)) - if self.aspect == 'square': + if self.aspect == 'square' and lbrt: xrange = lbrt[2] - lbrt[0] yrange = lbrt[3] - lbrt[1] axis.set_aspect(xrange/yrange) - elif self.aspect is not None: + elif self.aspect not in [None, 'square']: axis.set_aspect(self.aspect) if xticks: