From dae1963cf38f730291126b7dadfda89ffb21cefd Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 29 Mar 2021 10:53:14 -0700 Subject: bpo-35930: Raising an exception raised in a "future" instance will create reference cycles (GH-24995) (#25071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: https://lists.es.python.org/pipermail/general/attachments/20201229/0c14bc58/attachment-0002.png After: https://lists.es.python.org/pipermail/general/attachments/20201229/0c14bc58/attachment-0003.png (cherry picked from commit 32430aadadf6e012e39167d3c18a24e49fb84874) Co-authored-by: Jesús Cea <jcea@jcea.es> Co-authored-by: Jesús Cea <jcea@jcea.es> --- Lib/concurrent/futures/_base.py | 38 +++++++++++++--------- .../2021-03-23-17-18-56.bpo-35930.RZ51pM.rst | 2 ++ 2 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-03-23-17-18-56.bpo-35930.RZ51pM.rst diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py index 6001e3b..fd2b244 100644 --- a/Lib/concurrent/futures/_base.py +++ b/Lib/concurrent/futures/_base.py @@ -385,7 +385,11 @@ class Future(object): def __get_result(self): if self._exception: - raise self._exception + try: + raise self._exception + finally: + # Break a reference cycle with the exception in self._exception + self = None else: return self._result @@ -425,20 +429,24 @@ class Future(object): timeout. Exception: If the call raised then that exception will be raised. """ - with self._condition: - if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: - raise CancelledError() - elif self._state == FINISHED: - return self.__get_result() - - self._condition.wait(timeout) - - if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: - raise CancelledError() - elif self._state == FINISHED: - return self.__get_result() - else: - raise TimeoutError() + try: + with self._condition: + if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: + raise CancelledError() + elif self._state == FINISHED: + return self.__get_result() + + self._condition.wait(timeout) + + if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: + raise CancelledError() + elif self._state == FINISHED: + return self.__get_result() + else: + raise TimeoutError() + finally: + # Break a reference cycle with the exception in self._exception + self = None def exception(self, timeout=None): """Return the exception raised by the call that the future represents. diff --git a/Misc/NEWS.d/next/Library/2021-03-23-17-18-56.bpo-35930.RZ51pM.rst b/Misc/NEWS.d/next/Library/2021-03-23-17-18-56.bpo-35930.RZ51pM.rst new file mode 100644 index 0000000..71c6012 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-03-23-17-18-56.bpo-35930.RZ51pM.rst @@ -0,0 +1,2 @@ +Raising an exception raised in a "future" instance will create reference +cycles. -- cgit v0.12