devtools.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #!/usr/bin/env python3
  2. class MMGenObject(object):
  3. 'placeholder - overridden when testing'
  4. def immutable_attr_init_check(self): pass
  5. import os
  6. if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN_TRACEBACK'):
  7. import sys,re,traceback,json,pprint
  8. from decimal import Decimal
  9. from difflib import unified_diff,ndiff
  10. def pmsg(*args,out=sys.stderr):
  11. d = args if len(args) > 1 else '' if not args else args[0]
  12. out.write(pprint.PrettyPrinter(indent=4).pformat(d) + '\n')
  13. def pdie(*args,exit_val=1,out=sys.stderr):
  14. pmsg(*args,out=out)
  15. sys.exit(exit_val)
  16. def pexit(*args,out=sys.stderr):
  17. pdie(*args,exit_val=0,out=out)
  18. def Pmsg(*args):
  19. pmsg(*args,out=sys.stdout)
  20. def Pdie(*args):
  21. pdie(*args,out=sys.stdout)
  22. def Pexit(*args):
  23. pexit(*args,out=sys.stdout)
  24. def print_stack_trace(message=None):
  25. tb1 = traceback.extract_stack()
  26. tb2 = [t for t in tb1 if t.filename[:1] != '<'][2:-2]
  27. sys.stderr.write('STACK TRACE {}:\n'.format(message or '(unnamed)'))
  28. fs = ' {}:{}: in {}:\n {}\n'
  29. for t in tb2:
  30. fn = re.sub(r'^\./','',os.path.relpath(t.filename))
  31. func = t.name+'()' if t.name[-1] != '>' else t.name
  32. sys.stderr.write(fs.format(fn,t.lineno,func,t.line or '(none)'))
  33. class MMGenObject(object):
  34. # Pretty-print any object subclassed from MMGenObject, recursing into sub-objects - WIP
  35. def pmsg(self):
  36. print(self.pfmt())
  37. def pdie(self):
  38. print(self.pfmt())
  39. sys.exit(1)
  40. def pfmt(self,lvl=0,id_list=[]):
  41. scalars = (str,int,float,Decimal)
  42. def do_list(out,e,lvl=0,is_dict=False):
  43. out.append('\n')
  44. for i in e:
  45. el = i if not is_dict else e[i]
  46. if is_dict:
  47. out.append('{s}{:<{l}}'.format(i,s=' '*(4*lvl+8),l=10,l2=8*(lvl+1)+8))
  48. if hasattr(el,'pfmt'):
  49. out.append('{:>{l}}{}'.format('',el.pfmt(
  50. lvl=lvl+1,id_list=id_list+[id(self)]),l=(lvl+1)*8))
  51. elif isinstance(el,scalars):
  52. if isList(e):
  53. out.append('{:>{l}}{:16}\n'.format('',repr(el),l=lvl*8))
  54. else:
  55. out.append(' {}'.format(repr(el)))
  56. elif isList(el) or isDict(el):
  57. indent = 1 if is_dict else lvl*8+4
  58. out.append('{:>{l}}{:16}'.format('','<'+type(el).__name__+'>',l=indent))
  59. if isList(el) and isinstance(el[0],scalars):
  60. out.append('\n')
  61. do_list(out,el,lvl=lvl+1,is_dict=isDict(el))
  62. else:
  63. out.append('{:>{l}}{:16} {}\n'.format(
  64. '','<'+type(el).__name__+'>',repr(el),l=(lvl*8)+8))
  65. out.append('\n')
  66. if not e: out.append('{}\n'.format(repr(e)))
  67. def isDict(obj):
  68. return isinstance(obj,dict)
  69. def isList(obj):
  70. return isinstance(obj,list)
  71. def isScalar(obj):
  72. return isinstance(obj,scalars)
  73. out = ['<{}>{}\n'.format(type(self).__name__,' '+repr(self) if isScalar(self) else '')]
  74. if id(self) in id_list:
  75. return out[-1].rstrip() + ' [RECURSION]\n'
  76. if isList(self) or isDict(self):
  77. do_list(out,self,lvl=lvl,is_dict=isDict(self))
  78. for k in self.__dict__:
  79. e = getattr(self,k)
  80. if isList(e) or isDict(e):
  81. out.append('{:>{l}}{:<10} {:16}'.format('',k,'<'+type(e).__name__+'>',l=(lvl*8)+4))
  82. do_list(out,e,lvl=lvl,is_dict=isDict(e))
  83. elif hasattr(e,'pfmt') and type(e) != type:
  84. out.append('{:>{l}}{:10} {}'.format(
  85. '',k,e.pfmt(lvl=lvl+1,id_list=id_list+[id(self)]),l=(lvl*8)+4))
  86. else:
  87. out.append('{:>{l}}{:<10} {:16} {}\n'.format(
  88. '',k,'<'+type(e).__name__+'>',repr(e),l=(lvl*8)+4))
  89. import re
  90. return re.sub('\n+','\n',''.join(out))
  91. # Check that all immutables have been initialized. Expensive, so do only when testing.
  92. def immutable_attr_init_check(self):
  93. from .globalvars import g
  94. if g.test_suite:
  95. from .util import rdie
  96. cls = type(self)
  97. for attrname in sorted({a for a in self.valid_attrs if a[0] != '_'}):
  98. for o in (cls,cls.__bases__[0]): # assume there's only one base class
  99. if attrname in o.__dict__:
  100. attr = o.__dict__[attrname]
  101. break
  102. else:
  103. rdie(3,'unable to find descriptor {}.{}'.format(cls.__name__,attrname))
  104. if type(attr).__name__ == 'ImmutableAttr':
  105. if attrname not in self.__dict__:
  106. fs = 'attribute {!r} of {} has not been initialized in constructor!'
  107. rdie(3,fs.format(attrname,cls.__name__))
  108. def print_diff(a,b,from_file='',to_file='',from_json=True):
  109. if from_json:
  110. a = json.dumps(json.loads(a),indent=4).split('\n') if a else []
  111. b = json.dumps(json.loads(b),indent=4).split('\n') if b else []
  112. else:
  113. a = a.split('\n')
  114. b = b.split('\n')
  115. sys.stderr.write(' DIFF:\n {}\n'.format('\n '.join(unified_diff(a,b,from_file,to_file))))
  116. def get_ndiff(a,b):
  117. a = a.split('\n')
  118. b = b.split('\n')
  119. return list(ndiff(a,b))