string_repr.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. # -*- coding: utf-8 -*-
  2. """
  3. Part of the astor library for Python AST manipulation.
  4. License: 3-clause BSD
  5. Copyright (c) 2015 Patrick Maupin
  6. Pretty-print strings for the decompiler
  7. We either return the repr() of the string,
  8. or try to format it as a triple-quoted string.
  9. This is a lot harder than you would think.
  10. This has lots of Python 2 / Python 3 ugliness.
  11. """
  12. import re
  13. try:
  14. special_unicode = unicode
  15. except NameError:
  16. class special_unicode(object):
  17. pass
  18. try:
  19. basestring = basestring
  20. except NameError:
  21. basestring = str
  22. def _properly_indented(s, line_indent):
  23. mylist = s.split('\n')[1:]
  24. mylist = [x.rstrip() for x in mylist]
  25. mylist = [x for x in mylist if x]
  26. if not s:
  27. return False
  28. counts = [(len(x) - len(x.lstrip())) for x in mylist]
  29. return counts and min(counts) >= line_indent
  30. mysplit = re.compile(r'(\\|\"\"\"|\"$)').split
  31. replacements = {'\\': '\\\\', '"""': '""\\"', '"': '\\"'}
  32. def _prep_triple_quotes(s, mysplit=mysplit, replacements=replacements):
  33. """ Split the string up and force-feed some replacements
  34. to make sure it will round-trip OK
  35. """
  36. s = mysplit(s)
  37. s[1::2] = (replacements[x] for x in s[1::2])
  38. return ''.join(s)
  39. def string_triplequote_repr(s):
  40. """Return string's python representation in triple quotes.
  41. """
  42. return '"""%s"""' % _prep_triple_quotes(s)
  43. def pretty_string(s, embedded, current_line, uni_lit=False,
  44. min_trip_str=20, max_line=100):
  45. """There are a lot of reasons why we might not want to or
  46. be able to return a triple-quoted string. We can always
  47. punt back to the default normal string.
  48. """
  49. default = repr(s)
  50. # Punt on abnormal strings
  51. if (isinstance(s, special_unicode) or not isinstance(s, basestring)):
  52. return default
  53. if uni_lit and isinstance(s, bytes):
  54. return 'b' + default
  55. len_s = len(default)
  56. if current_line.strip():
  57. len_current = len(current_line)
  58. second_line_start = s.find('\n') + 1
  59. if embedded > 1 and not second_line_start:
  60. return default
  61. if len_s < min_trip_str:
  62. return default
  63. line_indent = len_current - len(current_line.lstrip())
  64. # Could be on a line by itself...
  65. if embedded and not second_line_start:
  66. return default
  67. total_len = len_current + len_s
  68. if total_len < max_line and not _properly_indented(s, line_indent):
  69. return default
  70. fancy = string_triplequote_repr(s)
  71. # Sometimes this doesn't work. One reason is that
  72. # the AST has no understanding of whether \r\n was
  73. # entered that way in the string or was a cr/lf in the
  74. # file. So we punt just so we can round-trip properly.
  75. try:
  76. if eval(fancy) == s and '\r' not in fancy:
  77. return fancy
  78. except Exception:
  79. pass
  80. return default