blob: 397347ed66972406cdd72ac1592ab1de1e4ac33c [file] [log] [blame]
Just van Rossum40f9b7b1999-01-30 22:39:17 +00001import W
2import Wkeys
3import struct
4import string
5import types
Jack Jansen9ad27522001-02-21 13:54:31 +00006import re
Just van Rossum4e6d13c2002-03-26 12:06:11 +00007from Carbon import Qd, Icn, Fm, QuickDraw
Just van Rossum4e6d13c2002-03-26 12:06:11 +00008from Carbon.QuickDraw import hilitetransfermode
9
Just van Rossum40f9b7b1999-01-30 22:39:17 +000010
11nullid = '\0\0'
12closedid = struct.pack('h', 468)
13openid = struct.pack('h', 469)
14closedsolidid = struct.pack('h', 470)
15opensolidid = struct.pack('h', 471)
16
17arrows = (nullid, closedid, openid, closedsolidid, opensolidid)
18
Just van Rossum3eec7622001-07-10 19:25:40 +000019has_ctlcharsRE = re.compile(r'[\000-\037\177-\377]')
Jack Jansen9ad27522001-02-21 13:54:31 +000020def ctlcharsREsearch(str):
Jack Jansendbd0c3a2001-03-01 23:15:54 +000021 if has_ctlcharsRE.search(str) is None:
Jack Jansen9ad27522001-02-21 13:54:31 +000022 return -1
23 return 1
24
Just van Rossum40f9b7b1999-01-30 22:39:17 +000025def double_repr(key, value, truncvalue = 0,
26 type = type, StringType = types.StringType,
Jack Jansen9ad27522001-02-21 13:54:31 +000027 has_ctlchars = ctlcharsREsearch, _repr = repr, str = str):
Just van Rossum40f9b7b1999-01-30 22:39:17 +000028 if type(key) == StringType and has_ctlchars(key) < 0:
29 key = str(key)
30 else:
31 key = _repr(key)
Just van Rossum460ff201999-10-30 11:43:25 +000032 if key == '__builtins__':
Just van Rossum40f9b7b1999-01-30 22:39:17 +000033 value = "<" + type(value).__name__ + " '__builtin__'>"
34 elif key == '__return__':
35 # bleh, when returning from a class codeblock we get infinite recursion in repr.
36 # Use safe repr instead.
37 import repr
38 value = repr.repr(value)
39 else:
40 try:
41 value = _repr(value)
42 '' + value # test to see if it is a string, in case a __repr__ method is buggy
43 except:
Just van Rossumdc3c6172001-06-19 21:37:33 +000044 value = '\xa5\xa5\xa5 exception in repr()'
Just van Rossum40f9b7b1999-01-30 22:39:17 +000045 if truncvalue:
46 return key + '\t' + value[:255]
47 return key + '\t' + value
48
49
Just van Rossum4e6d13c2002-03-26 12:06:11 +000050def truncString(s, maxwid):
51 if maxwid < 1:
52 return 1, ""
53 strlen = len(s)
54 strwid = Qd.TextWidth(s, 0, strlen);
55 if strwid <= maxwid:
56 return 0, s
Just van Rossum40f9b7b1999-01-30 22:39:17 +000057
Just van Rossum4e6d13c2002-03-26 12:06:11 +000058 Qd.TextFace(QuickDraw.condense)
59 strwid = Qd.TextWidth(s, 0, strlen)
60 ellipsis = Qd.StringWidth('\xc9')
61
62 if strwid <= maxwid:
63 Qd.TextFace(0)
64 return 1, s
65 if strwid < 1:
66 Qd.TextFace(0)
67 return 1, ""
68
69 mid = int(strlen * maxwid / strwid)
70 while 1:
71 if mid <= 0:
72 mid = 0
73 break
74 strwid = Qd.TextWidth(s, 0, mid) + ellipsis
75 strwid2 = Qd.TextWidth(s, 0, mid + 1) + ellipsis
76 if strwid <= maxwid and maxwid <= strwid2:
77 if maxwid == strwid2:
78 mid += 1
79 break
80 if strwid > maxwid:
81 mid -= 1
82 if mid <= 0:
83 mid = 0
84 break
85 elif strwid2 < maxwid:
86 mid += 1
87 Qd.TextFace(0)
88 return 1, s[:mid] + '\xc9'
89
90
91def drawTextCell(text, cellRect, ascent, theList):
92 l, t, r, b = cellRect
93 cellwidth = r - l
Jack Jansena359a3d2003-02-12 15:39:16 +000094 Qd.MoveTo(int(l + 2), int(t + ascent))
Just van Rossum4e6d13c2002-03-26 12:06:11 +000095 condense, text = truncString(text, cellwidth - 3)
96 if condense:
97 Qd.TextFace(QuickDraw.condense)
98 Qd.DrawText(text, 0, len(text))
99 Qd.TextFace(0)
100
101
102PICTWIDTH = 16
103
104
105class BrowserWidget(W.CustomList):
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000106
107 def __init__(self, possize, object = None, col = 100, closechildren = 0):
108 W.List.__init__(self, possize, callback = self.listhit)
109 self.object = (None,)
110 self.indent = 16
111 self.lastmaxindent = 0
112 self.closechildren = closechildren
113 self.children = []
114 self.mincol = 64
115 self.setcolumn(col)
116 self.bind('return', self.openselection)
117 self.bind('enter', self.openselection)
118 if object is not None:
119 self.set(object)
120
121 def set(self, object):
122 if self.object[0] is not object:
123 self.object = object,
124 self[:] = self.unpack(object, 0)
125 elif self._parentwindow is not None and self._parentwindow.wid:
126 self.update()
127
128 def unpack(self, object, indent):
129 return unpack_object(object, indent)
130
131 def update(self):
132 # for now...
133 W.SetCursor('watch')
134 self.setdrawingmode(0)
135 sel = self.getselectedobjects()
136 fold = self.getunfoldedobjects()
137 topcell = self.gettopcell()
138 self[:] = self.unpack(self.object[0], 0)
139 self.unfoldobjects(fold)
140 self.setselectedobjects(sel)
141 self.settopcell(topcell)
142 self.setdrawingmode(1)
143
144 def setcolumn(self, col):
145 self.col = col
146 self.colstr = struct.pack('h', col)
147 if self._list:
148 sel = self.getselection()
149 self.setitems(self.items)
150 self.setselection(sel)
151
152 def key(self, char, event):
153 if char in (Wkeys.leftarrowkey, Wkeys.rightarrowkey):
154 sel = self.getselection()
155 sel.reverse()
156 self.setdrawingmode(0)
157 for index in sel:
158 self.fold(index, char == Wkeys.rightarrowkey)
159 self.setdrawingmode(1)
160 else:
161 W.List.key(self, char, event)
162
163 def rollover(self, (x, y), onoff):
164 if onoff:
165 if self.incolumn((x, y)):
166 W.SetCursor('hmover')
167 else:
168 W.SetCursor('arrow')
169
170 def inarrow(self, (x, y)):
171 cl, ct, cr, cb = self._list.LRect((0, 0))
172 l, t, r, b = self._bounds
173 if (x - cl) < 16:
174 cellheight = cb - ct
175 index = (y - ct) / cellheight
176 if index < len(self.items):
177 return 1, index
178 return None, None
179
180 def incolumn(self, (x, y)):
181 l, t, r, b = self._list.LRect((0, 0))
182 abscol = l + self.col
183 return abs(abscol - x) < 3
184
185 def trackcolumn(self, (x, y)):
Jack Jansen5a6fdcd2001-08-25 12:15:04 +0000186 from Carbon import Qd, QuickDraw, Evt
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000187 self.SetPort()
188 l, t, r, b = self._bounds
189 bounds = l, t, r, b = l + 1, t + 1, r - 16, b - 1
190 abscol = l + self.col
191 mincol = l + self.mincol
192 maxcol = r - 10
193 diff = abscol - x
194 Qd.PenPat('\000\377\000\377\000\377\000\377')
195 Qd.PenMode(QuickDraw.srcXor)
196 rect = abscol - 1, t, abscol, b
197 Qd.PaintRect(rect)
198 lastpoint = (x, y)
199 newcol = -1
200 #W.SetCursor('fist')
201 while Evt.Button():
Just van Rossumf376ef02001-11-18 14:12:43 +0000202 Evt.WaitNextEvent(0, 1, None) # needed for OSX
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000203 (x, y) = Evt.GetMouse()
204 if (x, y) <> lastpoint:
205 newcol = x + diff
206 newcol = max(newcol, mincol)
207 newcol = min(newcol, maxcol)
208 Qd.PaintRect(rect)
209 rect = newcol - 1, t, newcol, b
210 Qd.PaintRect(rect)
211 lastpoint = (x, y)
212 Qd.PaintRect(rect)
Jack Jansen362c7cd02002-11-30 00:01:29 +0000213 Qd.PenPat(Qd.GetQDGlobalsBlack())
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000214 Qd.PenNormal()
215 if newcol > 0 and newcol <> abscol:
216 self.setcolumn(newcol - l)
217
218 def click(self, point, modifiers):
219 if point == (-1, -1): # gross.
220 W.List.click(self, point ,modifiers)
221 return
222 hit, index = self.inarrow(point)
223 if hit:
224 (key, value, arrow, indent) = self.items[index]
225 self.fold(index, arrow == 1)
226 elif self.incolumn(point):
227 self.trackcolumn(point)
228 else:
229 W.List.click(self, point, modifiers)
230
231 # for W.List.key
232 def findmatch(self, tag):
233 lower = string.lower
234 items = self.items
235 taglen = len(tag)
236 match = '\377' * 100
237 match_i = -1
238 for i in range(len(items)):
239 item = lower(str(items[i][0]))
240 if tag <= item < match:
241 match = item
242 match_i = i
243 if match_i >= 0:
244 return match_i
245 else:
246 return len(items) - 1
247
248 def close(self):
249 if self.closechildren:
250 for window in self.children:
251 window.close()
252 self.children = []
253 W.List.close(self)
254
255 def fold(self, index, onoff):
256 (key, value, arrow, indent) = self.items[index]
257 if arrow == 0 or (onoff and arrow == 2) or (not onoff and arrow == 1):
258 return
259 W.SetCursor('watch')
260 topcell = self.gettopcell()
261 if onoff:
262 self[index] = (key, value, 4, indent)
263 self.setdrawingmode(0)
264 self[index+1:index+1] = self.unpack(value, indent + 1)
265 self[index] = (key, value, 2, indent)
266 else:
267 self[index] = (key, value, 3, indent)
268 self.setdrawingmode(0)
269 count = 0
270 for i in range(index + 1, len(self.items)):
271 (dummy, dummy, dummy, subindent) = self.items[i]
272 if subindent <= indent:
273 break
274 count = count + 1
275 self[index+1:index+1+count] = []
276 self[index] = (key, value, 1, indent)
277 maxindent = self.getmaxindent()
278 if maxindent <> self.lastmaxindent:
279 newabsindent = self.col + (maxindent - self.lastmaxindent) * self.indent
280 if newabsindent >= self.mincol:
281 self.setcolumn(newabsindent)
282 self.lastmaxindent = maxindent
283 self.settopcell(topcell)
284 self.setdrawingmode(1)
285
286 def unfoldobjects(self, objects):
287 for obj in objects:
288 try:
289 index = self.items.index(obj)
290 except ValueError:
291 pass
292 else:
293 self.fold(index, 1)
294
295 def getunfoldedobjects(self):
296 curindent = 0
297 objects = []
298 for index in range(len(self.items)):
299 (key, value, arrow, indent) = self.items[index]
300 if indent > curindent:
301 (k, v, a, i) = self.items[index - 1]
302 objects.append((k, v, 1, i))
303 curindent = indent
304 elif indent < curindent:
305 curindent = indent
306 return objects
307
308 def listhit(self, isdbl):
309 if isdbl:
310 self.openselection()
311
312 def openselection(self):
313 import os
314 sel = self.getselection()
315 for index in sel:
316 (key, value, arrow, indent) = self[index]
317 if arrow:
318 self.children.append(Browser(value))
319 elif type(value) == types.StringType and '\0' not in value:
320 editor = self._parentwindow.parent.getscript(value)
321 if editor:
322 editor.select()
323 return
324 elif os.path.exists(value) and os.path.isfile(value):
Jack Jansene7ee17c2003-02-06 22:32:35 +0000325 if MacOS.GetCreatorAndType(value)[1] in ('TEXT', '\0\0\0\0'):
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000326 W.getapplication().openscript(value)
327
328 def itemrepr(self, (key, value, arrow, indent), str = str, double_repr = double_repr,
329 arrows = arrows, pack = struct.pack):
330 arrow = arrows[arrow]
331 return arrow + pack('h', self.indent * indent) + self.colstr + \
332 double_repr(key, value, 1)
333
334 def getmaxindent(self, max = max):
335 maxindent = 0
336 for item in self.items:
337 maxindent = max(maxindent, item[3])
338 return maxindent
339
340 def domenu_copy(self, *args):
341 sel = self.getselectedobjects()
342 selitems = []
343 for key, value, dummy, dummy in sel:
344 selitems.append(double_repr(key, value))
345 text = string.join(selitems, '\r')
346 if text:
Just van Rossum01c98052001-11-02 19:21:34 +0000347 from Carbon import Scrap
Jack Jansen65293682001-12-31 15:08:04 +0000348 if hasattr(Scrap, 'PutScrap'):
349 Scrap.ZeroScrap()
350 Scrap.PutScrap('TEXT', text)
351 else:
352 Scrap.ClearCurrentScrap()
353 sc = Scrap.GetCurrentScrap()
354 sc.PutScrapFlavor('TEXT', 0, text)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000355
Just van Rossum4e6d13c2002-03-26 12:06:11 +0000356 def listDefDraw(self, selected, cellRect, theCell,
357 dataOffset, dataLen, theList):
358 self.myDrawCell(0, selected, cellRect, theCell,
359 dataOffset, dataLen, theList)
360
361 def listDefHighlight(self, selected, cellRect, theCell,
362 dataOffset, dataLen, theList):
363 self.myDrawCell(1, selected, cellRect, theCell,
364 dataOffset, dataLen, theList)
365
366 def myDrawCell(self, onlyHilite, selected, cellRect, theCell,
367 dataOffset, dataLen, theList):
368 savedPort = Qd.GetPort()
Jack Jansen362c7cd02002-11-30 00:01:29 +0000369 Qd.SetPort(theList.GetListPort())
Just van Rossum4e6d13c2002-03-26 12:06:11 +0000370 savedClip = Qd.NewRgn()
371 Qd.GetClip(savedClip)
372 Qd.ClipRect(cellRect)
373 savedPenState = Qd.GetPenState()
374 Qd.PenNormal()
375
376 l, t, r, b = cellRect
377
378 if not onlyHilite:
379 Qd.EraseRect(cellRect)
380
381 ascent, descent, leading, size, hm = Fm.FontMetrics()
382 linefeed = ascent + descent + leading
383
384 if dataLen >= 6:
385 data = theList.LGetCell(dataLen, theCell)
386 iconId, indent, tab = struct.unpack("hhh", data[:6])
Just van Rossum3c4dee42002-09-16 21:18:49 +0000387 try:
388 key, value = data[6:].split("\t", 1)
389 except ValueError:
390 # bogus data, at least don't crash.
391 indent = 0
392 tab = 0
393 iconId = 0
394 key = ""
395 value = data[6:]
Just van Rossum4e6d13c2002-03-26 12:06:11 +0000396
397 if iconId:
Just van Rossum3c4dee42002-09-16 21:18:49 +0000398 try:
399 theIcon = Icn.GetCIcon(iconId)
400 except Icn.Error:
401 pass
402 else:
403 rect = (0, 0, 16, 16)
404 rect = Qd.OffsetRect(rect, l, t)
405 rect = Qd.OffsetRect(rect, 0, (theList.cellSize[1] - (rect[3] - rect[1])) / 2)
406 Icn.PlotCIcon(rect, theIcon)
Just van Rossum4e6d13c2002-03-26 12:06:11 +0000407
408 if len(key) >= 0:
409 cl, ct, cr, cb = cellRect
410 vl, vt, vr, vb = self._viewbounds
411 cl = vl + PICTWIDTH + indent
412 cr = vl + tab
413 if cr > vr:
414 cr = vr
415 if cl < cr:
416 drawTextCell(key, (cl, ct, cr, cb), ascent, theList)
417 cl = vl + tab
418 cr = vr
419 if cl < cr:
420 drawTextCell(value, (cl, ct, cr, cb), ascent, theList)
421 #elif dataLen != 0:
422 # drawTextCell("???", 3, cellRect, ascent, theList)
Just van Rossum3c4dee42002-09-16 21:18:49 +0000423 else:
424 return # we have bogus data
Just van Rossum4e6d13c2002-03-26 12:06:11 +0000425
426 # draw nice dotted line
427 l, t, r, b = cellRect
428 l = self._viewbounds[0] + tab
429 r = l + 1;
430 if not (theList.cellSize[1] & 0x01) or (t & 0x01):
431 myPat = "\xff\x00\xff\x00\xff\x00\xff\x00"
432 else:
433 myPat = "\x00\xff\x00\xff\x00\xff\x00\xff"
434 Qd.PenPat(myPat)
435 Qd.PenMode(QuickDraw.srcCopy)
436 Qd.PaintRect((l, t, r, b))
437 Qd.PenNormal()
438
439 if selected or onlyHilite:
440 l, t, r, b = cellRect
441 l = self._viewbounds[0] + PICTWIDTH
442 r = self._viewbounds[2]
443 Qd.PenMode(hilitetransfermode)
444 Qd.PaintRect((l, t, r, b))
445
446 # restore graphics environment
447 Qd.SetPort(savedPort)
448 Qd.SetClip(savedClip)
449 Qd.DisposeRgn(savedClip)
450 Qd.SetPenState(savedPenState)
451
452
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000453
454class Browser:
455
456 def __init__(self, object = None, title = None, closechildren = 0):
457 if hasattr(object, '__name__'):
458 name = object.__name__
459 else:
460 name = ''
461 if title is None:
462 title = 'Object browser'
463 if name:
464 title = title + ': ' + name
465 self.w = w = W.Window((300, 400), title, minsize = (100, 100))
466 w.info = W.TextBox((18, 8, -70, 15))
Just van Rossumf376ef02001-11-18 14:12:43 +0000467 w.updatebutton = W.BevelButton((-64, 4, 50, 16), 'Update', self.update)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000468 w.browser = BrowserWidget((-1, 24, 1, -14), None)
469 w.bind('cmdu', w.updatebutton.push)
470 w.open()
471 self.set(object, name)
472
473 def close(self):
474 if self.w.wid:
475 self.w.close()
476
477 def set(self, object, name = ''):
478 W.SetCursor('watch')
479 tp = type(object).__name__
480 try:
481 length = len(object)
482 except:
483 length = -1
484 if not name and hasattr(object, '__name__'):
485 name = object.__name__
486 if name:
487 info = name + ': ' + tp
488 else:
489 info = tp
490 if length >= 0:
491 if length == 1:
492 info = info + ' (%d element)' % length
493 else:
494 info = info + ' (%d elements)' % length
495 self.w.info.set(info)
496 self.w.browser.set(object)
497
498 def update(self):
499 self.w.browser.update()
500
501
502SIMPLE_TYPES = (
Just van Rossum927bc452002-12-01 22:10:36 +0000503 type(None),
504 int,
505 long,
506 float,
507 complex,
508 str,
509 unicode,
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000510)
511
Just van Rossum927bc452002-12-01 22:10:36 +0000512def get_ivars(obj):
513 """Return a list the names of all (potential) instance variables."""
514 # __mro__ recipe from Guido
515 slots = {}
516 # old-style C objects
517 if hasattr(obj, "__members__"):
518 for name in obj.__members__:
519 slots[name] = None
520 if hasattr(obj, "__methods__"):
521 for name in obj.__methods__:
522 slots[name] = None
523 # generic type
524 if hasattr(obj, "__dict__"):
525 slots.update(obj.__dict__)
526 cls = type(obj)
527 if hasattr(cls, "__mro__"):
528 # new-style class, use descriptors
529 for base in cls.__mro__:
530 for name, value in base.__dict__.items():
531 # XXX using callable() is a heuristic which isn't 100%
532 # foolproof.
533 if hasattr(value, "__get__") and not callable(value):
534 slots[name] = None
535 if "__dict__" in slots:
536 del slots["__dict__"]
537 slots = slots.keys()
538 slots.sort()
539 return slots
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000540
541def unpack_object(object, indent = 0):
542 tp = type(object)
Just van Rossum927bc452002-12-01 22:10:36 +0000543 if isinstance(object, SIMPLE_TYPES) and object is not None:
Just van Rossumdc3c6172001-06-19 21:37:33 +0000544 raise TypeError, "can't browse simple type: %s" % tp.__name__
Just van Rossum927bc452002-12-01 22:10:36 +0000545 elif isinstance(object, dict):
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000546 return unpack_dict(object, indent)
Just van Rossum927bc452002-12-01 22:10:36 +0000547 elif isinstance(object, (tuple, list)):
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000548 return unpack_sequence(object, indent)
Just van Rossum927bc452002-12-01 22:10:36 +0000549 elif isinstance(object, types.ModuleType):
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000550 return unpack_dict(object.__dict__, indent)
551 else:
552 return unpack_other(object, indent)
553
554def unpack_sequence(seq, indent = 0):
Just van Rossum71fd01c2002-12-13 11:07:20 +0000555 return [(i, v, not isinstance(v, SIMPLE_TYPES), indent)
556 for i, v in enumerate(seq)]
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000557
558def unpack_dict(dict, indent = 0):
559 items = dict.items()
560 return pack_items(items, indent)
561
562def unpack_instance(inst, indent = 0):
563 if hasattr(inst, '__pybrowse_unpack__'):
564 return unpack_object(inst.__pybrowse_unpack__(), indent)
565 else:
566 items = [('__class__', inst.__class__)] + inst.__dict__.items()
567 return pack_items(items, indent)
568
569def unpack_class(clss, indent = 0):
570 items = [('__bases__', clss.__bases__), ('__name__', clss.__name__)] + clss.__dict__.items()
571 return pack_items(items, indent)
572
573def unpack_other(object, indent = 0):
Just van Rossum927bc452002-12-01 22:10:36 +0000574 attrs = get_ivars(object)
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000575 items = []
576 for attr in attrs:
Just van Rossum927bc452002-12-01 22:10:36 +0000577 try:
578 value = getattr(object, attr)
579 except:
580 pass
581 else:
582 items.append((attr, value))
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000583 return pack_items(items, indent)
584
585def pack_items(items, indent = 0):
Just van Rossumfeddf772002-12-13 15:14:22 +0000586 items = [(k, v, not isinstance(v, SIMPLE_TYPES), indent)
587 for k, v in items]
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000588 return tuple_caselesssort(items)
589
590def caselesssort(alist):
591 """Return a sorted copy of a list. If there are only strings in the list,
592 it will not consider case"""
593
594 try:
595 # turn ['FOO', 'aaBc', 'ABcD'] into [('foo', 'FOO'), ('aabc', 'aaBc'), ('abcd', 'ABcD')], if possible
596 tupledlist = map(lambda item, lower = string.lower: (lower(item), item), alist)
597 except TypeError:
598 # at least one element in alist is not a string, proceed the normal way...
599 alist = alist[:]
600 alist.sort()
601 return alist
602 else:
603 tupledlist.sort()
604 # turn [('aabc', 'aaBc'), ('abcd', 'ABcD'), ('foo', 'FOO')] into ['aaBc', 'ABcD', 'FOO']
605 return map(lambda x: x[1], tupledlist)
606
607def tuple_caselesssort(items):
608 try:
609 tupledlist = map(lambda tuple, lower = string.lower: (lower(tuple[0]), tuple), items)
Just van Rossum6508c7c2000-10-20 06:34:57 +0000610 except (AttributeError, TypeError):
Just van Rossum40f9b7b1999-01-30 22:39:17 +0000611 items = items[:]
612 items.sort()
613 return items
614 else:
615 tupledlist.sort()
616 return map(lambda (low, tuple): tuple, tupledlist)
617