PythonUtil.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. import types
  2. import string
  3. import re
  4. import math
  5. import operator
  6. import inspect
  7. import os
  8. import sys
  9. import Verify
  10. # NOTE: ifAbsentPut has been replaced with Python's dictionary's builtin setdefault
  11. # before:
  12. # ifAbsentPut(dict, key, defaultValue)
  13. # after:
  14. # dict.setdefault(key, defaultValue)
  15. # Please use setdefault instead -- Joe
  16. def unique(L1, L2):
  17. """Return a list containing all items in 'L1' that are not in 'L2'"""
  18. L2 = dict([(k,None) for k in L2])
  19. return [item for item in L1 if item not in L2]
  20. def indent(stream, numIndents, str):
  21. """
  22. Write str to stream with numIndents in front of it
  23. """
  24. # To match emacs, instead of a tab character we will use 4 spaces
  25. stream.write(' ' * numIndents + str)
  26. def apropos(obj, *args):
  27. """
  28. Obsolete, use pdir
  29. """
  30. print 'Use pdir instead'
  31. def getClassLineage(obj):
  32. """ getClassLineage(obj): print object inheritance list """
  33. # Just a dictionary, return dictionary
  34. if type(obj) == types.DictionaryType:
  35. return [obj]
  36. # Instance, make a list with the instance and its class interitance
  37. elif type(obj) == types.InstanceType:
  38. return [obj] + getClassLineage(obj.__class__)
  39. # Class, see what it derives from
  40. elif type(obj) == types.ClassType:
  41. lineage = [obj]
  42. for c in obj.__bases__:
  43. lineage = lineage + getClassLineage(c)
  44. return lineage
  45. # Not what I'm looking for
  46. else:
  47. return []
  48. def pdir(obj, str = None, fOverloaded = 0, width = None,
  49. fTruncate = 1, lineWidth = 75, wantPrivate = 0):
  50. # Remove redundant class entries
  51. uniqueLineage = []
  52. for l in getClassLineage(obj):
  53. if type(l) == types.ClassType:
  54. if l in uniqueLineage:
  55. break
  56. uniqueLineage.append(l)
  57. # Pretty print out directory info
  58. uniqueLineage.reverse()
  59. for obj in uniqueLineage:
  60. _pdir(obj, str, fOverloaded, width, fTruncate, lineWidth, wantPrivate)
  61. print
  62. def _pdir(obj, str = None, fOverloaded = 0, width = None,
  63. fTruncate = 1, lineWidth = 75, wantPrivate = 0):
  64. """
  65. Print out a formatted list of members and methods of an instance or class
  66. """
  67. def printHeader(name):
  68. name = ' ' + name + ' '
  69. length = len(name)
  70. if length < 70:
  71. padBefore = int((70 - length)/2.0)
  72. padAfter = max(0,70 - length - padBefore)
  73. header = '*' * padBefore + name + '*' * padAfter
  74. print header
  75. print
  76. def printInstanceHeader(i, printHeader = printHeader):
  77. printHeader(i.__class__.__name__ + ' INSTANCE INFO')
  78. def printClassHeader(c, printHeader = printHeader):
  79. printHeader(c.__name__ + ' CLASS INFO')
  80. def printDictionaryHeader(d, printHeader = printHeader):
  81. printHeader('DICTIONARY INFO')
  82. # Print Header
  83. if type(obj) == types.InstanceType:
  84. printInstanceHeader(obj)
  85. elif type(obj) == types.ClassType:
  86. printClassHeader(obj)
  87. elif type (obj) == types.DictionaryType:
  88. printDictionaryHeader(obj)
  89. # Get dict
  90. if type(obj) == types.DictionaryType:
  91. dict = obj
  92. else:
  93. dict = obj.__dict__
  94. # Adjust width
  95. if width:
  96. maxWidth = width
  97. else:
  98. maxWidth = 10
  99. keyWidth = 0
  100. aproposKeys = []
  101. privateKeys = []
  102. remainingKeys = []
  103. for key in dict.keys():
  104. if not width:
  105. keyWidth = len(key)
  106. if str:
  107. if re.search(str, key, re.I):
  108. aproposKeys.append(key)
  109. if (not width) and (keyWidth > maxWidth):
  110. maxWidth = keyWidth
  111. else:
  112. if key[:1] == '_':
  113. if wantPrivate:
  114. privateKeys.append(key)
  115. if (not width) and (keyWidth > maxWidth):
  116. maxWidth = keyWidth
  117. else:
  118. remainingKeys.append(key)
  119. if (not width) and (keyWidth > maxWidth):
  120. maxWidth = keyWidth
  121. # Sort appropriate keys
  122. if str:
  123. aproposKeys.sort()
  124. else:
  125. privateKeys.sort()
  126. remainingKeys.sort()
  127. # Print out results
  128. if wantPrivate:
  129. keys = aproposKeys + privateKeys + remainingKeys
  130. else:
  131. keys = aproposKeys + remainingKeys
  132. format = '%-' + `maxWidth` + 's'
  133. for key in keys:
  134. value = dict[key]
  135. if callable(value):
  136. strvalue = `Signature(value)`
  137. else:
  138. strvalue = `value`
  139. if fTruncate:
  140. # Cut off line (keeping at least 1 char)
  141. strvalue = strvalue[:max(1,lineWidth - maxWidth)]
  142. print (format % key)[:maxWidth] + '\t' + strvalue
  143. # Magic numbers: These are the bit masks in func_code.co_flags that
  144. # reveal whether or not the function has a *arg or **kw argument.
  145. _POS_LIST = 4
  146. _KEY_DICT = 8
  147. def _is_variadic(function):
  148. return function.func_code.co_flags & _POS_LIST
  149. def _has_keywordargs(function):
  150. return function.func_code.co_flags & _KEY_DICT
  151. def _varnames(function):
  152. return function.func_code.co_varnames
  153. def _getcode(f):
  154. """
  155. _getcode(f)
  156. This function returns the name and function object of a callable
  157. object.
  158. """
  159. def method_get(f):
  160. return f.__name__, f.im_func
  161. def function_get(f):
  162. return f.__name__, f
  163. def instance_get(f):
  164. if hasattr(f, '__call__'):
  165. method = f.__call__
  166. if (type(method) == types.MethodType):
  167. func = method.im_func
  168. else:
  169. func = method
  170. return ("%s%s" % (f.__class__.__name__, '__call__'), func)
  171. else:
  172. s = ("Instance %s of class %s does not have a __call__ method" %
  173. (f, f.__class__.__name__))
  174. raise TypeError, s
  175. def class_get(f):
  176. if hasattr(f, '__init__'):
  177. return f.__name__, f.__init__.im_func
  178. else:
  179. return f.__name__, lambda: None
  180. codedict = { types.UnboundMethodType: method_get,
  181. types.MethodType : method_get,
  182. types.FunctionType : function_get,
  183. types.InstanceType : instance_get,
  184. types.ClassType : class_get,
  185. }
  186. try:
  187. return codedict[type(f)](f)
  188. except KeyError:
  189. if callable(f): # eg, built-in functions and methods
  190. # raise ValueError, "type %s not supported yet." % type(f)
  191. return f.__name__, None
  192. else:
  193. raise TypeError, ("object %s of type %s is not callable." %
  194. (f, type(f)))
  195. class Signature:
  196. def __init__(self, func):
  197. self.type = type(func)
  198. self.name, self.func = _getcode(func)
  199. def ordinary_args(self):
  200. n = self.func.func_code.co_argcount
  201. return _varnames(self.func)[0:n]
  202. def special_args(self):
  203. n = self.func.func_code.co_argcount
  204. x = {}
  205. #
  206. if _is_variadic(self.func):
  207. x['positional'] = _varnames(self.func)[n]
  208. if _has_keywordargs(self.func):
  209. x['keyword'] = _varnames(self.func)[n+1]
  210. elif _has_keywordargs(self.func):
  211. x['keyword'] = _varnames(self.func)[n]
  212. else:
  213. pass
  214. return x
  215. def full_arglist(self):
  216. base = list(self.ordinary_args())
  217. x = self.special_args()
  218. if x.has_key('positional'):
  219. base.append(x['positional'])
  220. if x.has_key('keyword'):
  221. base.append(x['keyword'])
  222. return base
  223. def defaults(self):
  224. defargs = self.func.func_defaults
  225. args = self.ordinary_args()
  226. mapping = {}
  227. if defargs is not None:
  228. for i in range(-1, -(len(defargs)+1), -1):
  229. mapping[args[i]] = defargs[i]
  230. else:
  231. pass
  232. return mapping
  233. def __repr__(self):
  234. if self.func:
  235. defaults = self.defaults()
  236. specials = self.special_args()
  237. l = []
  238. for arg in self.ordinary_args():
  239. if defaults.has_key(arg):
  240. l.append( arg + '=' + str(defaults[arg]) )
  241. else:
  242. l.append( arg )
  243. if specials.has_key('positional'):
  244. l.append( '*' + specials['positional'] )
  245. if specials.has_key('keyword'):
  246. l.append( '**' + specials['keyword'] )
  247. return "%s(%s)" % (self.name, string.join(l, ', '))
  248. else:
  249. return "%s(?)" % self.name
  250. def aproposAll(obj):
  251. """
  252. Print out a list of all members and methods (including overloaded methods)
  253. of an instance or class
  254. """
  255. apropos(obj, fOverloaded = 1, fTruncate = 0)
  256. def doc(obj):
  257. if (isinstance(obj, types.MethodType)) or \
  258. (isinstance(obj, types.FunctionType)):
  259. print obj.__doc__
  260. def adjust(command = None, dim = 1, parent = None, **kw):
  261. """
  262. adjust(command = None, parent = None, **kw)
  263. Popup and entry scale to adjust a parameter
  264. Accepts any Slider keyword argument. Typical arguments include:
  265. command: The one argument command to execute
  266. min: The min value of the slider
  267. max: The max value of the slider
  268. resolution: The resolution of the slider
  269. text: The label on the slider
  270. These values can be accessed and/or changed after the fact
  271. >>> vg = adjust()
  272. >>> vg['min']
  273. 0.0
  274. >>> vg['min'] = 10.0
  275. >>> vg['min']
  276. 10.0
  277. """
  278. # Make sure we enable Tk
  279. import Valuator
  280. # Set command if specified
  281. if command:
  282. kw['command'] = lambda x: apply(command, x)
  283. if parent is None:
  284. kw['title'] = command.__name__
  285. kw['dim'] = dim
  286. # Create toplevel if needed
  287. if not parent:
  288. vg = apply(Valuator.ValuatorGroupPanel, (parent,), kw)
  289. else:
  290. vg = apply(Valuator.ValuatorGroup,(parent,), kw)
  291. vg.pack(expand = 1, fill = 'x')
  292. return vg
  293. def intersection(a, b):
  294. """
  295. intersection(list, list):
  296. """
  297. if not a: return []
  298. if not b: return []
  299. d = []
  300. for i in a:
  301. if (i in b) and (i not in d):
  302. d.append(i)
  303. for i in b:
  304. if (i in a) and (i not in d):
  305. d.append(i)
  306. return d
  307. def union(a, b):
  308. """
  309. union(list, list):
  310. """
  311. # Copy a
  312. c = a[:]
  313. for i in b:
  314. if (i not in c):
  315. c.append(i)
  316. return c
  317. def sameElements(a, b):
  318. if len(a) != len(b):
  319. return 0
  320. for elem in a:
  321. if elem not in b:
  322. return 0
  323. for elem in b:
  324. if elem not in a:
  325. return 0
  326. return 1
  327. def list2dict(L, value=None):
  328. """creates dict using elements of list, all assigned to same value"""
  329. return dict([(k,value) for k in L])
  330. def uniqueElements(L):
  331. """are all elements of list unique?"""
  332. return len(L) == len(list2dict(L))
  333. def contains(whole, sub):
  334. """
  335. Return 1 if whole contains sub, 0 otherwise
  336. """
  337. if (whole == sub):
  338. return 1
  339. for elem in sub:
  340. # The first item you find not in whole, return 0
  341. if elem not in whole:
  342. return 0
  343. # If you got here, whole must contain sub
  344. return 1
  345. def replace(list, old, new, all=0):
  346. """
  347. replace 'old' with 'new' in 'list'
  348. if all == 0, replace first occurrence
  349. otherwise replace all occurrences
  350. returns the number of items replaced
  351. """
  352. if old not in list:
  353. return 0
  354. if not all:
  355. i = list.index(old)
  356. list[i] = new
  357. return 1
  358. else:
  359. numReplaced = 0
  360. for i in xrange(len(list)):
  361. if list[i] == old:
  362. numReplaced += 1
  363. list[i] = new
  364. return numReplaced
  365. def reduceAngle(deg):
  366. """
  367. Reduces an angle (in degrees) to a value in [-180..180)
  368. """
  369. return (((deg + 180.) % 360.) - 180.)
  370. def fitSrcAngle2Dest(src, dest):
  371. """
  372. given a src and destination angle, returns an equivalent src angle
  373. that is within [-180..180) of dest
  374. examples:
  375. fitSrcAngle2Dest(30,60) == 30
  376. fitSrcAngle2Dest(60,30) == 60
  377. fitSrcAngle2Dest(0,180) == 0
  378. fitSrcAngle2Dest(-1,180) == 359
  379. fitSrcAngle2Dest(-180,180) == 180
  380. """
  381. return dest + reduceAngle(src - dest)
  382. def fitDestAngle2Src(src, dest):
  383. """
  384. given a src and destination angle, returns an equivalent dest angle
  385. that is within [-180..180) of src
  386. examples:
  387. fitDestAngle2Src(30,60) == 60
  388. fitDestAngle2Src(60,30) == 30
  389. fitDestAngle2Src(0,180) == -180
  390. fitDestAngle2Src(1,180) == 180
  391. """
  392. return src + (reduceAngle(dest - src))
  393. def closestDestAngle2(src, dest):
  394. # The function above didn't seem to do what I wanted. So I hacked
  395. # this one together. I can't really say I understand it. It's more
  396. # from impirical observation... GRW
  397. diff = src - dest
  398. # if the difference is greater that 180 it's shorter to go the other way
  399. if diff > 180:
  400. return dest - 360
  401. # or perhaps the OTHER other way...
  402. elif diff < -180:
  403. return dest + 360
  404. # otherwise just go to the original destination
  405. else:
  406. return dest
  407. def closestDestAngle(src, dest):
  408. # The function above didn't seem to do what I wanted. So I hacked
  409. # this one together. I can't really say I understand it. It's more
  410. # from impirical observation... GRW
  411. diff = src - dest
  412. # if the difference is greater that 180 it's shorter to go the other way
  413. if diff > 180:
  414. return src - (diff - 360)
  415. # or perhaps the OTHER other way...
  416. elif diff < -180:
  417. return src - (360 + diff)
  418. # otherwise just go to the original destination
  419. else:
  420. return dest
  421. def binaryRepr(number, max_length = 32):
  422. # This will only work reliably for relatively small numbers.
  423. # Increase the value of max_length if you think you're going
  424. # to use long integers
  425. assert number < 2L << max_length
  426. shifts = map (operator.rshift, max_length * [number], \
  427. range (max_length - 1, -1, -1))
  428. digits = map (operator.mod, shifts, max_length * [2])
  429. if not digits.count (1): return 0
  430. digits = digits [digits.index (1):]
  431. return string.join (map (repr, digits), '')
  432. # constant profile defaults
  433. PyUtilProfileDefaultFilename = 'profiledata'
  434. PyUtilProfileDefaultLines = 80
  435. PyUtilProfileDefaultSorts = ['cumulative', 'time', 'calls']
  436. # call this from the prompt, and break back out to the prompt
  437. # to stop profiling
  438. #
  439. # OR to do inline profiling, you must make a globally-visible
  440. # function to be profiled, i.e. to profile 'self.load()', do
  441. # something like this:
  442. #
  443. # def func(self=self):
  444. # self.load()
  445. # import __builtin__
  446. # __builtin__.func = func
  447. # PythonUtil.startProfile(cmd='func()', filename='loadProfile')
  448. # del __builtin__.func
  449. #
  450. def startProfile(filename=PyUtilProfileDefaultFilename,
  451. lines=PyUtilProfileDefaultLines,
  452. sorts=PyUtilProfileDefaultSorts,
  453. silent=0,
  454. cmd='run()'):
  455. import profile
  456. profile.run(cmd, filename)
  457. if not silent:
  458. printProfile(filename, lines, sorts)
  459. # call this to see the results again
  460. def printProfile(filename=PyUtilProfileDefaultFilename,
  461. lines=PyUtilProfileDefaultLines,
  462. sorts=PyUtilProfileDefaultSorts,):
  463. import pstats
  464. s = pstats.Stats(filename)
  465. s.strip_dirs()
  466. for sort in sorts:
  467. s.sort_stats(sort)
  468. s.print_stats(lines)
  469. class Functor:
  470. def __init__(self, function, *args, **kargs):
  471. assert callable(function), "function should be a callable obj"
  472. self._function = function
  473. self._args = args
  474. self._kargs = kargs
  475. self.__name__ = 'Functor: %s' % self._function.__name__
  476. self.__doc__ = self._function.__doc__
  477. def __call__(self, *args, **kargs):
  478. """call function"""
  479. _args = list(self._args)
  480. _args.extend(args)
  481. _kargs = self._kargs.copy()
  482. _kargs.update(kargs)
  483. return apply(self._function,_args,_kargs)
  484. def bound(value, bound1, bound2):
  485. """
  486. returns value if value is between bound1 and bound2
  487. otherwise returns bound that is closer to value
  488. """
  489. if bound1 > bound2:
  490. return min(max(value, bound2), bound1)
  491. else:
  492. return min(max(value, bound1), bound2)
  493. def lerp(v0, v1, t):
  494. """
  495. returns a value lerped between v0 and v1, according to t
  496. t == 0 maps to v0, t == 1 maps to v1
  497. """
  498. return v0 + (t * (v1 - v0))
  499. def boolEqual(a, b):
  500. """
  501. returns true if a and b are both true or both false.
  502. returns false otherwise
  503. """
  504. return (a and b) or not (a or b)
  505. def lineupPos(i, num, spacing):
  506. """
  507. use to line up a series of 'num' objects, in one dimension,
  508. centered around zero
  509. 'i' is the index of the object in the lineup
  510. 'spacing' is the amount of space between objects in the lineup
  511. """
  512. assert num >= 1
  513. assert i >= 0 and i < num
  514. pos = float(i) * spacing
  515. return pos - ((float(spacing) * (num-1))/2.)
  516. def formatElapsedSeconds(seconds):
  517. """
  518. Returns a string of the form "mm:ss" or "hh:mm:ss" or "n days",
  519. representing the indicated elapsed time in seconds.
  520. """
  521. sign = ''
  522. if seconds < 0:
  523. seconds = -seconds
  524. sign = '-'
  525. # We use math.floor() instead of casting to an int, so we avoid
  526. # problems with numbers that are too large to represent as
  527. # type int.
  528. seconds = math.floor(seconds)
  529. hours = math.floor(seconds / (60 * 60))
  530. if hours > 36:
  531. days = math.floor((hours + 12) / 24)
  532. return "%s%d days" % (sign, days)
  533. seconds -= hours * (60 * 60)
  534. minutes = (int)(seconds / 60)
  535. seconds -= minutes * 60
  536. if hours != 0:
  537. return "%s%d:%02d:%02d" % (sign, hours, minutes, seconds)
  538. else:
  539. return "%s%d:%02d" % (sign, minutes, seconds)
  540. def solveQuadratic(a, b, c):
  541. # quadratic equation: ax^2 + bx + c = 0
  542. # quadratic formula: x = [-b +/- sqrt(b^2 - 4ac)] / 2a
  543. # returns None, root, or [root1, root2]
  544. # a cannot be zero.
  545. if a == 0.:
  546. return None
  547. # calculate the determinant (b^2 - 4ac)
  548. D = (b * b) - (4. * a * c)
  549. if D < 0:
  550. # there are no solutions (sqrt(negative number) is undefined)
  551. return None
  552. elif D == 0:
  553. # only one root
  554. return (-b) / (2. * a)
  555. else:
  556. # OK, there are two roots
  557. sqrtD = math.sqrt(D)
  558. twoA = 2. * a
  559. root1 = ((-b) - sqrtD) / twoA
  560. root2 = ((-b) + sqrtD) / twoA
  561. return [root1, root2]
  562. def stackEntryInfo(depth=0, baseFileName=1):
  563. """
  564. returns the sourcefilename, line number, and function name of
  565. an entry in the stack.
  566. 'depth' is how far back to go in the stack; 0 is the caller of this
  567. function, 1 is the function that called the caller of this function, etc.
  568. by default, strips off the path of the filename; override with baseFileName
  569. returns (fileName, lineNum, funcName) --> (string, int, string)
  570. returns (None, None, None) on error
  571. """
  572. try:
  573. stack = None
  574. frame = None
  575. try:
  576. stack = inspect.stack()
  577. # add one to skip the frame associated with this function
  578. frame = stack[depth+1]
  579. filename = frame[1]
  580. if baseFileName:
  581. filename = os.path.basename(filename)
  582. lineNum = frame[2]
  583. funcName = frame[3]
  584. result = (filename, lineNum, funcName)
  585. finally:
  586. del stack
  587. del frame
  588. except:
  589. result = (None, None, None)
  590. return result
  591. def lineInfo(baseFileName=1):
  592. """
  593. returns the sourcefilename, line number, and function name of the
  594. code that called this function
  595. (answers the question: 'hey lineInfo, where am I in the codebase?')
  596. see stackEntryInfo, above, for info on 'baseFileName' and return types
  597. """
  598. return stackEntryInfo(1)
  599. def callerInfo(baseFileName=1):
  600. """
  601. returns the sourcefilename, line number, and function name of the
  602. caller of the function that called this function
  603. (answers the question: 'hey callerInfo, who called me?')
  604. see stackEntryInfo, above, for info on 'baseFileName' and return types
  605. """
  606. return stackEntryInfo(2)
  607. def lineTag(baseFileName=1, verbose=0, separator=':'):
  608. """
  609. returns a string containing the sourcefilename and line number
  610. of the code that called this function
  611. (equivalent to lineInfo, above, with different return type)
  612. see stackEntryInfo, above, for info on 'baseFileName'
  613. if 'verbose' is false, returns a compact string of the form
  614. 'fileName:lineNum:funcName'
  615. if 'verbose' is true, returns a longer string that matches the
  616. format of Python stack trace dumps
  617. returns empty string on error
  618. """
  619. fileName, lineNum, funcName = callerInfo()
  620. if fileName is None:
  621. return ''
  622. if verbose:
  623. return 'File "%s", line %s, in %s' % (fileName, lineNum, funcName)
  624. else:
  625. return '%s%s%s%s%s' % (fileName, separator, lineNum, separator,
  626. funcName)
  627. def findPythonModule(module):
  628. # Look along the python load path for the indicated filename.
  629. # Returns the located pathname, or None if the filename is not
  630. # found.
  631. filename = module + '.py'
  632. for dir in sys.path:
  633. pathname = os.path.join(dir, filename)
  634. if os.path.exists(pathname):
  635. return pathname
  636. return None
  637. def describeException(backTrace = 4):
  638. # When called in an exception handler, returns a string describing
  639. # the current exception.
  640. def byteOffsetToLineno(code, byte):
  641. # Returns the source line number corresponding to the given byte
  642. # offset into the indicated Python code module.
  643. import array
  644. lnotab = array.array('B', code.co_lnotab)
  645. line = code.co_firstlineno
  646. for i in range(0, len(lnotab),2):
  647. byte -= lnotab[i]
  648. if byte <= 0:
  649. return line
  650. line += lnotab[i+1]
  651. return line
  652. infoArr = sys.exc_info()
  653. exception = infoArr[0]
  654. exceptionName = getattr(exception, '__name__', None)
  655. extraInfo = infoArr[1]
  656. trace = infoArr[2]
  657. stack = []
  658. while trace.tb_next:
  659. # We need to call byteOffsetToLineno to determine the true
  660. # line number at which the exception occurred, even though we
  661. # have both trace.tb_lineno and frame.f_lineno, which return
  662. # the correct line number only in non-optimized mode.
  663. frame = trace.tb_frame
  664. module = frame.f_globals.get('__name__', None)
  665. lineno = byteOffsetToLineno(frame.f_code, frame.f_lasti)
  666. stack.append("%s:%s, " % (module, lineno))
  667. trace = trace.tb_next
  668. frame = trace.tb_frame
  669. module = frame.f_globals.get('__name__', None)
  670. lineno = byteOffsetToLineno(frame.f_code, frame.f_lasti)
  671. stack.append("%s:%s, " % (module, lineno))
  672. description = ""
  673. for i in range(len(stack) - 1, max(len(stack) - backTrace, 0) - 1, -1):
  674. description += stack[i]
  675. description += "%s: %s" % (exceptionName, extraInfo)
  676. return description
  677. def mostDerivedLast(classList):
  678. """pass in list of classes. sorts list in-place, with derived classes
  679. appearing after their bases"""
  680. def compare(a,b):
  681. if issubclass(a,b):
  682. result=1
  683. elif issubclass(b,a):
  684. result=-1
  685. else:
  686. result=0
  687. #print a,b,result
  688. return result
  689. classList.sort(compare)