Summary
Both full-unpack paths in promising/promise.py follow nested Promise results without tracking previously seen promises. If a promise resolves to itself or back to an earlier promise in the chain, unpack_all_sync() and await promise/unpack_all() will spin forever or recurse until they blow up instead of raising a PromisingError.
Affected code
Promise.unpack_all_sync() — lines ~260-297
_AwaitablePromiseUnpacker.__await__() (the loop used by await promise/unpack_all()) — lines ~668-689
Suggested fix
Add a seen set guard in both unpack-all loops:
# unpack_all_sync
def unpack_all_sync(self, *, timeout: float | None = None) -> T_co:
deadline = None if timeout is None else time.monotonic() + timeout
result = self.as_concurrent_future().result(timeout=timeout)
seen: set[int] = {id(self)}
while isinstance(result, Promise):
if id(result) in seen:
raise PromisingError("Cyclic promise resolution detected")
seen.add(id(result))
remaining = None if deadline is None else deadline - time.monotonic()
if remaining is not None:
remaining = max(remaining, 0)
result = result.as_concurrent_future().result(timeout=remaining)
return result
# _AwaitablePromiseUnpacker.__await__
def __await__(self) -> Generator[Any, None, T_co | Promise[T_co]]:
seen: set[int] = {id(self._promise)}
...
if self._unpack_all:
while isinstance(result, Promise):
if id(result) in seen:
raise PromisingError("Cyclic promise resolution detected")
seen.add(id(result))
result = yield from result.__await__()
return result
Origin
Flagged as an outside-diff-range comment in PR #93 (review run 5f7583e4-f8cc-417e-b8e8-81118bf59de3, commit d00143025be5aa65f9ebb18b02de0b1bfe8783b2).
PR: #93
Requested by: @teremterem
Summary
Both full-unpack paths in
promising/promise.pyfollow nestedPromiseresults without tracking previously seen promises. If a promise resolves to itself or back to an earlier promise in the chain,unpack_all_sync()andawait promise/unpack_all()will spin forever or recurse until they blow up instead of raising aPromisingError.Affected code
Promise.unpack_all_sync()— lines ~260-297_AwaitablePromiseUnpacker.__await__()(the loop used byawait promise/unpack_all()) — lines ~668-689Suggested fix
Add a
seenset guard in both unpack-all loops:Origin
Flagged as an outside-diff-range comment in PR #93 (review run
5f7583e4-f8cc-417e-b8e8-81118bf59de3, commitd00143025be5aa65f9ebb18b02de0b1bfe8783b2).PR: #93
Requested by: @teremterem