You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
3.7KB

  1. import pprint
  2. import reprlib
  3. from typing import Any
  4. from typing import Dict
  5. from typing import IO
  6. from typing import Optional
  7. def _try_repr_or_str(obj: object) -> str:
  8. try:
  9. return repr(obj)
  10. except (KeyboardInterrupt, SystemExit):
  11. raise
  12. except BaseException:
  13. return '{}("{}")'.format(type(obj).__name__, obj)
  14. def _format_repr_exception(exc: BaseException, obj: object) -> str:
  15. try:
  16. exc_info = _try_repr_or_str(exc)
  17. except (KeyboardInterrupt, SystemExit):
  18. raise
  19. except BaseException as exc:
  20. exc_info = "unpresentable exception ({})".format(_try_repr_or_str(exc))
  21. return "<[{} raised in repr()] {} object at 0x{:x}>".format(
  22. exc_info, type(obj).__name__, id(obj)
  23. )
  24. def _ellipsize(s: str, maxsize: int) -> str:
  25. if len(s) > maxsize:
  26. i = max(0, (maxsize - 3) // 2)
  27. j = max(0, maxsize - 3 - i)
  28. return s[:i] + "..." + s[len(s) - j :]
  29. return s
  30. class SafeRepr(reprlib.Repr):
  31. """repr.Repr that limits the resulting size of repr() and includes
  32. information on exceptions raised during the call."""
  33. def __init__(self, maxsize: int) -> None:
  34. super().__init__()
  35. self.maxstring = maxsize
  36. self.maxsize = maxsize
  37. def repr(self, x: object) -> str:
  38. try:
  39. s = super().repr(x)
  40. except (KeyboardInterrupt, SystemExit):
  41. raise
  42. except BaseException as exc:
  43. s = _format_repr_exception(exc, x)
  44. return _ellipsize(s, self.maxsize)
  45. def repr_instance(self, x: object, level: int) -> str:
  46. try:
  47. s = repr(x)
  48. except (KeyboardInterrupt, SystemExit):
  49. raise
  50. except BaseException as exc:
  51. s = _format_repr_exception(exc, x)
  52. return _ellipsize(s, self.maxsize)
  53. def safeformat(obj: object) -> str:
  54. """Return a pretty printed string for the given object.
  55. Failing __repr__ functions of user instances will be represented
  56. with a short exception info.
  57. """
  58. try:
  59. return pprint.pformat(obj)
  60. except Exception as exc:
  61. return _format_repr_exception(exc, obj)
  62. def saferepr(obj: object, maxsize: int = 240) -> str:
  63. """Return a size-limited safe repr-string for the given object.
  64. Failing __repr__ functions of user instances will be represented
  65. with a short exception info and 'saferepr' generally takes
  66. care to never raise exceptions itself.
  67. This function is a wrapper around the Repr/reprlib functionality of the
  68. standard 2.6 lib.
  69. """
  70. return SafeRepr(maxsize).repr(obj)
  71. class AlwaysDispatchingPrettyPrinter(pprint.PrettyPrinter):
  72. """PrettyPrinter that always dispatches (regardless of width)."""
  73. def _format(
  74. self,
  75. object: object,
  76. stream: IO[str],
  77. indent: int,
  78. allowance: int,
  79. context: Dict[int, Any],
  80. level: int,
  81. ) -> None:
  82. # Type ignored because _dispatch is private.
  83. p = self._dispatch.get(type(object).__repr__, None) # type: ignore[attr-defined]
  84. objid = id(object)
  85. if objid in context or p is None:
  86. # Type ignored because _format is private.
  87. super()._format( # type: ignore[misc]
  88. object, stream, indent, allowance, context, level,
  89. )
  90. return
  91. context[objid] = 1
  92. p(self, object, stream, indent, allowance, context, level + 1)
  93. del context[objid]
  94. def _pformat_dispatch(
  95. object: object,
  96. indent: int = 1,
  97. width: int = 80,
  98. depth: Optional[int] = None,
  99. *,
  100. compact: bool = False,
  101. ) -> str:
  102. return AlwaysDispatchingPrettyPrinter(
  103. indent=indent, width=width, depth=depth, compact=compact
  104. ).pformat(object)