Skip to content

Commit 24fd41a

Browse files
author
Zerline
committed
A bunch of new tests, and a bugfix in new object validation.
1 parent 1ed4ca2 commit 24fd41a

File tree

1 file changed

+175
-15
lines changed

1 file changed

+175
-15
lines changed

sage_combinat_widgets/grid_view_editor.py

Lines changed: 175 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,31 @@ def to_cell(self, val):
180180
"""
181181
return val
182182

183-
def validate(self, obj, value=None, obj_class=None):
183+
def validate(self, new_obj, obj_class=None):
184184
r"""
185185
Validate object type.
186+
187+
TESTS ::
188+
189+
sage: from sage_combinat_widgets import GridViewEditor
190+
sage: obj = Tableau([[1, 2, 5, 6], [3], [4]])
191+
sage: e = GridViewEditor(obj)
192+
sage: e.donottrack = False # This class is not meant to work by itself without a widget.
193+
sage: new_valid_obj = Tableau([[1, 2, 3, 6], [4], [5]])
194+
sage: e.validate(new_valid_obj, obj.__class__)
195+
True
196+
sage: new_invalid_obj = Partition([3,3,1])
197+
sage: issubclass(new_invalid_obj.__class__, obj.__class__)
198+
False
199+
sage: e.validate(new_invalid_obj, obj.__class__)
200+
False
201+
sage: new_invalid_obj = 42
202+
sage: e.validate(new_invalid_obj)
203+
False
186204
"""
187205
if obj_class:
188-
return issubclass(obj.__class__, obj_class)
189-
return issubclass(obj.__class__, SageObject)
206+
return issubclass(new_obj.__class__, obj_class)
207+
return issubclass(new_obj.__class__, SageObject) and hasattr(new_obj, 'cells')
190208

191209
def modified_add_traits(self, **traits):
192210
r"""
@@ -305,33 +323,83 @@ def get_value(self):
305323
sage: from sage.combinat.tableau import Tableau
306324
sage: from sage_combinat_widgets import GridViewEditor
307325
sage: e = GridViewEditor(Tableau([[1, 2, 5, 6], [3], [4]]))
308-
sage: e.value
326+
sage: e.get_value()
309327
[[1, 2, 5, 6], [3], [4]]
310-
sage: type(GridViewEditor.value)
311-
<class 'traitlets.traitlets.Any'>
312-
sage: e.set_value(Tableau([[1, 2], [3], [4]]))
313-
sage: e.value
314-
[[1, 2], [3], [4]]
315328
"""
316329
return self.value
317330

318331
def set_value(self, obj):
319332
r"""
320333
Check compatibility, then set editor value.
334+
335+
TESTS ::
336+
337+
sage: from sage_combinat_widgets import GridViewEditor
338+
sage: t = Tableau([[1, 2, 5, 6], [3], [4]])
339+
sage: e = GridViewEditor(t)
340+
sage: e.donottrack = False # This class is not meant to work by itself without a widget.
341+
sage: new_valid_obj = Tableau([[1, 2, 7, 6], [3], [4]])
342+
sage: e.set_value(new_valid_obj)
343+
sage: e.value
344+
[[1, 2, 7, 6], [3], [4]]
345+
sage: new_invalid_obj = 42
346+
sage: e.set_value(new_invalid_obj)
347+
Traceback (most recent call last):
348+
...
349+
ValueError: Object 42 is not compatible.
321350
"""
322351
if not self.validate(obj, self.value.__class__):
323352
raise ValueError("Object %s is not compatible." % str(obj))
324353
self.value = obj
325354

326355
def push_history(self, obj):
327356
r"""
357+
Push an object to editor history.
358+
Ensure that history does not become too long.
359+
360+
INPUT:
361+
362+
- ``obj`` -- an object (the old one)
363+
364+
TESTS::
365+
366+
sage: from sage_combinat_widgets import GridViewEditor
367+
sage: t = Tableau([[1, 2, 5, 6], [3], [4]])
368+
sage: e = GridViewEditor(t)
369+
sage: e._history
370+
[]
371+
sage: e.push_history(t)
372+
sage: e._history
373+
[[[1, 2, 5, 6], [3], [4]]]
374+
328375
"""
329376
self._history.append(obj)
330377
if len(self._history) > MAX_LEN_HISTORY:
331378
self._history = self._history[1:]
332379

333380
@traitlets.observe('value')
334381
def value_changed(self, change):
382+
r"""
383+
What to do when the value has been changed.
384+
385+
INPUT:
386+
387+
- ``change`` -- a change Bunch
388+
389+
TESTS ::
390+
391+
sage: from sage_combinat_widgets import GridViewEditor
392+
sage: t = Tableau([[1, 2, 5, 6], [3], [4]])
393+
sage: new_t = Tableau([[1, 2, 7, 6], [3], [4]])
394+
sage: e = GridViewEditor(t)
395+
sage: e.donottrack = False # This class is not meant to work by itself without a widget.
396+
sage: e._history
397+
[]
398+
sage: from traitlets import Bunch
399+
sage: e.value_changed(Bunch({'name': 'value', 'old': t, 'new': new_t, 'owner': e, 'type': 'change'}))
400+
sage: e._history
401+
[[[1, 2, 5, 6], [3], [4]]]
402+
"""
335403
self.reset_dirty()
336404
if self.donottrack:
337405
return
@@ -389,35 +457,125 @@ def set_value_from_cells(self, obj_class=None, cells={}):
389457
print("These cells cannot be turned into a %s" % cl)
390458
else:
391459
raise TypeError("Unable to cast the given cells into a grid-like object.")
392-
if not self.validate(obj, None, obj_class):
460+
if not self.validate(obj, obj_class):
393461
raise ValueError("Could not make a compatible ('%s') object from given cells" % str(obj_class))
394462
self.donottrack = True
395463
self.set_value(obj)
396464
self.donottrack = False
397465

398-
def set_dirty(self, pos, val, e=None):
466+
def set_dirty(self, pos, val, err=None):
467+
r"""
468+
Set a cell 'dirty'.
469+
470+
INPUT:
471+
472+
- ``pos`` -- a tuple
473+
- ``val`` -- a(n incorrect) value for `pos`
474+
- ``err`` -- an exception
475+
476+
TESTS ::
477+
478+
sage: from sage_combinat_widgets import GridViewEditor
479+
sage: t = StandardTableau([[1, 2, 5, 6], [3], [4]])
480+
sage: e = GridViewEditor(t)
481+
sage: e.donottrack = False # This class is not meant to work by itself without a widget.
482+
sage: from traitlets import Bunch
483+
sage: err = e.set_cell(Bunch({'name': 'cell_0_2', 'old': 5, 'new': 7, 'owner': e, 'type': 'change'}))
484+
sage: e.set_dirty((0,2), 7, err)
485+
sage: e.dirty
486+
{(0, 2): 7}
487+
sage: e.dirty_errors[(0,2)]
488+
ValueError('the entries in each row of a semistandard tableau must be weakly increasing',)
489+
"""
399490
self.dirty[pos] = val
400-
if e:
401-
self.dirty_errors[pos] = e
491+
if err:
492+
self.dirty_errors[pos] = err
402493

403494
def unset_dirty(self, pos):
495+
r"""
496+
Set a cell no more 'dirty'.
497+
498+
INPUT:
499+
500+
- ``pos`` -- a tuple
501+
502+
TESTS ::
503+
504+
sage: from sage_combinat_widgets import GridViewEditor
505+
sage: t = StandardTableau([[1, 2, 5, 6], [3], [4]])
506+
sage: e = GridViewEditor(t)
507+
sage: e.donottrack = False # This class is not meant to work by itself without a widget.
508+
sage: from traitlets import Bunch
509+
sage: err = e.set_cell(Bunch({'name': 'cell_0_2', 'old': 5, 'new': 7, 'owner': e, 'type': 'change'}))
510+
sage: e.set_dirty((0,2), 7, err)
511+
sage: err = e.set_cell(Bunch({'name': 'cell_2_0', 'old': 4, 'new': 9, 'owner': e, 'type': 'change'}))
512+
sage: e.set_dirty((2,0), 9, err)
513+
sage: e.dirty
514+
{(0, 2): 7, (2, 0): 9}
515+
sage: e.unset_dirty((0,2))
516+
sage: e.dirty
517+
{(2, 0): 9}
518+
"""
404519
del self.dirty[pos]
405520
del self.dirty_errors[pos]
406521

407522
def reset_dirty(self):
523+
r"""
524+
Reset all previously 'dirty' cells.
525+
526+
TESTS ::
527+
528+
sage: from sage_combinat_widgets import GridViewEditor
529+
sage: t = StandardTableau([[1, 2, 5, 6], [3], [4]])
530+
sage: e = GridViewEditor(t)
531+
sage: e.donottrack = False # This class is not meant to work by itself without a widget.
532+
sage: from traitlets import Bunch
533+
sage: err = e.set_cell(Bunch({'name': 'cell_0_2', 'old': 5, 'new': 7, 'owner': e, 'type': 'change'}))
534+
sage: e.set_dirty((0,2), 7, err)
535+
sage: err = e.set_cell(Bunch({'name': 'cell_2_0', 'old': 4, 'new': 9, 'owner': e, 'type': 'change'}))
536+
sage: e.set_dirty((2,0), 9, err)
537+
sage: e.dirty
538+
{(0, 2): 7, (2, 0): 9}
539+
sage: e.reset_dirty()
540+
sage: e.dirty
541+
{}
542+
"""
408543
if not self.dirty: # Prevent any interactive loops
409544
return
410-
for pos in self.dirty.keys():
411-
self.unset_dirty(pos)
545+
self.dirty = {}
546+
self.dirty_errors = {}
412547

413548
def dirty_info(self, pos):
549+
r"""
550+
Get error details from a 'dirty' cell.
551+
552+
INPUT:
553+
554+
- ``pos`` -- a tuple
555+
556+
TESTS ::
557+
558+
sage: from sage_combinat_widgets import GridViewEditor
559+
sage: t = StandardTableau([[1, 2, 5, 6], [3], [4]])
560+
sage: e = GridViewEditor(t)
561+
sage: e.donottrack = False # This class is not meant to work by itself without a widget.
562+
sage: from traitlets import Bunch
563+
sage: err = e.set_cell(Bunch({'name': 'cell_0_2', 'old': 5, 'new': 7, 'owner': e, 'type': 'change'}))
564+
sage: e.set_dirty((0,2), 7, err)
565+
sage: err = e.set_cell(Bunch({'name': 'cell_2_0', 'old': 4, 'new': 9, 'owner': e, 'type': 'change'}))
566+
sage: e.set_dirty((2,0), 9, err)
567+
sage: e.dirty_info((0, 2))
568+
'the entries in each row of a semistandard tableau must be weakly increasing'
569+
"""
414570
if pos in self.dirty_errors:
415571
return str(self.dirty_errors[pos])
416572
return ''
417573

418574
@traitlets.observe(traitlets.All)
419575
def set_cell(self, change):
420576
r"""
577+
What to do when a cell value has been changed.
578+
421579
TESTS ::
422580
423581
sage: from sage_combinat_widgets import GridViewEditor
@@ -533,6 +691,8 @@ def add_cell(self, change):
533691
@traitlets.observe(traitlets.All)
534692
def remove_cell(self, change):
535693
r"""
694+
What to do when a cell has been removed.
695+
536696
TESTS ::
537697
538698
sage: from sage_combinat_widgets import GridViewEditor

0 commit comments

Comments
 (0)