From a552a248dbed7238ee5ae071105e1992844b947f Mon Sep 17 00:00:00 2001 From: mprahl Date: Sep 20 2019 15:51:44 +0000 Subject: Allow ClientSession objects to get cleaned up by the garbage collector Fixes #1652 --- diff --git a/koji/__init__.py b/koji/__init__.py index b3ea04b..9ab8377 100644 --- a/koji/__init__.py +++ b/koji/__init__.py @@ -77,6 +77,7 @@ import tempfile import time import traceback import warnings +import weakref import xml.sax import xml.sax.handler import six.moves.urllib @@ -2141,7 +2142,10 @@ class ClientSession(object): self.opts = opts self.authtype = None self.setSession(sinfo) - self._multicall = MultiCallHack(self) + # Use a weak reference here so the garbage collector can still clean up + # ClientSession objects even with a circular reference, and the optional + # cycle detector being disabled due to the __del__ method being used. + self._multicall = MultiCallHack(weakref.ref(self)) self._calls = [] self.logger = logging.getLogger('koji') self.rsession = None @@ -2889,6 +2893,9 @@ class MultiCallHack(object): def __init__(self, session): self.value = False + # session must be a weak reference + if not isinstance(session, weakref.ReferenceType): + raise TypeError('The session parameter must be a weak reference') self.session = session def __nonzero__(self): @@ -2898,7 +2905,9 @@ class MultiCallHack(object): return self.value def __call__(self, **kw): - return MultiCallSession(self.session, **kw) + # self.session is a weak reference, which is why it is being called + # first + return MultiCallSession(self.session(), **kw) class MultiCallNotReady(Exception): diff --git a/tests/test_lib/test_client_session.py b/tests/test_lib/test_client_session.py index f62539a..a3db72d 100644 --- a/tests/test_lib/test_client_session.py +++ b/tests/test_lib/test_client_session.py @@ -1,6 +1,7 @@ from __future__ import absolute_import import mock import six +import weakref try: import unittest2 as unittest except ImportError: @@ -227,3 +228,11 @@ class TestMultiCall(unittest.TestCase): self.assertEqual(2, self.ksession._sendCall.call_count) self.assertEqual([['a', 'b', 'c'], {'faultCode': 1000, 'faultString': 'msg'}], ret) + + def test_MultiCallHack_weakref_validation(self): + expected_exc = 'The session parameter must be a weak reference' + with self.assertRaisesRegexp(TypeError, expected_exc): + koji.MultiCallHack(self.ksession) + + # This should not raise an exception + koji.MultiCallHack(weakref.ref(self.ksession))