Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions influxdb_client/_sync/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,30 @@ def __init__(self, configuration=None, header_name=None, header_value=None,

def __del__(self):
"""Dispose pools."""
self._signout()
if self._pool:
self._pool.close()
self._pool.join()
# During interpreter shutdown, attributes and imported services can already be cleared.
# A finalizer must not let those teardown details escape as an ignored exception.
try:
if getattr(self, 'configuration', None) is not None:
self._signout()
except Exception:
pass

pool = getattr(self, '_pool', None)
if pool:
try:
pool.close()
pool.join()
except Exception:
pass
self._pool = None
if self.rest_client and self.rest_client.pool_manager and hasattr(self.rest_client.pool_manager, 'clear'):
self.rest_client.pool_manager.clear()

rest_client = getattr(self, 'rest_client', None)
pool_manager = getattr(rest_client, 'pool_manager', None)
if hasattr(pool_manager, 'clear'):
try:
pool_manager.clear()
except Exception:
pass

@property
def pool(self):
Expand Down
54 changes: 53 additions & 1 deletion tests/test_InfluxDBClientAuthorization.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import unittest
from unittest.mock import Mock

import httpretty

from influxdb_client import InfluxDBClient
from influxdb_client._sync.api_client import ApiClient


class InfluxDBClientAuthorization(unittest.TestCase):

def setUp(self) -> None:
httpretty.enable()
httpretty.reset()
self.influxdb_client = None

def tearDown(self) -> None:
if self.influxdb_client:
Expand All @@ -35,7 +38,10 @@ def test_session_request(self):

self.assertEqual(2, len(httpretty.httpretty.latest_requests))
# basic auth header
self.assertEqual('Basic bXktdXNlcm5hbWU6bXktcGFzc3dvcmQ=', httpretty.httpretty.latest_requests[0].headers['Authorization'])
self.assertEqual(
'Basic bXktdXNlcm5hbWU6bXktcGFzc3dvcmQ=',
httpretty.httpretty.latest_requests[0].headers['Authorization'],
)
# cookie header
self.assertEqual('session=xyz', httpretty.httpretty.latest_requests[1].headers['Cookie'])
self.assertIsNotNone(self.influxdb_client.api_client.cookie)
Expand All @@ -44,3 +50,49 @@ def test_session_request(self):
self.influxdb_client.close()

self.assertEqual(3, len(httpretty.httpretty.latest_requests))

def test_api_client_finalizer_ignores_partially_torn_down_state(self):
api_client = ApiClient()
del api_client.configuration
del api_client.rest_client

api_client.__del__()

def test_api_client_finalizer_cleans_pools_when_signout_fails(self):
api_client = ApiClient()
api_client._signout = Mock(side_effect=RuntimeError('session service unavailable'))
pool = api_client._pool = Mock()
manager = api_client.rest_client.pool_manager = Mock()

api_client.__del__()

api_client._signout.assert_called_once_with()
pool.close.assert_called_once_with()
pool.join.assert_called_once_with()
self.assertIsNone(api_client._pool)
manager.clear.assert_called_once_with()

def test_api_client_finalizer_clears_http_pool_when_thread_pool_cleanup_fails(self):
for operation in ('close', 'join'):
with self.subTest(operation=operation):
api_client = ApiClient()
api_client._signout = Mock()
pool = api_client._pool = Mock()
getattr(pool, operation).side_effect = RuntimeError('pool already shut down')
manager = api_client.rest_client.pool_manager = Mock()

api_client.__del__()

getattr(pool, operation).assert_called_once_with()
self.assertIsNone(api_client._pool)
manager.clear.assert_called_once_with()

def test_api_client_finalizer_ignores_http_pool_cleanup_failure(self):
api_client = ApiClient()
api_client._signout = Mock()
manager = api_client.rest_client.pool_manager = Mock()
manager.clear.side_effect = RuntimeError('HTTP pool already shut down')

api_client.__del__()

manager.clear.assert_called_once_with()