Changeset 746

Show
Ignore:
Timestamp:
08/24/05 05:46:31 (3 years ago)
Author:
antont
Message:

tests for multiple logs and log cell removal. some fixes and conveniences.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • nbdoc/trunk/notabene/notebook.py

    r745 r746  
    5757        #to be replaced by newinit 
    5858        self.element = element 
    59         self.number = int(element.attrib['number']) 
    6059 
    6160    @classmethod 
     
    6463        #(if/)when nbshell does not suppose the current anymore, 
    6564        #note: parent is a Log object, which has the 'root' as .element 
    66         element = ET.SubElement(parent.element, 'cell', number=str(number)) 
    67         return cls(element) 
     65        element = ET.SubElement(parent.element, 'cell') 
     66        instance = cls(element) 
     67        instance.number = number #sets the xml too via the property 
     68        return instance 
    6869 
    6970    input  = property(SubelemGetter('input'),  SubelemSetter('input')) 
     
    7475    def get_number(self): 
    7576        return int(self.element.attrib['number']) 
    76      
    7777    def set_number(self, num): 
    7878        s = str(num) 
    7979        assert s.isdigit() 
    8080        self.element.attrib['number'] = s 
    81      
     81        #note: this does not update the cell index in the log! 
     82        #so probably currently is safe only in the constructor, which is 
     83        #given the index number by the Log.add <- Notebook.add_cell methods 
    8284    number = property(get_number, set_number) 
    8385 
     
    100102        self.id = logid 
    101103        self.element = ET.SubElement(parent.root, 'ipython-log', id=logid) 
    102         self.cells = [] 
     104        self._cells = [] #it is not safe to manipulate this from outside 
    103105 
    104106    def add(self, number): 
    105         if number == len(self.cells): #is to be put at the end 
     107        if number == len(self._cells): #is to be put at the end 
    106108            #cell = Cell(self, number) #use this when nbshell is refactored 
    107109            cell = Cell.newinit(self, number) #temporary for transition 
    108             self.cells.append(cell) #always adds to end 
    109             self.element.append(cell.element) #XXX already became a subelement! 
    110             #should cells be a property that wraps the list and xml? 
    111             #dbg 
    112             print "NOTEBOOK Log: added cell number", number, "with number", cell.number 
     110            self._cells.append(cell) #always adds to end 
     111            #self.element.append(cell.element) #already became a subelement 
     112            #seems that that append call would have no effect. 
    113113            return cell 
    114114                 
    115115        else: 
    116116            try: 
    117                 self.cells[number] 
    118                 raise ValueError, 'a cell with number %d exists. note: multiple logs not implemented now.' % number 
     117                self._cells[number] 
     118                raise ValueError, 'a cell with number %d exists in log id %s' % (number, self.id) 
    119119            except IndexError: 
    120                 if number > len(self.cells): 
     120                if number > len(self._cells): 
    121121                    raise ValueError, "can only add at the end now. that will be fixed if needed." 
    122122                else: 
     
    124124 
    125125    def __getitem__(self, number): 
    126         return self.cells[number] 
    127         #should None i.e. removed cells be handles specially already here? 
     126        return self._cells[number] 
     127        #should None i.e. removed cells be handled specially already here? 
     128 
     129    def __len__(self): 
     130        return len(self._cells) 
     131 
     132    def __iter__(self): 
     133        """for iterating cells in the log. filters out Nones.""" 
     134        cells = [cell for cell in self._cells if cell is not None] 
     135        return iter(cells) 
    128136 
    129137    def remove(self, number): 
    130         cell = self.cells[number] 
    131         self.cells[number] = None #XXX probably not handled properly elsewhere 
     138        cell = self._cells[number] 
     139        if cell is self._cells[-1]: #if is last element 
     140            self._cells.pop() #remove 
     141            while self._cells[-1] is None: #if the previous, next. prev etc 
     142                self._cells.pop() #remove all Nones at the end 
     143        else: 
     144            self._cells[number] = None #XXX these gaps not handled everywhere 
    132145        self.element.remove(cell.element) 
    133146 
     
    221234        """Add a log element. 
    222235 
    223         id is a unique identifier. No other XML element in this file should have 
    224         the same id. 
    225         """ 
    226         self.logs[id] = Log(self, id) 
     236        id is a unique identifier. No other XML element in this file should have        the same id. 
     237        """ 
     238        #should the uniqueness of the id be checked here? 
     239        log = Log(self, id) #this creates a subelement to self.root 
     240        self.logs[id] = log 
     241        return log 
    227242 
    228243    def get_log(self, logid='default-log'): 
     
    238253            raise ValueError('No log with id="%s"' % logid) 
    239254 
     255    def newget_log(self, logid='default-log'): 
     256        #useful for getting a default log without id, 
     257        #even if/when self.logs is exposed for id-using getting of logs. 
     258        #to replace current get_log when that is not used anymore 
     259        if logid in self.logs: 
     260            return self.logs[logid] 
     261        else: 
     262            raise ValueError('No log with id="%s"' % logid) 
     263 
    240264    def oldget_cell(self, number, logid='default-log'): 
    241265        #note: Tzanko considers this too slow for nbshell 
     
    247271            return ET.SubElement(log, 'cell', number=str(number)) 
    248272 
    249     def add_cell(self, number,  logid='default-log'): 
     273    def add_cell(self, number=None,  logid='default-log'): 
    250274        log = self.logs[logid] 
     275        if number is None: #add to end by default 
     276            #not used by nbshell currently, 
     277            #but handy for at least tests.. 
     278            number = len(log) 
    251279        cell = log.add(number) 
    252280        return cell 
     
    256284        return log[number] 
    257285 
    258     def get_last_cell(self,logid='default-log'): 
     286    def get_last_cell(self, logid='default-log'): 
    259287        log = self.logs[logid] 
    260288        return log[-1] 
     
    400428        #XXX new for the new cell and log system, untested 
    401429        log = self.logs[logid] 
    402         return '\n'.join(cell.get_input(specials) for cell in log.cells
     430        return '\n'.join(cell.get_input(specials) for cell in log
    403431 
    404432    def start_checkpointing(self, checkpoint=10): 
     
    594622        sheet = ET.Element('sheet') 
    595623        block = ET.SubElement(sheet, 'ipython-block', logid=logid) 
    596         for cell in log.cells
     624        for cell in log
    597625            #dbg 
    598626            print "*NEW*DEFAULT SHEET: cell", cell 
  • nbdoc/trunk/notabene/testing/test_notebook.py

    r742 r746  
    195195 
    196196def test_log(): 
    197     nb = test_new() 
    198     log = nb.get_log() 
    199      
     197    nb = test_new() #adds a default log 
     198    logelement = nb.get_log() #old way, to be removed? 
     199    log = nb.newget_log() 
     200 
     201    #using the default log via notebook methods 
    200202    py.test.raises(IndexError, "nb.get_cell(1)") #should not create anymore 
    201203    py.test.raises(ValueError, "nb.add_cell(15)") #can only add to end now 
    202     cell = nb.add_cell(0) 
     204    cell = nb.add_cell(0) #is added to default log 
    203205    assert cell is nb.get_cell(0) #this should be the same one 
    204206    assert cell.input is None 
    205207    assert cell.output is None 
    206208 
     209    #multiple log support 
     210    assert py.test.raises(ValueError, "nb.get_log('log2')") #nonexisting 
     211    log2 = nb.add_log('log2') 
     212    assert log2.element is nb.get_log('log2') #backwards compat. for nbshell 
     213    assert log2 is nb.logs['log2'] #this is how Notebook does internally. 
     214                                   #ok to expose to outside too? 
     215    cell20 = nb.add_cell(0, 'log2') 
     216    cell21 = log2.add(1) #also this is possible. ok? 
     217    assert cell20 is nb.get_cell(0, 'log2') 
     218    assert cell21 is nb.get_cell(1, 'log2') 
     219    assert cell21.number == 1 
     220    assert cell20 is log2[0] #again: Notebook does this. ok from outside? 
     221 
     222    #back to default log, to test cell functionality 
    207223    python_in = "3 // 2" 
    208224    python_out = "1" 
     
    215231    assert cell.element.find('input').text == python_in 
    216232    assert cell.element.find('output').text == python_out 
    217      
    218     #note: the do-specials in original Cell not there 
     233    #nbshell also calls methods of xml elements 
     234    #so they need to be exposed like this, 
     235    #but at least in some cases the use should be limited to querying 
     236    #.. altough manipulating some attributes is safe, 
     237    #but e.g. changing the number attribute of a cell is not, 
     238    #even though it is a property that wraps xml, 
     239    #because Logs also have an index of them too. 
     240     
     241    #note: the do-specials in original Cell not in the new system (yet) 
    219242 
    220243    #should be standard elements too? 
     
    232255    assert cell.element.find('stderr').text == stderrtext 
    233256 
     257    #(default) sheet test 
    234258    sheet = nb.default_sheet() 
    235259    ipblock = sheet.find('ipython-block') 
     
    239263            found.add(ic.attrib['type']) 
    240264    assert found == set(['input', 'stdout', 'stderr', 'output']) 
    241  
    242  
    243      
    244      
    245  
    246      
     265    #cell.get_sheet_tags lacks tests 
     266 
     267    #cell removal/deletion 
     268    cell2 = nb.add_cell() 
     269    cell3 = nb.add_cell() 
     270    for number, cell in enumerate(log): 
     271        assert number == cell.number 
     272    number = cell2.number 
     273    log.remove(number) 
     274    assert log[number] is None #is not removed 'cause is in the middle 
     275    numcells = log.element.xpath('./cell[@number=%s]' % number) 
     276    assert len(numcells) == 0 #the cell was actually removed from xml 
     277 
     278    #so far so good, but then all kinds of strange things start to happen: 
     279    assert nb.get_cell(number) is None #does this make sense? 
     280    #assert nb.get_last_cell() is None #this certainly does not! 
     281    #so log.remove is fixed to handle it: 
     282    lastcell = nb.get_last_cell() 
     283    assert lastcell is log[-1] #nice, no? this also public.. 
     284    nb.remove_cell(lastcell.number) 
     285    assert nb.get_last_cell() is not None 
     286    for cell in log: 
     287        print etree.tostring(cell.element) 
     288 
     289    #should all trailing Nones be removed? 
     290    #probably so, to make this succeed: 
     291    cell2 = nb.add_cell() 
     292    cell3 = nb.add_cell() 
     293    cell4 = nb.add_cell() 
     294    nb.remove_cell(cell2.number) 
     295    nb.remove_cell(cell3.number) 
     296    nb.remove_cell(cell4.number) 
     297    assert nb.get_last_cell() is not None 
     298 
     299     
     300 
     301     
     302     
     303 
     304