PythonUtil.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  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 random
  10. if __debug__:
  11. import traceback
  12. from direct.directutil import Verify
  13. # NOTE: ifAbsentPut has been replaced with Python's dictionary's builtin setdefault
  14. # before:
  15. # ifAbsentPut(dict, key, defaultValue)
  16. # after:
  17. # dict.setdefault(key, defaultValue)
  18. # Please use setdefault instead -- Joe
  19. def unique(L1, L2):
  20. """Return a list containing all items in 'L1' that are not in 'L2'"""
  21. L2 = dict([(k,None) for k in L2])
  22. return [item for item in L1 if item not in L2]
  23. def indent(stream, numIndents, str):
  24. """
  25. Write str to stream with numIndents in front of it
  26. """
  27. # To match emacs, instead of a tab character we will use 4 spaces
  28. stream.write(' ' * numIndents + str)
  29. def writeFsmTree(instance, indent = 0):
  30. if hasattr(instance, 'parentFSM'):
  31. writeFsmTree(instance.parentFSM, indent-2)
  32. elif hasattr(instance, 'fsm'):
  33. name = ''
  34. if hasattr(instance.fsm, 'state'):
  35. name = instance.fsm.state.name
  36. print "%s: %s"%(instance.fsm.name, name)
  37. if __debug__:
  38. class StackTrace:
  39. def __init__(self, label="", start=0, limit=None):
  40. """
  41. label is a string (or anything that be be a string)
  42. that is printed as part of the trace back.
  43. This is just to make it easier to tell what the
  44. stack trace is referring to.
  45. start is an integer number of stack frames back
  46. from the most recent. (This is automatically
  47. bumped up by one to skip the __init__ call
  48. to the StackTrace).
  49. limit is an integer number of stack frames
  50. to record (or None for unlimited).
  51. """
  52. self.label = label
  53. self.trace = traceback.extract_stack(sys._getframe(1+start), limit=10)
  54. def __str__(self):
  55. r = "Debug stack trace of %s (back %s frames):\n"%(
  56. self.label, len(self.trace),)
  57. for i in traceback.format_list(self.trace):
  58. r+=i
  59. return r
  60. def traceFunctionCall(frame):
  61. """
  62. return a string that shows the call frame with calling arguments.
  63. e.g.
  64. foo(x=234, y=135)
  65. """
  66. f = frame
  67. co = f.f_code
  68. dict = f.f_locals
  69. n = co.co_argcount
  70. if co.co_flags & 4: n = n+1
  71. if co.co_flags & 8: n = n+1
  72. r=f.f_code.co_name+'('
  73. comma=0 # formatting, whether we should type a comma.
  74. for i in range(n):
  75. name = co.co_varnames[i]
  76. if name=='self':
  77. continue
  78. if comma:
  79. r+=', '
  80. else:
  81. # ok, we skipped the first one, the rest get commas:
  82. comma=1
  83. r+=name
  84. r+='='
  85. if dict.has_key(name):
  86. v=str(dict[name])
  87. if len(v)>200:
  88. r+="<too big for debug>"
  89. else:
  90. r+=str(dict[name])
  91. else: r+="*** undefined ***"
  92. return r+')'
  93. def traceParentCall():
  94. return traceFunctionCall(sys._getframe(2))
  95. def printThisCall():
  96. print traceFunctionCall(sys._getframe(1))
  97. return 1 # to allow "assert printThisCall()"
  98. def tron():
  99. sys.settrace(trace)
  100. def trace(frame, event, arg):
  101. if event == 'line':
  102. pass
  103. elif event == 'call':
  104. print traceFunctionCall(sys._getframe(1))
  105. elif event == 'return':
  106. print "returning"
  107. elif event == 'exception':
  108. print "exception"
  109. return trace
  110. def troff():
  111. sys.settrace(None)
  112. def apropos(obj, *args):
  113. """
  114. Obsolete, use pdir
  115. """
  116. print 'Use pdir instead'
  117. def getClassLineage(obj):
  118. """
  119. print object inheritance list
  120. """
  121. if type(obj) == types.DictionaryType:
  122. # Just a dictionary, return dictionary
  123. return [obj]
  124. elif type(obj) == types.InstanceType:
  125. # Instance, make a list with the instance and its class interitance
  126. return [obj] + getClassLineage(obj.__class__)
  127. elif type(obj) == types.ClassType:
  128. # Class, see what it derives from
  129. lineage = [obj]
  130. for c in obj.__bases__:
  131. lineage = lineage + getClassLineage(c)
  132. return lineage
  133. else:
  134. # Not what I'm looking for
  135. return []
  136. def pdir(obj, str = None, fOverloaded = 0, width = None,
  137. fTruncate = 1, lineWidth = 75, wantPrivate = 0):
  138. # Remove redundant class entries
  139. uniqueLineage = []
  140. for l in getClassLineage(obj):
  141. if type(l) == types.ClassType:
  142. if l in uniqueLineage:
  143. break
  144. uniqueLineage.append(l)
  145. # Pretty print out directory info
  146. uniqueLineage.reverse()
  147. for obj in uniqueLineage:
  148. _pdir(obj, str, fOverloaded, width, fTruncate, lineWidth, wantPrivate)
  149. print
  150. def _pdir(obj, str = None, fOverloaded = 0, width = None,
  151. fTruncate = 1, lineWidth = 75, wantPrivate = 0):
  152. """
  153. Print out a formatted list of members and methods of an instance or class
  154. """
  155. def printHeader(name):
  156. name = ' ' + name + ' '
  157. length = len(name)
  158. if length < 70:
  159. padBefore = int((70 - length)/2.0)
  160. padAfter = max(0,70 - length - padBefore)
  161. header = '*' * padBefore + name + '*' * padAfter
  162. print header
  163. print
  164. def printInstanceHeader(i, printHeader = printHeader):
  165. printHeader(i.__class__.__name__ + ' INSTANCE INFO')
  166. def printClassHeader(c, printHeader = printHeader):
  167. printHeader(c.__name__ + ' CLASS INFO')
  168. def printDictionaryHeader(d, printHeader = printHeader):
  169. printHeader('DICTIONARY INFO')
  170. # Print Header
  171. if type(obj) == types.InstanceType:
  172. printInstanceHeader(obj)
  173. elif type(obj) == types.ClassType:
  174. printClassHeader(obj)
  175. elif type (obj) == types.DictionaryType:
  176. printDictionaryHeader(obj)
  177. # Get dict
  178. if type(obj) == types.DictionaryType:
  179. dict = obj
  180. else:
  181. dict = obj.__dict__
  182. # Adjust width
  183. if width:
  184. maxWidth = width
  185. else:
  186. maxWidth = 10
  187. keyWidth = 0
  188. aproposKeys = []
  189. privateKeys = []
  190. remainingKeys = []
  191. for key in dict.keys():
  192. if not width:
  193. keyWidth = len(key)
  194. if str:
  195. if re.search(str, key, re.I):
  196. aproposKeys.append(key)
  197. if (not width) and (keyWidth > maxWidth):
  198. maxWidth = keyWidth
  199. else:
  200. if key[:1] == '_':
  201. if wantPrivate:
  202. privateKeys.append(key)
  203. if (not width) and (keyWidth > maxWidth):
  204. maxWidth = keyWidth
  205. else:
  206. remainingKeys.append(key)
  207. if (not width) and (keyWidth > maxWidth):
  208. maxWidth = keyWidth
  209. # Sort appropriate keys
  210. if str:
  211. aproposKeys.sort()
  212. else:
  213. privateKeys.sort()
  214. remainingKeys.sort()
  215. # Print out results
  216. if wantPrivate:
  217. keys = aproposKeys + privateKeys + remainingKeys
  218. else:
  219. keys = aproposKeys + remainingKeys
  220. format = '%-' + `maxWidth` + 's'
  221. for key in keys:
  222. value = dict[key]
  223. if callable(value):
  224. strvalue = `Signature(value)`
  225. else:
  226. strvalue = `value`
  227. if fTruncate:
  228. # Cut off line (keeping at least 1 char)
  229. strvalue = strvalue[:max(1,lineWidth - maxWidth)]
  230. print (format % key)[:maxWidth] + '\t' + strvalue
  231. # Magic numbers: These are the bit masks in func_code.co_flags that
  232. # reveal whether or not the function has a *arg or **kw argument.
  233. _POS_LIST = 4
  234. _KEY_DICT = 8
  235. def _is_variadic(function):
  236. return function.func_code.co_flags & _POS_LIST
  237. def _has_keywordargs(function):
  238. return function.func_code.co_flags & _KEY_DICT
  239. def _varnames(function):
  240. return function.func_code.co_varnames
  241. def _getcode(f):
  242. """
  243. _getcode(f)
  244. This function returns the name and function object of a callable
  245. object.
  246. """
  247. def method_get(f):
  248. return f.__name__, f.im_func
  249. def function_get(f):
  250. return f.__name__, f
  251. def instance_get(f):
  252. if hasattr(f, '__call__'):
  253. method = f.__call__
  254. if (type(method) == types.MethodType):
  255. func = method.im_func
  256. else:
  257. func = method
  258. return ("%s%s" % (f.__class__.__name__, '__call__'), func)
  259. else:
  260. s = ("Instance %s of class %s does not have a __call__ method" %
  261. (f, f.__class__.__name__))
  262. raise TypeError, s
  263. def class_get(f):
  264. if hasattr(f, '__init__'):
  265. return f.__name__, f.__init__.im_func
  266. else:
  267. return f.__name__, lambda: None
  268. codedict = { types.UnboundMethodType: method_get,
  269. types.MethodType : method_get,
  270. types.FunctionType : function_get,
  271. types.InstanceType : instance_get,
  272. types.ClassType : class_get,
  273. }
  274. try:
  275. return codedict[type(f)](f)
  276. except KeyError:
  277. if callable(f): # eg, built-in functions and methods
  278. # raise ValueError, "type %s not supported yet." % type(f)
  279. return f.__name__, None
  280. else:
  281. raise TypeError, ("object %s of type %s is not callable." %
  282. (f, type(f)))
  283. class Signature:
  284. def __init__(self, func):
  285. self.type = type(func)
  286. self.name, self.func = _getcode(func)
  287. def ordinary_args(self):
  288. n = self.func.func_code.co_argcount
  289. return _varnames(self.func)[0:n]
  290. def special_args(self):
  291. n = self.func.func_code.co_argcount
  292. x = {}
  293. #
  294. if _is_variadic(self.func):
  295. x['positional'] = _varnames(self.func)[n]
  296. if _has_keywordargs(self.func):
  297. x['keyword'] = _varnames(self.func)[n+1]
  298. elif _has_keywordargs(self.func):
  299. x['keyword'] = _varnames(self.func)[n]
  300. else:
  301. pass
  302. return x
  303. def full_arglist(self):
  304. base = list(self.ordinary_args())
  305. x = self.special_args()
  306. if x.has_key('positional'):
  307. base.append(x['positional'])
  308. if x.has_key('keyword'):
  309. base.append(x['keyword'])
  310. return base
  311. def defaults(self):
  312. defargs = self.func.func_defaults
  313. args = self.ordinary_args()
  314. mapping = {}
  315. if defargs is not None:
  316. for i in range(-1, -(len(defargs)+1), -1):
  317. mapping[args[i]] = defargs[i]
  318. else:
  319. pass
  320. return mapping
  321. def __repr__(self):
  322. if self.func:
  323. defaults = self.defaults()
  324. specials = self.special_args()
  325. l = []
  326. for arg in self.ordinary_args():
  327. if defaults.has_key(arg):
  328. l.append( arg + '=' + str(defaults[arg]) )
  329. else:
  330. l.append( arg )
  331. if specials.has_key('positional'):
  332. l.append( '*' + specials['positional'] )
  333. if specials.has_key('keyword'):
  334. l.append( '**' + specials['keyword'] )
  335. return "%s(%s)" % (self.name, string.join(l, ', '))
  336. else:
  337. return "%s(?)" % self.name
  338. def aproposAll(obj):
  339. """
  340. Print out a list of all members and methods (including overloaded methods)
  341. of an instance or class
  342. """
  343. apropos(obj, fOverloaded = 1, fTruncate = 0)
  344. def doc(obj):
  345. if (isinstance(obj, types.MethodType)) or \
  346. (isinstance(obj, types.FunctionType)):
  347. print obj.__doc__
  348. def adjust(command = None, dim = 1, parent = None, **kw):
  349. """
  350. adjust(command = None, parent = None, **kw)
  351. Popup and entry scale to adjust a parameter
  352. Accepts any Slider keyword argument. Typical arguments include:
  353. command: The one argument command to execute
  354. min: The min value of the slider
  355. max: The max value of the slider
  356. resolution: The resolution of the slider
  357. text: The label on the slider
  358. These values can be accessed and/or changed after the fact
  359. >>> vg = adjust()
  360. >>> vg['min']
  361. 0.0
  362. >>> vg['min'] = 10.0
  363. >>> vg['min']
  364. 10.0
  365. """
  366. # Make sure we enable Tk
  367. from direct.tkwidgets import Valuator
  368. # Set command if specified
  369. if command:
  370. kw['command'] = lambda x: apply(command, x)
  371. if parent is None:
  372. kw['title'] = command.__name__
  373. kw['dim'] = dim
  374. # Create toplevel if needed
  375. if not parent:
  376. vg = apply(Valuator.ValuatorGroupPanel, (parent,), kw)
  377. else:
  378. vg = apply(Valuator.ValuatorGroup,(parent,), kw)
  379. vg.pack(expand = 1, fill = 'x')
  380. return vg
  381. def intersection(a, b):
  382. """
  383. intersection(list, list):
  384. """
  385. if not a: return []
  386. if not b: return []
  387. d = []
  388. for i in a:
  389. if (i in b) and (i not in d):
  390. d.append(i)
  391. for i in b:
  392. if (i in a) and (i not in d):
  393. d.append(i)
  394. return d
  395. def union(a, b):
  396. """
  397. union(list, list):
  398. """
  399. # Copy a
  400. c = a[:]
  401. for i in b:
  402. if (i not in c):
  403. c.append(i)
  404. return c
  405. def sameElements(a, b):
  406. if len(a) != len(b):
  407. return 0
  408. for elem in a:
  409. if elem not in b:
  410. return 0
  411. for elem in b:
  412. if elem not in a:
  413. return 0
  414. return 1
  415. def list2dict(L, value=None):
  416. """creates dict using elements of list, all assigned to same value"""
  417. return dict([(k,value) for k in L])
  418. def invertDict(D):
  419. """creates a dictionary by 'inverting' D; keys are placed in the new
  420. dictionary under their corresponding value in the old dictionary.
  421. Data will be lost if D contains any duplicate values.
  422. >>> old = {'key1':1, 'key2':2}
  423. >>> invertDict(old)
  424. {1: 'key1', 2: 'key2'}
  425. """
  426. n = {}
  427. for key, value in D.items():
  428. n[value] = key
  429. return n
  430. def invertDictLossless(D):
  431. """similar to invertDict, but values of new dict are lists of keys from
  432. old dict. No information is lost.
  433. >>> old = {'key1':1, 'key2':2, 'keyA':2}
  434. >>> invertDictLossless(old)
  435. {1: ['key1'], 2: ['key2', 'keyA']}
  436. """
  437. n = {}
  438. for key, value in D.items():
  439. n.setdefault(value, [])
  440. n[value].append(key)
  441. return n
  442. def uniqueElements(L):
  443. """are all elements of list unique?"""
  444. return len(L) == len(list2dict(L))
  445. def disjoint(L1, L2):
  446. """returns non-zero if L1 and L2 have no common elements"""
  447. used = dict([(k,None) for k in L1])
  448. for k in L2:
  449. if k in used:
  450. return 0
  451. return 1
  452. def contains(whole, sub):
  453. """
  454. Return 1 if whole contains sub, 0 otherwise
  455. """
  456. if (whole == sub):
  457. return 1
  458. for elem in sub:
  459. # The first item you find not in whole, return 0
  460. if elem not in whole:
  461. return 0
  462. # If you got here, whole must contain sub
  463. return 1
  464. def replace(list, old, new, all=0):
  465. """
  466. replace 'old' with 'new' in 'list'
  467. if all == 0, replace first occurrence
  468. otherwise replace all occurrences
  469. returns the number of items replaced
  470. """
  471. if old not in list:
  472. return 0
  473. if not all:
  474. i = list.index(old)
  475. list[i] = new
  476. return 1
  477. else:
  478. numReplaced = 0
  479. for i in xrange(len(list)):
  480. if list[i] == old:
  481. numReplaced += 1
  482. list[i] = new
  483. return numReplaced
  484. def reduceAngle(deg):
  485. """
  486. Reduces an angle (in degrees) to a value in [-180..180)
  487. """
  488. return (((deg + 180.) % 360.) - 180.)
  489. def fitSrcAngle2Dest(src, dest):
  490. """
  491. given a src and destination angle, returns an equivalent src angle
  492. that is within [-180..180) of dest
  493. examples:
  494. fitSrcAngle2Dest(30,60) == 30
  495. fitSrcAngle2Dest(60,30) == 60
  496. fitSrcAngle2Dest(0,180) == 0
  497. fitSrcAngle2Dest(-1,180) == 359
  498. fitSrcAngle2Dest(-180,180) == 180
  499. """
  500. return dest + reduceAngle(src - dest)
  501. def fitDestAngle2Src(src, dest):
  502. """
  503. given a src and destination angle, returns an equivalent dest angle
  504. that is within [-180..180) of src
  505. examples:
  506. fitDestAngle2Src(30,60) == 60
  507. fitDestAngle2Src(60,30) == 30
  508. fitDestAngle2Src(0,180) == -180
  509. fitDestAngle2Src(1,180) == 180
  510. """
  511. return src + (reduceAngle(dest - src))
  512. def closestDestAngle2(src, dest):
  513. # The function above didn't seem to do what I wanted. So I hacked
  514. # this one together. I can't really say I understand it. It's more
  515. # from impirical observation... GRW
  516. diff = src - dest
  517. if diff > 180:
  518. # if the difference is greater that 180 it's shorter to go the other way
  519. return dest - 360
  520. elif diff < -180:
  521. # or perhaps the OTHER other way...
  522. return dest + 360
  523. else:
  524. # otherwise just go to the original destination
  525. return dest
  526. def closestDestAngle(src, dest):
  527. # The function above didn't seem to do what I wanted. So I hacked
  528. # this one together. I can't really say I understand it. It's more
  529. # from impirical observation... GRW
  530. diff = src - dest
  531. if diff > 180:
  532. # if the difference is greater that 180 it's shorter to go the other way
  533. return src - (diff - 360)
  534. elif diff < -180:
  535. # or perhaps the OTHER other way...
  536. return src - (360 + diff)
  537. else:
  538. # otherwise just go to the original destination
  539. return dest
  540. def binaryRepr(number, max_length = 32):
  541. # This will only work reliably for relatively small numbers.
  542. # Increase the value of max_length if you think you're going
  543. # to use long integers
  544. assert number < 2L << max_length
  545. shifts = map (operator.rshift, max_length * [number], \
  546. range (max_length - 1, -1, -1))
  547. digits = map (operator.mod, shifts, max_length * [2])
  548. if not digits.count (1): return 0
  549. digits = digits [digits.index (1):]
  550. return string.join (map (repr, digits), '')
  551. # constant profile defaults
  552. PyUtilProfileDefaultFilename = 'profiledata'
  553. PyUtilProfileDefaultLines = 80
  554. PyUtilProfileDefaultSorts = ['cumulative', 'time', 'calls']
  555. # call this from the prompt, and break back out to the prompt
  556. # to stop profiling
  557. #
  558. # OR to do inline profiling, you must make a globally-visible
  559. # function to be profiled, i.e. to profile 'self.load()', do
  560. # something like this:
  561. #
  562. # def func(self=self):
  563. # self.load()
  564. # import __builtin__
  565. # __builtin__.func = func
  566. # PythonUtil.startProfile(cmd='func()', filename='profileData')
  567. # del __builtin__.func
  568. #
  569. def startProfile(filename=PyUtilProfileDefaultFilename,
  570. lines=PyUtilProfileDefaultLines,
  571. sorts=PyUtilProfileDefaultSorts,
  572. silent=0,
  573. callInfo=1,
  574. cmd='run()'):
  575. import profile
  576. profile.run(cmd, filename)
  577. if not silent:
  578. printProfile(filename, lines, sorts, callInfo)
  579. # call this to see the results again
  580. def printProfile(filename=PyUtilProfileDefaultFilename,
  581. lines=PyUtilProfileDefaultLines,
  582. sorts=PyUtilProfileDefaultSorts,
  583. callInfo=1):
  584. import pstats
  585. s = pstats.Stats(filename)
  586. s.strip_dirs()
  587. for sort in sorts:
  588. s.sort_stats(sort)
  589. s.print_stats(lines)
  590. if callInfo:
  591. s.print_callees(lines)
  592. s.print_callers(lines)
  593. class Functor:
  594. def __init__(self, function, *args, **kargs):
  595. assert callable(function), "function should be a callable obj"
  596. self._function = function
  597. self._args = args
  598. self._kargs = kargs
  599. self.__name__ = 'Functor: %s' % self._function.__name__
  600. self.__doc__ = self._function.__doc__
  601. def __call__(self, *args, **kargs):
  602. """call function"""
  603. _args = list(self._args)
  604. _args.extend(args)
  605. _kargs = self._kargs.copy()
  606. _kargs.update(kargs)
  607. return apply(self._function,_args,_kargs)
  608. def bound(value, bound1, bound2):
  609. """
  610. returns value if value is between bound1 and bound2
  611. otherwise returns bound that is closer to value
  612. """
  613. if bound1 > bound2:
  614. return min(max(value, bound2), bound1)
  615. else:
  616. return min(max(value, bound1), bound2)
  617. def lerp(v0, v1, t):
  618. """
  619. returns a value lerped between v0 and v1, according to t
  620. t == 0 maps to v0, t == 1 maps to v1
  621. """
  622. return v0 + (t * (v1 - v0))
  623. def boolEqual(a, b):
  624. """
  625. returns true if a and b are both true or both false.
  626. returns false otherwise
  627. (a.k.a. xnor -- eXclusive Not OR).
  628. """
  629. return (a and b) or not (a or b)
  630. def lineupPos(i, num, spacing):
  631. """
  632. use to line up a series of 'num' objects, in one dimension,
  633. centered around zero
  634. 'i' is the index of the object in the lineup
  635. 'spacing' is the amount of space between objects in the lineup
  636. """
  637. assert num >= 1
  638. assert i >= 0 and i < num
  639. pos = float(i) * spacing
  640. return pos - ((float(spacing) * (num-1))/2.)
  641. def formatElapsedSeconds(seconds):
  642. """
  643. Returns a string of the form "mm:ss" or "hh:mm:ss" or "n days",
  644. representing the indicated elapsed time in seconds.
  645. """
  646. sign = ''
  647. if seconds < 0:
  648. seconds = -seconds
  649. sign = '-'
  650. # We use math.floor() instead of casting to an int, so we avoid
  651. # problems with numbers that are too large to represent as
  652. # type int.
  653. seconds = math.floor(seconds)
  654. hours = math.floor(seconds / (60 * 60))
  655. if hours > 36:
  656. days = math.floor((hours + 12) / 24)
  657. return "%s%d days" % (sign, days)
  658. seconds -= hours * (60 * 60)
  659. minutes = (int)(seconds / 60)
  660. seconds -= minutes * 60
  661. if hours != 0:
  662. return "%s%d:%02d:%02d" % (sign, hours, minutes, seconds)
  663. else:
  664. return "%s%d:%02d" % (sign, minutes, seconds)
  665. def solveQuadratic(a, b, c):
  666. # quadratic equation: ax^2 + bx + c = 0
  667. # quadratic formula: x = [-b +/- sqrt(b^2 - 4ac)] / 2a
  668. # returns None, root, or [root1, root2]
  669. # a cannot be zero.
  670. if a == 0.:
  671. return None
  672. # calculate the determinant (b^2 - 4ac)
  673. D = (b * b) - (4. * a * c)
  674. if D < 0:
  675. # there are no solutions (sqrt(negative number) is undefined)
  676. return None
  677. elif D == 0:
  678. # only one root
  679. return (-b) / (2. * a)
  680. else:
  681. # OK, there are two roots
  682. sqrtD = math.sqrt(D)
  683. twoA = 2. * a
  684. root1 = ((-b) - sqrtD) / twoA
  685. root2 = ((-b) + sqrtD) / twoA
  686. return [root1, root2]
  687. def stackEntryInfo(depth=0, baseFileName=1):
  688. """
  689. returns the sourcefilename, line number, and function name of
  690. an entry in the stack.
  691. 'depth' is how far back to go in the stack; 0 is the caller of this
  692. function, 1 is the function that called the caller of this function, etc.
  693. by default, strips off the path of the filename; override with baseFileName
  694. returns (fileName, lineNum, funcName) --> (string, int, string)
  695. returns (None, None, None) on error
  696. """
  697. try:
  698. stack = None
  699. frame = None
  700. try:
  701. stack = inspect.stack()
  702. # add one to skip the frame associated with this function
  703. frame = stack[depth+1]
  704. filename = frame[1]
  705. if baseFileName:
  706. filename = os.path.basename(filename)
  707. lineNum = frame[2]
  708. funcName = frame[3]
  709. result = (filename, lineNum, funcName)
  710. finally:
  711. del stack
  712. del frame
  713. except:
  714. result = (None, None, None)
  715. return result
  716. def lineInfo(baseFileName=1):
  717. """
  718. returns the sourcefilename, line number, and function name of the
  719. code that called this function
  720. (answers the question: 'hey lineInfo, where am I in the codebase?')
  721. see stackEntryInfo, above, for info on 'baseFileName' and return types
  722. """
  723. return stackEntryInfo(1)
  724. def callerInfo(baseFileName=1):
  725. """
  726. returns the sourcefilename, line number, and function name of the
  727. caller of the function that called this function
  728. (answers the question: 'hey callerInfo, who called me?')
  729. see stackEntryInfo, above, for info on 'baseFileName' and return types
  730. """
  731. return stackEntryInfo(2)
  732. def lineTag(baseFileName=1, verbose=0, separator=':'):
  733. """
  734. returns a string containing the sourcefilename and line number
  735. of the code that called this function
  736. (equivalent to lineInfo, above, with different return type)
  737. see stackEntryInfo, above, for info on 'baseFileName'
  738. if 'verbose' is false, returns a compact string of the form
  739. 'fileName:lineNum:funcName'
  740. if 'verbose' is true, returns a longer string that matches the
  741. format of Python stack trace dumps
  742. returns empty string on error
  743. """
  744. fileName, lineNum, funcName = callerInfo()
  745. if fileName is None:
  746. return ''
  747. if verbose:
  748. return 'File "%s", line %s, in %s' % (fileName, lineNum, funcName)
  749. else:
  750. return '%s%s%s%s%s' % (fileName, separator, lineNum, separator,
  751. funcName)
  752. def findPythonModule(module):
  753. # Look along the python load path for the indicated filename.
  754. # Returns the located pathname, or None if the filename is not
  755. # found.
  756. filename = module + '.py'
  757. for dir in sys.path:
  758. pathname = os.path.join(dir, filename)
  759. if os.path.exists(pathname):
  760. return pathname
  761. return None
  762. def describeException(backTrace = 4):
  763. # When called in an exception handler, returns a string describing
  764. # the current exception.
  765. def byteOffsetToLineno(code, byte):
  766. # Returns the source line number corresponding to the given byte
  767. # offset into the indicated Python code module.
  768. import array
  769. lnotab = array.array('B', code.co_lnotab)
  770. line = code.co_firstlineno
  771. for i in range(0, len(lnotab),2):
  772. byte -= lnotab[i]
  773. if byte <= 0:
  774. return line
  775. line += lnotab[i+1]
  776. return line
  777. infoArr = sys.exc_info()
  778. exception = infoArr[0]
  779. exceptionName = getattr(exception, '__name__', None)
  780. extraInfo = infoArr[1]
  781. trace = infoArr[2]
  782. stack = []
  783. while trace.tb_next:
  784. # We need to call byteOffsetToLineno to determine the true
  785. # line number at which the exception occurred, even though we
  786. # have both trace.tb_lineno and frame.f_lineno, which return
  787. # the correct line number only in non-optimized mode.
  788. frame = trace.tb_frame
  789. module = frame.f_globals.get('__name__', None)
  790. lineno = byteOffsetToLineno(frame.f_code, frame.f_lasti)
  791. stack.append("%s:%s, " % (module, lineno))
  792. trace = trace.tb_next
  793. frame = trace.tb_frame
  794. module = frame.f_globals.get('__name__', None)
  795. lineno = byteOffsetToLineno(frame.f_code, frame.f_lasti)
  796. stack.append("%s:%s, " % (module, lineno))
  797. description = ""
  798. for i in range(len(stack) - 1, max(len(stack) - backTrace, 0) - 1, -1):
  799. description += stack[i]
  800. description += "%s: %s" % (exceptionName, extraInfo)
  801. return description
  802. def mostDerivedLast(classList):
  803. """pass in list of classes. sorts list in-place, with derived classes
  804. appearing after their bases"""
  805. def compare(a,b):
  806. if issubclass(a,b):
  807. result=1
  808. elif issubclass(b,a):
  809. result=-1
  810. else:
  811. result=0
  812. #print a,b,result
  813. return result
  814. classList.sort(compare)
  815. def clampScalar(value, a, b):
  816. # calling this ought to be faster than calling both min and max
  817. if a < b:
  818. if value < a:
  819. return a
  820. elif value > b:
  821. return b
  822. else:
  823. return value
  824. else:
  825. if value < b:
  826. return b
  827. elif value > a:
  828. return a
  829. else:
  830. return value
  831. def weightedChoice(choiceList, rng=random.random, sum=None):
  832. """given a list of (weight,item) pairs, chooses an item based on the
  833. weights. rng must return 0..1. if you happen to have the sum of the
  834. weights, pass it in 'sum'."""
  835. # TODO: add support for dicts
  836. if sum is None:
  837. sum = 0.
  838. for weight, item in choiceList:
  839. sum += weight
  840. rand = rng()
  841. accum = rand * sum
  842. for weight, item in choiceList:
  843. accum -= weight
  844. if accum <= 0.:
  845. return item
  846. # rand is ~1., and floating-point error prevented accum from hitting 0.
  847. # Or you passed in a 'sum' that was was too large.
  848. # Return the last item.
  849. return item
  850. def randFloat(a, b=0., rng=random.random):
  851. """returns a random float in [a,b]
  852. call with single argument to generate random float between arg and zero
  853. """
  854. return lerp(a,b,rng())
  855. def normalDistrib(a, b, gauss=random.gauss):
  856. """
  857. NOTE: assumes a < b
  858. Returns random number between a and b, using gaussian distribution, with
  859. mean=avg(a,b), and a standard deviation that fits ~99.7% of the curve
  860. between a and b. Outlying results are clipped to a and b.
  861. ------------------------------------------------------------------------
  862. http://www-stat.stanford.edu/~naras/jsm/NormalDensity/NormalDensity.html
  863. The 68-95-99.7% Rule
  864. ====================
  865. All normal density curves satisfy the following property which is often
  866. referred to as the Empirical Rule:
  867. 68% of the observations fall within 1 standard deviation of the mean.
  868. 95% of the observations fall within 2 standard deviations of the mean.
  869. 99.7% of the observations fall within 3 standard deviations of the mean.
  870. Thus, for a normal distribution, almost all values lie within 3 standard
  871. deviations of the mean.
  872. ------------------------------------------------------------------------
  873. In calculating our standard deviation, we divide (b-a) by 6, since the
  874. 99.7% figure includes 3 standard deviations _on_either_side_ of the mean.
  875. """
  876. return max(a, min(b, gauss((a+b)*.5, (b-a)/6.)))
  877. def randUint31(rng=random.random):
  878. """returns a random integer in [0..2^31).
  879. rng must return float in [0..1]"""
  880. return int(rng() * 0x7FFFFFFF)
  881. def randInt32(rng=random.random):
  882. """returns a random integer in [-2147483648..2147483647].
  883. rng must return float in [0..1]
  884. """
  885. i = int(rng() * 0x7FFFFFFF)
  886. if rng() < .5:
  887. i += 0x80000000
  888. return i
  889. class Enum:
  890. """Pass in list of strings or string of comma-separated strings.
  891. Items are accessible as instance.item, and are assigned unique,
  892. increasing integer values. Pass in integer for 'start' to override
  893. starting value.
  894. Example:
  895. >>> colors = Enum('red, green, blue')
  896. >>> colors.red
  897. 0
  898. >>> colors.green
  899. 1
  900. >>> colors.blue
  901. 2
  902. >>> colors.getString(colors.red)
  903. 'red'
  904. """
  905. if __debug__:
  906. # chars that cannot appear within an item string.
  907. InvalidChars = string.whitespace
  908. def _checkValidIdentifier(item):
  909. invalidChars = string.whitespace+string.punctuation
  910. invalidChars = invalidChars.replace('_','')
  911. invalidFirstChars = invalidChars+string.digits
  912. if item[0] in invalidFirstChars:
  913. raise SyntaxError, ("Enum '%s' contains invalid first char" %
  914. item)
  915. if not disjoint(item, invalidChars):
  916. for char in item:
  917. if char in invalidChars:
  918. raise SyntaxError, (
  919. "Enum\n'%s'\ncontains illegal char '%s'" %
  920. (item, char))
  921. return 1
  922. _checkValidIdentifier = staticmethod(_checkValidIdentifier)
  923. def __init__(self, items, start=0):
  924. if type(items) == types.StringType:
  925. items = items.split(',')
  926. self._stringTable = {}
  927. # make sure we don't overwrite an existing element of the class
  928. assert(self._checkExistingMembers(items))
  929. assert(uniqueElements(items))
  930. i = start
  931. for item in items:
  932. # remove leading/trailing whitespace
  933. item = string.strip(item)
  934. # is there anything left?
  935. if len(item) == 0:
  936. continue
  937. # make sure there are no invalid characters
  938. assert(Enum._checkValidIdentifier(item))
  939. self.__dict__[item] = i
  940. self._stringTable[i] = item
  941. i += 1
  942. def getString(self, value):
  943. return self._stringTable[value]
  944. def __contains__(self, value):
  945. return value in self._stringTable
  946. def __len__(self):
  947. return len(self._stringTable)
  948. if __debug__:
  949. def _checkExistingMembers(self, items):
  950. for item in items:
  951. if hasattr(self, item):
  952. return 0
  953. return 1
  954. ############################################################
  955. # class: Singleton
  956. # Purpose: This provides a base metaclass for all classes
  957. # that require one and only one instance.
  958. #
  959. # Example: class mySingleton:
  960. # __metaclass__ = PythonUtil.Singleton
  961. # def __init__(self,...):
  962. # ...
  963. #
  964. # Note: This class is based on Python's New-Style Class
  965. # design. An error will occur if a defined class
  966. # attemps to inherit from a Classic-Style Class only,
  967. # ie: class myClassX:
  968. # def __init__(self, ...):
  969. # ...
  970. #
  971. # class myNewClassX(myClassX):
  972. # __metaclass__ = PythonUtil.Singleton
  973. # def __init__(self, ...):
  974. # myClassX.__init__(self, ...)
  975. # ...
  976. #
  977. # This causes problems because myNewClassX is a
  978. # New-Style class that inherits from only a
  979. # Classic-Style base class. There are two ways
  980. # simple ways to resolve this issue.
  981. #
  982. # First, if possible, make myClassX a
  983. # New-Style class by inheriting from object
  984. # object. IE: class myClassX(object):
  985. #
  986. # If for some reason that is not an option, make
  987. # myNewClassX inherit from object and myClassX.
  988. # IE: class myNewClassX(object, myClassX):
  989. ############################################################
  990. class Singleton(type):
  991. def __init__(cls,name,bases,dic):
  992. super(Singleton,cls).__init__(name,bases,dic)
  993. cls.instance=None
  994. def __call__(cls,*args,**kw):
  995. if cls.instance is None:
  996. cls.instance=super(Singleton,cls).__call__(*args,**kw)
  997. return cls.instance
  998. class SingletonError(ValueError):
  999. """ Used to indicate an inappropriate value for a Singleton."""