saferepr.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. from __future__ import annotations
  2. import pprint
  3. import reprlib
  4. def _try_repr_or_str(obj: object) -> str:
  5. try:
  6. return repr(obj)
  7. except (KeyboardInterrupt, SystemExit):
  8. raise
  9. except BaseException:
  10. return f'{type(obj).__name__}("{obj}")'
  11. def _format_repr_exception(exc: BaseException, obj: object) -> str:
  12. try:
  13. exc_info = _try_repr_or_str(exc)
  14. except (KeyboardInterrupt, SystemExit):
  15. raise
  16. except BaseException as inner_exc:
  17. exc_info = f"unpresentable exception ({_try_repr_or_str(inner_exc)})"
  18. return (
  19. f"<[{exc_info} raised in repr()] {type(obj).__name__} object at 0x{id(obj):x}>"
  20. )
  21. def _ellipsize(s: str, maxsize: int) -> str:
  22. if len(s) > maxsize:
  23. i = max(0, (maxsize - 3) // 2)
  24. j = max(0, maxsize - 3 - i)
  25. return s[:i] + "..." + s[len(s) - j :]
  26. return s
  27. class SafeRepr(reprlib.Repr):
  28. """
  29. repr.Repr that limits the resulting size of repr() and includes
  30. information on exceptions raised during the call.
  31. """
  32. def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None:
  33. """
  34. :param maxsize:
  35. If not None, will truncate the resulting repr to that specific size, using ellipsis
  36. somewhere in the middle to hide the extra text.
  37. If None, will not impose any size limits on the returning repr.
  38. """
  39. super().__init__()
  40. # ``maxstring`` is used by the superclass, and needs to be an int; using a
  41. # very large number in case maxsize is None, meaning we want to disable
  42. # truncation.
  43. self.maxstring = maxsize if maxsize is not None else 1_000_000_000
  44. self.maxsize = maxsize
  45. self.use_ascii = use_ascii
  46. def repr(self, x: object) -> str:
  47. try:
  48. if self.use_ascii:
  49. s = ascii(x)
  50. else:
  51. s = super().repr(x)
  52. except (KeyboardInterrupt, SystemExit):
  53. raise
  54. except BaseException as exc:
  55. s = _format_repr_exception(exc, x)
  56. if self.maxsize is not None:
  57. s = _ellipsize(s, self.maxsize)
  58. return s
  59. def repr_instance(self, x: object, level: int) -> str:
  60. try:
  61. s = repr(x)
  62. except (KeyboardInterrupt, SystemExit):
  63. raise
  64. except BaseException as exc:
  65. s = _format_repr_exception(exc, x)
  66. if self.maxsize is not None:
  67. s = _ellipsize(s, self.maxsize)
  68. return s
  69. def safeformat(obj: object) -> str:
  70. """Return a pretty printed string for the given object.
  71. Failing __repr__ functions of user instances will be represented
  72. with a short exception info.
  73. """
  74. try:
  75. return pprint.pformat(obj)
  76. except Exception as exc:
  77. return _format_repr_exception(exc, obj)
  78. # Maximum size of overall repr of objects to display during assertion errors.
  79. DEFAULT_REPR_MAX_SIZE = 240
  80. def saferepr(
  81. obj: object, maxsize: int | None = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False
  82. ) -> str:
  83. """Return a size-limited safe repr-string for the given object.
  84. Failing __repr__ functions of user instances will be represented
  85. with a short exception info and 'saferepr' generally takes
  86. care to never raise exceptions itself.
  87. This function is a wrapper around the Repr/reprlib functionality of the
  88. stdlib.
  89. """
  90. return SafeRepr(maxsize, use_ascii).repr(obj)
  91. def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str:
  92. """Return an unlimited-size safe repr-string for the given object.
  93. As with saferepr, failing __repr__ functions of user instances
  94. will be represented with a short exception info.
  95. This function is a wrapper around simple repr.
  96. Note: a cleaner solution would be to alter ``saferepr``this way
  97. when maxsize=None, but that might affect some other code.
  98. """
  99. try:
  100. if use_ascii:
  101. return ascii(obj)
  102. return repr(obj)
  103. except Exception as exc:
  104. return _format_repr_exception(exc, obj)