PythonUtil.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. import types
  2. import string
  3. import re
  4. import math
  5. import operator
  6. def ifAbsentPut(dict, key, newValue):
  7. """
  8. If dict has key, return the value, otherwise insert the newValue and return it
  9. """
  10. if dict.has_key(key):
  11. return dict[key]
  12. else:
  13. dict[key] = newValue
  14. return newValue
  15. def unique(L1, L2):
  16. """Return a list containing all items in 'L1' that are not in 'L2'"""
  17. L2 = dict([(k,None) for k in L2])
  18. return [item for item in L1 if item not in L2]
  19. def indent(stream, numIndents, str):
  20. """
  21. Write str to stream with numIndents in front it it
  22. """
  23. # To match emacs, instead of a tab character we will use 4 spaces
  24. stream.write(' ' * numIndents + str)
  25. def apropos(obj, *args):
  26. """
  27. Obsolete, use pdir
  28. """
  29. print 'Use pdir instead'
  30. def getClassLineage(obj):
  31. """ getClassLineage(obj): print object inheritance list """
  32. # Just a dictionary, return dictionary
  33. if type(obj) == types.DictionaryType:
  34. return [obj]
  35. # Instance, make a list with the instance and its class interitance
  36. elif type(obj) == types.InstanceType:
  37. return [obj] + getClassLineage(obj.__class__)
  38. # Class, see what it derives from
  39. elif type(obj) == types.ClassType:
  40. lineage = [obj]
  41. for c in obj.__bases__:
  42. lineage = lineage + getClassLineage(c)
  43. return lineage
  44. # Not what I'm looking for
  45. else:
  46. return []
  47. def pdir(obj, str = None, fOverloaded = 0, width = None,
  48. fTruncate = 1, lineWidth = 75, wantPrivate = 0):
  49. # Remove redundant class entries
  50. uniqueLineage = []
  51. for l in getClassLineage(obj):
  52. if type(l) == types.ClassType:
  53. if l in uniqueLineage:
  54. break
  55. uniqueLineage.append(l)
  56. # Pretty print out directory info
  57. uniqueLineage.reverse()
  58. for obj in uniqueLineage:
  59. _pdir(obj, str, fOverloaded, width, fTruncate, lineWidth, wantPrivate)
  60. print
  61. def _pdir(obj, str = None, fOverloaded = 0, width = None,
  62. fTruncate = 1, lineWidth = 75, wantPrivate = 0):
  63. """
  64. Print out a formatted list of members and methods of an instance or class
  65. """
  66. def printHeader(name):
  67. name = ' ' + name + ' '
  68. length = len(name)
  69. if length < 70:
  70. padBefore = int((70 - length)/2.0)
  71. padAfter = max(0,70 - length - padBefore)
  72. header = '*' * padBefore + name + '*' * padAfter
  73. print header
  74. print
  75. def printInstanceHeader(i, printHeader = printHeader):
  76. printHeader(i.__class__.__name__ + ' INSTANCE INFO')
  77. def printClassHeader(c, printHeader = printHeader):
  78. printHeader(c.__name__ + ' CLASS INFO')
  79. def printDictionaryHeader(d, printHeader = printHeader):
  80. printHeader('DICTIONARY INFO')
  81. # Print Header
  82. if type(obj) == types.InstanceType:
  83. printInstanceHeader(obj)
  84. elif type(obj) == types.ClassType:
  85. printClassHeader(obj)
  86. elif type (obj) == types.DictionaryType:
  87. printDictionaryHeader(obj)
  88. # Get dict
  89. if type(obj) == types.DictionaryType:
  90. dict = obj
  91. else:
  92. dict = obj.__dict__
  93. # Adjust width
  94. if width:
  95. maxWidth = width
  96. else:
  97. maxWidth = 10
  98. keyWidth = 0
  99. aproposKeys = []
  100. privateKeys = []
  101. remainingKeys = []
  102. for key in dict.keys():
  103. if not width:
  104. keyWidth = len(key)
  105. if str:
  106. if re.search(str, key, re.I):
  107. aproposKeys.append(key)
  108. if (not width) and (keyWidth > maxWidth):
  109. maxWidth = keyWidth
  110. else:
  111. if key[:1] == '_':
  112. if wantPrivate:
  113. privateKeys.append(key)
  114. if (not width) and (keyWidth > maxWidth):
  115. maxWidth = keyWidth
  116. else:
  117. remainingKeys.append(key)
  118. if (not width) and (keyWidth > maxWidth):
  119. maxWidth = keyWidth
  120. # Sort appropriate keys
  121. if str:
  122. aproposKeys.sort()
  123. else:
  124. privateKeys.sort()
  125. remainingKeys.sort()
  126. # Print out results
  127. if wantPrivate:
  128. keys = aproposKeys + privateKeys + remainingKeys
  129. else:
  130. keys = aproposKeys + remainingKeys
  131. format = '%-' + `maxWidth` + 's'
  132. for key in keys:
  133. value = dict[key]
  134. if callable(value):
  135. strvalue = `Signature(value)`
  136. else:
  137. strvalue = `value`
  138. if fTruncate:
  139. # Cut off line (keeping at least 1 char)
  140. strvalue = strvalue[:max(1,lineWidth - maxWidth)]
  141. print (format % key)[:maxWidth] + '\t' + strvalue
  142. # Magic numbers: These are the bit masks in func_code.co_flags that
  143. # reveal whether or not the function has a *arg or **kw argument.
  144. _POS_LIST = 4
  145. _KEY_DICT = 8
  146. def _is_variadic(function):
  147. return function.func_code.co_flags & _POS_LIST
  148. def _has_keywordargs(function):
  149. return function.func_code.co_flags & _KEY_DICT
  150. def _varnames(function):
  151. return function.func_code.co_varnames
  152. def _getcode(f):
  153. """
  154. _getcode(f)
  155. This function returns the name and function object of a callable
  156. object.
  157. """
  158. def method_get(f):
  159. return f.__name__, f.im_func
  160. def function_get(f):
  161. return f.__name__, f
  162. def instance_get(f):
  163. if hasattr(f, '__call__'):
  164. method = f.__call__
  165. if (type(method) == types.MethodType):
  166. func = method.im_func
  167. else:
  168. func = method
  169. return ("%s%s" % (f.__class__.__name__, '__call__'), func)
  170. else:
  171. s = ("Instance %s of class %s does not have a __call__ method" %
  172. (f, f.__class__.__name__))
  173. raise TypeError, s
  174. def class_get(f):
  175. if hasattr(f, '__init__'):
  176. return f.__name__, f.__init__.im_func
  177. else:
  178. return f.__name__, lambda: None
  179. codedict = { types.UnboundMethodType: method_get,
  180. types.MethodType : method_get,
  181. types.FunctionType : function_get,
  182. types.InstanceType : instance_get,
  183. types.ClassType : class_get,
  184. }
  185. try:
  186. return codedict[type(f)](f)
  187. except KeyError:
  188. if callable(f): # eg, built-in functions and methods
  189. # raise ValueError, "type %s not supported yet." % type(f)
  190. return f.__name__, None
  191. else:
  192. raise TypeError, ("object %s of type %s is not callable." %
  193. (f, type(f)))
  194. class Signature:
  195. def __init__(self, func):
  196. self.type = type(func)
  197. self.name, self.func = _getcode(func)
  198. def ordinary_args(self):
  199. n = self.func.func_code.co_argcount
  200. return _varnames(self.func)[0:n]
  201. def special_args(self):
  202. n = self.func.func_code.co_argcount
  203. x = {}
  204. #
  205. if _is_variadic(self.func):
  206. x['positional'] = _varnames(self.func)[n]
  207. if _has_keywordargs(self.func):
  208. x['keyword'] = _varnames(self.func)[n+1]
  209. elif _has_keywordargs(self.func):
  210. x['keyword'] = _varnames(self.func)[n]
  211. else:
  212. pass
  213. return x
  214. def full_arglist(self):
  215. base = list(self.ordinary_args())
  216. x = self.special_args()
  217. if x.has_key('positional'):
  218. base.append(x['positional'])
  219. if x.has_key('keyword'):
  220. base.append(x['keyword'])
  221. return base
  222. def defaults(self):
  223. defargs = self.func.func_defaults
  224. args = self.ordinary_args()
  225. mapping = {}
  226. if defargs is not None:
  227. for i in range(-1, -(len(defargs)+1), -1):
  228. mapping[args[i]] = defargs[i]
  229. else:
  230. pass
  231. return mapping
  232. def __repr__(self):
  233. if self.func:
  234. defaults = self.defaults()
  235. specials = self.special_args()
  236. l = []
  237. for arg in self.ordinary_args():
  238. if defaults.has_key(arg):
  239. l.append( arg + '=' + str(defaults[arg]) )
  240. else:
  241. l.append( arg )
  242. if specials.has_key('positional'):
  243. l.append( '*' + specials['positional'] )
  244. if specials.has_key('keyword'):
  245. l.append( '**' + specials['keyword'] )
  246. return "%s(%s)" % (self.name, string.join(l, ', '))
  247. else:
  248. return "%s(?)" % self.name
  249. def aproposAll(obj):
  250. """
  251. Print out a list of all members and methods (including overloaded methods)
  252. of an instance or class
  253. """
  254. apropos(obj, fOverloaded = 1, fTruncate = 0)
  255. def doc(obj):
  256. if (isinstance(obj, types.MethodType)) or \
  257. (isinstance(obj, types.FunctionType)):
  258. print obj.__doc__
  259. def adjust(command = None, dim = 1, parent = None, **kw):
  260. """
  261. adjust(command = None, parent = None, **kw)
  262. Popup and entry scale to adjust a parameter
  263. Accepts any Slider keyword argument. Typical arguments include:
  264. command: The one argument command to execute
  265. min: The min value of the slider
  266. max: The max value of the slider
  267. resolution: The resolution of the slider
  268. text: The label on the slider
  269. These values can be accessed and/or changed after the fact
  270. >>> vg = adjust()
  271. >>> vg['min']
  272. 0.0
  273. >>> vg['min'] = 10.0
  274. >>> vg['min']
  275. 10.0
  276. """
  277. # Make sure we enable Tk
  278. import Valuator
  279. # Set command if specified
  280. if command:
  281. kw['command'] = lambda x: apply(command, x)
  282. if parent is None:
  283. kw['title'] = command.__name__
  284. kw['dim'] = dim
  285. # Create toplevel if needed
  286. if not parent:
  287. vg = apply(Valuator.ValuatorGroupPanel, (parent,), kw)
  288. else:
  289. vg = apply(Valuator.ValuatorGroup,(parent,), kw)
  290. vg.pack(expand = 1, fill = 'x')
  291. return vg
  292. def intersection(a, b):
  293. """
  294. intersection(list, list):
  295. """
  296. if not a: return []
  297. if not b: return []
  298. c = a + b
  299. d = []
  300. for i in c:
  301. if (i in a) and (i in b):
  302. # make it unique, like a set
  303. if (i not in d):
  304. d.append(i)
  305. return d
  306. def union(a, b):
  307. """
  308. union(list, list):
  309. """
  310. # Copy a
  311. c = a[:]
  312. for i in b:
  313. if (i not in c):
  314. c.append(i)
  315. return c
  316. def sameElements(a, b):
  317. if len(a) != len(b):
  318. return 0
  319. for elem in a:
  320. if elem not in b:
  321. return 0
  322. for elem in b:
  323. if elem not in a:
  324. return 0
  325. return 1
  326. def contains(whole, sub):
  327. """
  328. Return 1 if whole contains sub, 0 otherwise
  329. """
  330. if (whole == sub):
  331. return 1
  332. for elem in sub:
  333. # The first item you find not in whole, return 0
  334. if elem not in whole:
  335. return 0
  336. # If you got here, whole must contain sub
  337. return 1
  338. def replace(list, old, new, all=0):
  339. """
  340. replace 'old' with 'new' in 'list'
  341. if all == 0, replace first occurrence
  342. otherwise replace all occurrences
  343. returns the number of items replaced
  344. """
  345. if old not in list:
  346. return 0
  347. if not all:
  348. i = list.index(old)
  349. list[i] = new
  350. return 1
  351. else:
  352. numReplaced = 0
  353. for i in xrange(len(list)):
  354. if list[i] == old:
  355. numReplaced += 1
  356. list[i] = new
  357. return numReplaced
  358. def reduceAngle(deg):
  359. """
  360. Reduces an angle (in degrees) to a value in [-180..180)
  361. """
  362. return (((deg + 180.) % 360.) - 180.)
  363. def fitSrcAngle2Dest(src, dest):
  364. """
  365. given a src and destination angle, returns an equivalent src angle
  366. that is within [-180..180) of dest
  367. examples:
  368. fitSrcAngle2Dest(30,60) == 30
  369. fitSrcAngle2Dest(60,30) == 60
  370. fitSrcAngle2Dest(0,180) == 0
  371. fitSrcAngle2Dest(-1,180) == 359
  372. fitSrcAngle2Dest(-180,180) == 180
  373. """
  374. return dest + reduceAngle(src - dest)
  375. def fitDestAngle2Src(src, dest):
  376. """
  377. given a src and destination angle, returns an equivalent dest angle
  378. that is within [-180..180) of src
  379. examples:
  380. fitDestAngle2Src(30,60) == 60
  381. fitDestAngle2Src(60,30) == 30
  382. fitDestAngle2Src(0,180) == -180
  383. fitDestAngle2Src(1,180) == 180
  384. """
  385. return src + (reduceAngle(dest - src))
  386. def closestDestAngle2(src, dest):
  387. # The function above didn't seem to do what I wanted. So I hacked
  388. # this one together. I can't really say I understand it. It's more
  389. # from impirical observation... GRW
  390. diff = src - dest
  391. # if the difference is greater that 180 it's shorter to go the other way
  392. if diff > 180:
  393. return dest - 360
  394. # or perhaps the OTHER other way...
  395. elif diff < -180:
  396. return dest + 360
  397. # otherwise just go to the original destination
  398. else:
  399. return dest
  400. def closestDestAngle(src, dest):
  401. # The function above didn't seem to do what I wanted. So I hacked
  402. # this one together. I can't really say I understand it. It's more
  403. # from impirical observation... GRW
  404. diff = src - dest
  405. # if the difference is greater that 180 it's shorter to go the other way
  406. if diff > 180:
  407. return src - (diff - 360)
  408. # or perhaps the OTHER other way...
  409. elif diff < -180:
  410. return src - (360 + diff)
  411. # otherwise just go to the original destination
  412. else:
  413. return dest
  414. def binaryRepr(number, max_length = 32):
  415. # This will only work reliably for relatively small numbers.
  416. # Increase the value of max_length if you think you're going
  417. # to use long integers
  418. assert number < 2L << max_length
  419. shifts = map (operator.rshift, max_length * [number], \
  420. range (max_length - 1, -1, -1))
  421. digits = map (operator.mod, shifts, max_length * [2])
  422. if not digits.count (1): return 0
  423. digits = digits [digits.index (1):]
  424. return string.join (map (repr, digits), '')
  425. # constant profile defaults
  426. PyUtilProfileDefaultFilename = 'profiledata'
  427. PyUtilProfileDefaultLines = 80
  428. PyUtilProfileDefaultSorts = ['cumulative', 'time', 'calls']
  429. # call this from the prompt, and break back out to the prompt
  430. # to stop profiling
  431. def startProfile(filename=PyUtilProfileDefaultFilename,
  432. lines=PyUtilProfileDefaultLines,
  433. sorts=PyUtilProfileDefaultSorts,
  434. silent=0):
  435. import profile
  436. profile.run('run()', filename)
  437. if not silent:
  438. printProfile(filename, lines, sorts)
  439. # call this to see the results again
  440. def printProfile(filename=PyUtilProfileDefaultFilename,
  441. lines=PyUtilProfileDefaultLines,
  442. sorts=PyUtilProfileDefaultSorts,):
  443. import pstats
  444. s = pstats.Stats(filename)
  445. s.strip_dirs()
  446. for sort in sorts:
  447. s.sort_stats(sort)
  448. s.print_stats(lines)
  449. class Functor:
  450. def __init__(self, function, *args, **kargs):
  451. assert callable(function), "function should be a callable obj"
  452. self._function = function
  453. self._args = args
  454. self._kargs = kargs
  455. def __call__(self, *args, **kargs):
  456. """call function"""
  457. _args = list(self._args)
  458. _args.extend(args)
  459. _kargs = self._kargs.copy()
  460. _kargs.update(kargs)
  461. return apply(self._function,_args,_kargs)