From 9c4603e4a19d4c70b446e010c64ce9cb64313db1 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 17 May 2024 10:28:17 +0100 Subject: [PATCH] Avoid reading `__code__` if it doesn't exist --- bugsnag/configuration.py | 6 +++++- bugsnag/sessiontracker.py | 5 ++++- tests/test_client.py | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/bugsnag/configuration.py b/bugsnag/configuration.py index f96752b8..114de35b 100644 --- a/bugsnag/configuration.py +++ b/bugsnag/configuration.py @@ -279,7 +279,11 @@ def delivery(self, value): # this should be made mandatory in the next major release if ( hasattr(value, 'deliver_sessions') and - callable(value.deliver_sessions) + callable(value.deliver_sessions) and + # Mock objects don't allow accessing or mocking '__code__' so + # ensure it exists before attempting to read it + # __code__ should always be present in a real delivery object + hasattr(value.deliver_sessions, '__code__') ): parameter_names = value.deliver_sessions.__code__.co_varnames diff --git a/bugsnag/sessiontracker.py b/bugsnag/sessiontracker.py index edb8bf26..adc31537 100644 --- a/bugsnag/sessiontracker.py +++ b/bugsnag/sessiontracker.py @@ -139,7 +139,10 @@ def __deliver(self, sessions: List[Dict], asynchronous=True): deliver = self.config.delivery.deliver_sessions - if 'options' in deliver.__code__.co_varnames: + if ( + hasattr(deliver, '__code__') and + 'options' in deliver.__code__.co_varnames + ): try: post_delivery_callback = self._request_tracker.new_request() diff --git a/tests/test_client.py b/tests/test_client.py index f01b8589..2b7233fc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -18,6 +18,7 @@ FeatureFlag ) +from bugsnag.delivery import Delivery import bugsnag.legacy as legacy from tests.utils import ( BrokenDelivery, @@ -124,6 +125,22 @@ def deliver(foo, config, payload, options={}): self.assertTrue(self.called) del self.called + # test for a regression caused by reading '__code__', which does not exist + # on Mock objects, nor can it be mocked + # see: https://github.com/bugsnag/bugsnag-python/commit/77e11747c293ba715bc764d17b49fb32918c030a#r142130768 + def test_delivery_can_be_mocked(self): + delivery = Mock(spec=Delivery) + + client = Client(delivery=delivery, api_key='abc') + client.notify(Exception('Oh no')) + + assert delivery.deliver.call_count == 1 + + client.session_tracker.start_session() + client.session_tracker.send_sessions() + + assert delivery.deliver_sessions.call_count == 1 + # Capture def test_notify_capture(self):