Changeset 686
- Timestamp:
- 08/13/05 10:06:36 (3 years ago)
- Files:
-
- nbshell/trunk/ChangeLog (modified) (1 diff)
- nbshell/trunk/nbshell/FigurePlugin.py (modified) (2 diffs)
- nbshell/trunk/nbshell/PlainTextPlugin.py (modified) (5 diffs)
- nbshell/trunk/nbshell/PythonPlugin.py (modified) (10 diffs)
- nbshell/trunk/nbshell/Sheet.py (modified) (16 diffs)
- nbshell/trunk/nbshell/frame.py (modified) (4 diffs)
- nbshell/trunk/nbshell/ipnDocument.py (modified) (1 diff)
- nbshell/trunk/nbshell/utils.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
nbshell/trunk/ChangeLog
r679 r686 1 08.13.2005: 2 * Changed format of notebook files. Now nbshell supports the 3 docbook format. 4 5 * Refactored methods for inserting cells in the notebook and added 6 UI to use them 7 1 8 08.10.2005: 2 9 * Added a type variable in each XXXDocumnetPlugin class the nbshell/trunk/nbshell/FigurePlugin.py
r679 r686 63 63 64 64 type = 'figure' 65 65 def __len__(self): 66 return 1 67 68 def Split(self, pos): 69 assert(0) 70 66 71 def Clear(self): 67 72 """Clears all data""" … … 188 193 index = self.view.GetIndex(self.id) 189 194 self.view.DeleteCell(index, update) 195 196 position = property(fget = lambda :0, fset = lambda x:None) 190 197 191 198 class FigureCtrl(wx.Window): nbshell/trunk/nbshell/PlainTextPlugin.py
r683 r686 46 46 # return "encoded" #Probably only the python code plugin should be raw 47 47 48 def CreateDocumentPlugin(self,document ):48 def CreateDocumentPlugin(self,document, text = None): 49 49 """Creates the document part of the plugin. The returned object is 50 50 stored in ipgDocument.celllist and is responsible for storing and 51 51 serialization of data. "data" contains initial data for the plugin. 52 52 """ 53 return PlainTextDocumentPlugin(document )53 return PlainTextDocumentPlugin(document, text) 54 54 55 55 def CreateViewPlugin(self,docplugin, view): … … 72 72 73 73 class PlainTextDocumentPlugin(object): 74 def __init__(self, document ):74 def __init__(self, document, text = None): 75 75 """Initialization. If element is <sheet> then the text is 76 76 element.text. If the element is something else, then the text is … … 79 79 self.document = document 80 80 self.sheet = document.sheet 81 self.text = ''81 self.text = text or '' 82 82 self.index = None #Set by AddCell, InsertCell, DeleteCell 83 83 self.view = None #This plugin is designed for a single view. For … … 134 134 135 135 def __len__(self): 136 return self.view.window.GetLeng ht()136 return self.view.window.GetLength() 137 137 138 138 def SetSavePoint(self): … … 175 175 if update: 176 176 self.view.Update() 177 return text[pos:] 177 return lambda p:\ 178 self.sheet.InsertCell('plaintext', p, update = False, text = text[pos:]) 178 179 179 180 class PlainTextNotebookViewPlugin(object): nbshell/trunk/nbshell/PythonPlugin.py
r679 r686 73 73 # return "raw" #Probably only the python code plugin should be raw 74 74 75 def CreateDocumentPlugin(self,document, ipython_block):75 def CreateDocumentPlugin(self,document, element): 76 76 """Creates the document part of the plugin. The returned object is 77 77 stored in ipgDocument.celllist and is responsible for storing and 78 serialization of data. "data" contains initial data for the plugin.78 serialization of data. 79 79 """ 80 return PythonDocumentPlugin(document, ipython_block)80 return PythonDocumentPlugin(document, element) 81 81 82 82 def CreateViewPlugin(self,docplugin, view): … … 100 100 101 101 class PythonDocumentPlugin(object): 102 def __init__(self, document, ipython_block):102 def __init__(self, document, element): 103 103 """Initialization. ipython-block is a Elemtent object holding a 104 104 <ipython-block> tag""" 105 105 self.document = document 106 106 self.sheet = document.sheet 107 self. block = ipython_block #TODO: a better name?108 self.logid = self. block.get('logid', 'default-log')107 self.element = element 108 self.logid = self.element.get('logid', 'default-log') 109 109 self.log = document.logs[self.logid] 110 110 self.notebook = document.notebook … … 114 114 #multiple views there should be some modifications 115 115 #print "block:" 116 #etree.dump(self. block) #dbg116 #etree.dump(self.element) #dbg 117 117 self.cells = \ 118 118 [notebook.Cell(self.notebook.get_cell(x.attrib['number'],self.logid))\ 119 for x in self. block]119 for x in self.element] 120 120 121 121 type = 'python' 122 122 123 123 def __len__ (self): 124 return len(self. block)124 return len(self.element) 125 125 126 126 def Clear(self): 127 127 """Clears all data""" 128 self. block.clear()129 self. block.attrib['logid'] = self.logid128 self.element.clear() 129 self.element.attrib['logid'] = self.logid 130 130 self.cells = [] 131 131 self.view.Update() … … 140 140 def GetStuff(self, id): #TODO: a better name 141 141 """Returns a tuple (type, elem) where type is the type of the id'th 142 element in self. blockand elem is the the corresponding element in the142 element in self.element and elem is the the corresponding element in the 143 143 cell in the log""" 144 144 145 145 #print "id -> %s"%(str(id),) #dbg 146 type = self. block[id].attrib['type']146 type = self.element[id].attrib['type'] 147 147 #print "type-> %s"%(type,) #dbg 148 148 return (type, self.cells[id].element.find(type)) … … 154 154 155 155 root = etree.Element('ipython-block', logid = self.logid) 156 for elem in self. block[pos:]:156 for elem in self.element[pos:]: 157 157 root.append(elem) 158 del self. block[pos:]158 del self.element[pos:] 159 159 del self.cells[pos:] 160 160 #TODO: spagetti 161 161 self.sheet.Update(dicts = True, update = update) 162 return root 162 return lambda p:\ 163 self.sheet.InsertCell('python', p, update = False, element = root) 163 164 164 165 def GetFactory(self): … … 198 199 return self.id 199 200 200 #self.position is a property which gives the index in self. soc.blockof201 #self.position is a property which gives the index in self.doc.element of 201 202 #the ipython-cell on which the cursor currently is. 202 203 def __get_position(self): 203 linenum = self.window.GetCurrentLine() 204 if self.line2log[linenum] is not None: 205 return self.line2log[linenum][0] 204 pos = self.window.GetCurrentPos() 205 linenum = self.window.LineFromPosition(pos) 206 if pos == self.window.GetLength(): 207 return len(self.line2log) 208 elif self.line2log[linenum] is not None: 209 return len(self.doc.element) 206 210 else: 207 211 l = len(self.line2log) … … 209 213 linenum +=1 210 214 if linenum == l: 211 return len(self.doc. block)215 return len(self.doc.element) 212 216 else: 213 217 return self.line2log[linenum][0] … … 288 292 #every little change that can occur. Think of a way to avoid this 289 293 cells = self.doc.cells 290 block = self.doc. block294 block = self.doc.element 291 295 #print "data->", data #dbg 292 296 … … 371 375 372 376 #get the number of digits of the number of the input 373 item = self.doc. block[line[0]]377 item = self.doc.element[line[0]] 374 378 type = self.doc.GetStuff(line[0])[0] 375 379 strnumber = item.attrib['number'] … … 496 500 else: 497 501 pos = self.line2log[linenum][0] 498 if pos == len(self.doc. block) -1:502 if pos == len(self.doc.element) -1: 499 503 #this block has only one cell, so do nothing 500 504 return nbshell/trunk/nbshell/Sheet.py
r683 r686 13 13 __version__ = Release.version 14 14 15 import StringIO 15 16 16 17 from lxml import etree … … 19 20 20 21 from nbshell import PythonPlugin 21 from nbshell.utils import getindex, getiterator222 from nbshell.utils import * 22 23 23 24 … … 41 42 # 'special', 'output', etc. The values of the dictionary are lists of 42 43 # tuples of the type (block, id) where block is a PythonDocumentPlugin 43 # object and the corresponding element is block. block[id]44 # object and the corresponding element is block.element[id] 44 45 self.cell2sheet = {} 45 46 # sheet2sheet is a dictionary. For each element in a block it returns … … 55 56 56 57 def __get_current_cell(self): 58 if self._currentcell is None: 59 self.__set_current_cell(self.celllist[0]) 57 60 return self._currentcell 61 58 62 def __set_current_cell(self, cell): 59 63 self._currentcell = cell … … 65 69 pos. If pos=-1 insert at the end. **kwds is passed to the 66 70 plugin.Returns an instance to the cell""" 67 #TODO: Update the dicts here68 71 factory = self.factory[type] 69 72 cell = factory.CreateDocumentPlugin(self.doc, **kwds) … … 74 77 print 'cell-> %s, pos->%s'%(str(cell),str(pos)) #dbg 75 78 self.__insert_cell(cell, pos) 79 80 #TODO: Smarter update of the dicts here 81 self.Update(update = False, dicts = True) 76 82 if update: 77 83 view.Update() … … 148 154 149 155 def UpdateDoc(self): 150 """Updates data from the view""" 156 """Updates data from the view. Then updates self.element, from the 157 internal representation of data. If there was some error, throws an 158 exception""" 151 159 for doccell in self.celllist: 152 160 doccell.view.UpdateDoc() 153 161 154 162 #Get the xml from the document 163 text = StringIO.StringIO() 164 text.write('<sheet>') 165 for doccell in self.celllist: 166 if doccell.type == 'plaintext': 167 text.write(doccell.text) 168 elif doccell.type == 'python': 169 etr = etree.ElementTree(doccell.element) 170 etr.write(text, encoding='utf-8') 171 elif doccell.type == 'figure': 172 etr = etree.ElementTree(doccell.element) 173 etr.write(text, encoding = 'utf-8') 174 text.write('</sheet>') 175 text.flush() 176 text = StringIO.StringIO(text.getvalue()) 177 #Convert to etree.Element 178 etr = etree.ElementTree() 179 try: 180 etr.parse(text) 181 except: 182 #TODO: Handle syntax errors here 183 184 #NBDOC: The notebook should check the documents for syntax 185 #errors and be able to give meanigful description of errors to 186 #the user 187 188 raise #dbg 189 self.element = etr.getroot() 190 # Now remove the old sheet and replace it with the new one 191 oldsheet = self.notebook.root.find('sheet') 192 if oldsheet is not None: 193 self.notebook.root.remove(oldsheet) 194 self.notebook.root.append(self.element) 195 155 196 def __append_plaintext_cell(self, iterator, prevlist, elemlist,\ 156 197 endtaglist, update = True): … … 187 228 if elem.tag in tag2type.keys(): 188 229 self.InsertCell(tag2type[elem.tag], update = False, \ 189 ipython_block= elem)230 element = elem) 190 231 l = len(elemlist) 191 232 prevlist = elemlist[:-1] … … 335 376 self.cell2sheet = {} 336 377 self.sheet2sheet = {} 337 for block in self.celllist: 338 # check if block is a PythonDocumentPlugin object 339 try: 340 logid = block.logid 341 except: 342 continue 378 for block in filter(lambda x:x.type=='python', self.celllist): 379 logid = block.logid 343 380 for (i, cell) in enumerate(block.cells): 344 key = (logid, cell.number, block. block[i].attrib['type'])381 key = (logid, cell.number, block.element[i].attrib['type']) 345 382 val = self.cell2sheet.get(key,[]) 346 383 val.append((block, i)) … … 397 434 398 435 #1. Add the element 399 l = len(block. block)436 l = len(block.element) 400 437 if pos is None: 401 438 pos = l … … 405 442 406 443 element = etree.Element('ipython-cell',type = type, number = str(number)) 407 block. block[pos:pos] = [element]444 block.element[pos:pos] = [element] 408 445 block.cells[pos:pos] = [cell] 409 446 … … 437 474 438 475 #1. Delete the element 439 l = len(block. block)476 l = len(block.element) 440 477 if pos < 0: 441 478 pos = l + pos 442 elem = block. block[pos]443 del(block. block[pos])479 elem = block.element[pos] 480 del(block.element[pos]) 444 481 del(block.cells[pos]) 445 482 if update: … … 496 533 #1.1.1 Yes. Check if one of the elements is on the insert 497 534 #position. 498 if len(block. block)>pos2add and \535 if len(block.element)>pos2add and \ 499 536 block.cells[pos2add] == cell and \ 500 block. block[pos2add].attrib['type'] == type :537 block.element[pos2add].attrib['type'] == type : 501 538 #Increment the insert position 502 539 pos2add += 1 … … 641 678 lastfig = self.celllist[index] 642 679 codeelem = etree.Element('ipython-block', logid = block.logid) 643 #Fix the text644 if index < len(self.celllist)-1 and\645 self.celllist[index+1].type == 'plaintext':646 textblock = self.celllist[index+1]647 codeelem.tail = textblock.GetText()648 textblock.element = codeelem649 blocklist.append(textblock)650 680 #Insert the new block 651 681 block = self.InsertCell('python', index+1, update = False,\ 652 ipython_block= codeelem)682 element = codeelem) 653 683 self.Update(update = False, dicts = True) 654 684 retvalue = block … … 666 696 # self.__update_list(blocklist) 667 697 668 669 698 def ReplaceCells(self, logid, oldcell, newcell, update = True): 670 699 """Changes all the <ipython-cell type=..., number=oldcell.number> elements to … … 677 706 val = self.cell2sheet.get((logid, oldcell.number, type),[]) 678 707 for (block,pos) in val: 679 block. block[pos].attrib['number'] = str(newcell.number)708 block.element[pos].attrib['number'] = str(newcell.number) 680 709 block.cells[pos] = newcell 681 710 self.Update(update, dicts = True) 682 711 683 712 def InsertText(self, block, pos, update = True): 684 """Splits the given block and inserts a text cell""" 685 index = getindex(self.element,block.block) 686 #Get the XML for the new ipython-block 687 nextblock = block.Split(pos) 688 #update the old text cell to point to the new block 689 oldtextcell = self.celllist[block.index+1] 690 nextblock.tail = oldtextcell.GetText() 691 oldtextcell.element = nextblock 692 #insert nextblock in the sheet 693 self.element[index+1:index+1] = [nextblock] 694 ind = oldtextcell.index 695 self.InsertCell('python',ind, update = False, \ 696 ipython_block = nextblock) 697 #insert a new text cell in the celllist 698 self.InsertCell('plaintext', ind, update = False, element = block.block) 699 #update 700 self.Update(dicts = True) 701 if update: 702 self.__update_list(self.celllist[ind-1:ind+2]) 703 self.view.Update() 704 705 def InsertCode(self, block, pos, update = True): 706 """Splits the given text block and inserts a code block. The logid 707 for the new block is set in self.currentlog""" 713 """Splits the given block and inserts an empty text cell""" 714 self.Insert(block, pos,\ 715 check = lambda block, prev, next, pos:\ 716 (block.type != 'plaintext') and 717 (pos > 0 or prev == None or prev.type != 'plaintext') and\ 718 (pos < len(block) or next == None or next.type != 'plaintext'),\ 719 insert = lambda pos: 720 self.InsertCell('plaintext', pos, update = False, text = ''),\ 721 update = update) 722 723 def InsertCode(self, block, pos, logid = None, update = True): 724 """Splits the given text block and inserts an empty code block. If 725 logid is None self.currentlog is used""" 726 if logid is None: 727 logid = self.currentlog 708 728 assert(self.last) #The last inputs must be set 709 newtext = block.Split(pos, update = False) 710 #Create the new code element 711 codeelement = etree.Element('ipython-block',logid=self.currentlog) 712 #Delete the last element of self.currentlog from the sheet 713 log = self.doc.logs[self.currentlog] 714 key = (self.currentlog, log.lastcell.number, 'input') 715 value = self.cell2sheet[key] 716 while len(value)>0: 717 blk, position = value[0] 718 self.DeleteElement(blk,position,update = False) 719 #insert the last element in codeelement 720 codeelement.append(etree.Element('ipython-cell',type='input',\ 721 number = str(key[1]))) 722 #set newtext at the tail 723 codeelement.tail = newtext 724 #get the index in the <sheet> element where we must insert the new cell 725 id = block.index 726 l = len(self.celllist) 727 while id<l: 728 try: 729 self.celllist[id].log 730 except: 731 id+=1 732 else: 733 break 734 print 'new element:' #dbg 735 etree.dump(codeelement) #dbg 736 if id==l: #we must append the new element at the end of <sheet> 737 index = len(self.element) 738 self.element[index:index] = [codeelement] 739 else: 740 #insert the new code element 741 index = getindex(self.element,self.celllist[id].block) 742 self.element[index:index] = [codeelement] 743 #insert the new cells in celllist 744 self.InsertCell('python',pos=block.index+1,update = False,\ 745 ipython_block = codeelement) 746 self.InsertCell('plaintext', pos = block.index+2, update = False,\ 747 element = codeelement) 748 print 'new sheet:' #dbg 749 etree.dump(self.element) #dbg 750 #update stuff. I could update only the modified cells, but I'm lazy 751 self.Update(update,dicts = True) 752 729 730 def insert(p): 731 #Delete the last element of self.currentlog from the sheet 732 log = self.doc.logs[logid] 733 key = (logid, log.lastcell.number, 'input') 734 value = self.cell2sheet[key] 735 while len(value)>0: 736 blk, position = value[0] 737 self.DeleteElement(blk,position,update = False) 738 #insert the last element in codeelement 739 codeelement = etree.Element('ipython-block', logid = logid) 740 etree.SubElement(codeelement, 'ipython-cell',type='input',\ 741 number = str(key[1])) 742 return self.InsertCell('python', p, update = False,\ 743 element = codeelement) 744 745 746 self.Insert(block, pos, 747 check = lambda block, prev, next, pos:\ 748 (block.type != 'python' or block.logid != logid) and\ 749 (pos > 0 or prev == None or prev.type != 'python' or\ 750 prev.logid != logid) and\ 751 (pos < len(block) or next == None or next.type != 'python'\ 752 or next.logid != logid),\ 753 insert = insert, update = False) 754 self.Update(update) 755 753 756 def InsertFigure(self, block, pos, figurexml, update = True): 754 757 """Inserts the given figure at the given position in the given block. 755 758 Returns the new block 756 759 """ 757 758 fig_elem = etree.XML(figurexml) 759 #Figure out where to insert the figure, according to the type of the 760 #block 761 762 if block.type == 'python': 763 #This is a python block. Then if pos points to the position after 764 #the end of the block, insert the figure after the block. If not 765 #split the block and insert the figure at the given position. 766 index = getindex(self.element, block.block) 767 if pos >= len(block): # here we are using the __len__ method 768 #Insert the xml in the sheet 769 self.element[index+1:index+1] = [fig_elem] 770 #Fix the text after the given element 771 if self.celllist[block.index+1].type == 'plaintext': 772 textblock = self.celllist[block.index+1] 773 fig_elem.tail = textblock.GetText() 774 textblock.element = fig_elem 775 #Insert in the view 776 figcell = self.InsertCell('figure', pos = block.index+1,\ 777 update = False, element= fig_elem) 778 self.Update(update = update, dicts = True) 779 else: #We have to split the given block 780 #Get the XML for the new ipython-block 781 nextblock = block.Split(pos) 782 #update the old text cell to point to the new block 783 oldtextcell = self.celllist[block.index+1] 784 if oldtextcell.type == 'plaintext': 785 nextblock.tail = oldtextcell.GetText() 786 oldtextcell.element = nextblock 787 #insert nextblock in the sheet 788 self.element[index+1:index+1] = [nextblock] 789 self.InsertCell('python',pos = block.index+1, update = False, \ 790 ipython_block = nextblock) 791 #insert the figure 792 self.element[index+1:index+1] = [fig_elem] 793 figcell = self.InsertCell('figure', block.index+1,\ 794 update = False, element = fig_elem) 795 #update 796 self.Update(update = update, dicts = True) 797 return figcell 798 else: 799 raise NotImplementedError 800 760 self.Insert(block, pos,\ 761 check = lambda cur, prev, next, pos:True,\ 762 insert = lambda pos:\ 763 self.InsertCell('figure', pos, update = False, 764 element = etree.XML(figurexml)), 765 update = update) 766 767 def Insert(self, block, pos, check = lambda cur, prev, next, pos:True, 768 insert = lambda pos:None, update = True): 769 """Inserts something in the given block at the given position. The 770 block is inserted if the check function returns True. The actual 771 insertion is done by the insert function, which is given one 772 parameter, the index in the celllist where the new block must be 773 inserted. The insert function must return the new block""" 774 print 'block.index ->', block.index 775 if check(block,\ 776 ifelse(block.index>0,\ 777 lambda:self.celllist[block.index-1], lambda:None),\ 778 ifelse(block.index < len(self.celllist)-1,\ 779 lambda:self.celllist[block.index+1], lambda:None),pos): 780 if pos == 0: 781 list = [insert(block.index)] 782 elif pos == len(block): 783 list = [insert(block.index+1)] 784 else: 785 newinsert = block.Split(pos) 786 list = [insert(block.index+1), newinsert(block.index+2)] 787 if update: 788 self.__update_list(list) 789 self.view.Update() nbshell/trunk/nbshell/frame.py
r668 r686 13 13 __version__ = Release.version 14 14 15 import StringIO 16 15 17 import wx 18 19 from nbshell import SimpleXMLWriter 16 20 17 21 def idgen(): … … 32 36 #NBShell menu identifiers 33 37 ID_RERUN = id_iter.next() 38 39 #Insert menu identifiers 40 ID_INSERT_TEXT = id_iter.next() 41 ID_INSERT_CODE = id_iter.next() 42 ID_INSERT_FIGURE = id_iter.next() 34 43 35 44 class ipnFrame(wx.Frame): … … 75 84 nbshellmenu.Append(ID_RERUN, "&Rerun", "Rerun the notebook") 76 85 wx.EVT_MENU(self, ID_RERUN, self.OnRerun) 86 87 insertmenu = wx.Menu() 88 insertmenu.Append(ID_INSERT_TEXT, "Insert Text", "Inserts a text cell") 89 insertmenu.Append(ID_INSERT_CODE, "Insert Code", "Inserts a new empty code cell") 90 insertmenu.Append(ID_INSERT_FIGURE, "Insert Figure...", "Inserts a figure") 91 wx.EVT_MENU(self, ID_INSERT_CODE, self.OnInsertCode) 92 wx.EVT_MENU(self, ID_INSERT_TEXT, self.OnInsertText) 93 wx.EVT_MENU(self, ID_INSERT_FIGURE, self.OnInsertFigure) 94 77 95 menu = wx.MenuBar() 78 96 menu.Append(filemenu, "&File") 97 menu.Append(insertmenu, "&Insert") 79 98 menu.Append(nbshellmenu, "&NBShell") 80 99 self.SetMenuBar(menu) … … 175 194 def OnRerun(self,evt): 176 195 self.app.document.Rerun() 196 197 def OnInsertText(self, evt): 198 sheet = self.app.document.sheet 199 block = sheet.currentcell 200 sheet.InsertText(block, block.view.position, update = True) 201 202 def OnInsertCode(self, evt): 203 sheet = self.app.document.sheet 204 block = sheet.currentcell 205 sheet.InsertCode(block, block.view.position, update = True) 206 207 def OnInsertFigure(self, evt = None): 208 dlg = wx.FileDialog(self, "Choose a Fifure", \ 209 wildcard = "PNG files (*.png)|*.png",style = wx.OPEN) 210 val = dlg.ShowModal() 211 if val == wx.ID_CANCEL: 212 return None 213 else: 214 filename = dlg.GetPath() 215 sheet = self.app.document.sheet 216 text = StringIO.StringIO() 217 #We use XMLWriter, to avoid problems with a filename with special symbols, 218 #for example: blah.png"/><some random xml code>.png 219 writer = SimpleXMLWriter.XMLWriter(text, encoding = 'utf-8') 220 writer.start('ipython-figure', type = "png", filename = filename) 221 writer.end() 222 223 sheet.InsertFigure(sheet.currentcell, sheet.currentcell.view.position, 224 figurexml = text.getvalue()) 225 text.close() 226 nbshell/trunk/nbshell/ipnDocument.py
r683 r686 107 107 self.sheet.Update(update = True) 108 108 etree.dump(self.notebook.root) #dbg 109 #Set the current cell and position 109 110 except: 110 111 #self.Clear() #TODO: This does not work well if an exception occured. nbshell/trunk/nbshell/utils.py
r683 r686 44 44 def getiterator2(root): 45 45 """Returns an iterator which for each subelement yields a tuple of all 46 elements in the path from the root to the given subelement""" 46 elements in the path from the root to the given subelement, excluding the 47 root. The root element is not returned""" 47 48 48 root_tup = (root,)49 yield root_tup50 49 #Recursive generators, yummy :) 51 50 for subelement in root: 51 yield (subelement,) 52 52 iter = getiterator2(subelement) 53 53 for result in iter: 54 yield root_tup + result 54 yield (subelement,) + result 55 56 def default(func, default = None): 57 """If func throws an exception returns default, else returns the result of 58 the function""" 59 try: 60 res = func() 61 except: 62 res = default 63 return res 64 65 def ifelse(expr1, expr2, expr3): 66 """If expr1 is True returns expr2(), else returns expr3(). expr2 and expr3 67 are functions. To use ifelse for regular expressions use: 68 ifelse(expr1, lambda:expr2, lambda:expr3)""" 69 #This ensures that ifelse(1, lambda:None, lambda:2) will return None, not 2 70 #expr2 and expr 71 return ((expr1) and (expr2(),) or (expr3(),))[0]
