11__all__ = ()
22
33import reprlib
4+ from _thread import get_ident
45
56from . import format_helpers
67
@@ -41,6 +42,16 @@ def format_cb(callback):
4142 return f'cb=[{ cb } ]'
4243
4344
45+ # bpo-42183: _repr_running is needed for repr protection
46+ # when a Future or Task result contains itself directly or indirectly.
47+ # The logic is borrowed from @reprlib.recursive_repr decorator.
48+ # Unfortunately, the direct decorator usage is impossible because of
49+ # AttributeError: '_asyncio.Task' object has no attribute '__module__' error.
50+ #
51+ # After fixing this thing we can return to the decorator based approach.
52+ _repr_running = set ()
53+
54+
4455def _future_repr_info (future ):
4556 # (Future) -> str
4657 """helper function for Future.__repr__"""
@@ -49,9 +60,17 @@ def _future_repr_info(future):
4960 if future ._exception is not None :
5061 info .append (f'exception={ future ._exception !r} ' )
5162 else :
52- # use reprlib to limit the length of the output, especially
53- # for very long strings
54- result = reprlib .repr (future ._result )
63+ key = id (future ), get_ident ()
64+ if key in _repr_running :
65+ result = '...'
66+ else :
67+ _repr_running .add (key )
68+ try :
69+ # use reprlib to limit the length of the output, especially
70+ # for very long strings
71+ result = reprlib .repr (future ._result )
72+ finally :
73+ _repr_running .discard (key )
5574 info .append (f'result={ result } ' )
5675 if future ._callbacks :
5776 info .append (_format_callbacks (future ._callbacks ))
0 commit comments