diff --git a/CHANGELOG.md b/CHANGELOG.md index 382946b..b91271d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## TBD + +### Bug fixes + +* Avoid reading `__code__` when setting a custom delivery unless it exists + [#387](https://github.com/bugsnag/bugsnag-python/pull/387) + ## v4.7.0 (2024-04-24) ### Enhancements diff --git a/bugsnag/configuration.py b/bugsnag/configuration.py index f96752b..114de35 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 edb8bf2..adc3153 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 f01b858..2b7233f 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):