From 8508c242c9172c467865f10adab10da9db98d5a3 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 5 Nov 2024 16:29:53 +0000 Subject: [PATCH 01/23] replace utcnow calls --- bugsnag/utils.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/bugsnag/utils.py b/bugsnag/utils.py index c2e1a857..5a18be3e 100644 --- a/bugsnag/utils.py +++ b/bugsnag/utils.py @@ -4,6 +4,7 @@ from threading import local as threadlocal from typing import AnyStr, Tuple, Optional import warnings +import sys import copy import logging from datetime import datetime, timedelta @@ -422,14 +423,13 @@ def remove_query_from_url(url: AnyStr) -> Optional[AnyStr]: # milliseconds precision # Python can do this natively from version 3.6, but we need to include a # fallback implementation for Python 3.5 -try: - # this will raise if 'timespec' isn't supported - datetime.utcnow().isoformat(timespec='milliseconds') # type: ignore - +if sys.version_info >= (3, 6): + # Python 3.6+ has a built-in method for this def to_rfc3339(dt: datetime) -> str: return dt.isoformat(timespec='milliseconds') # type: ignore -except Exception: +else: + # Python 3.5 fallback implementation def _get_timezone_offset(dt: datetime) -> str: if dt.tzinfo is None: return '' @@ -464,17 +464,16 @@ def to_rfc3339(dt: datetime) -> str: def get_package_version(package_name: str) -> Optional[str]: - try: - from importlib import metadata - - return metadata.version(package_name) # type: ignore - except ImportError: + if sys.version_info >= (3, 8): try: - import pkg_resources - except ImportError: + from importlib import metadata + return metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: return None - + else: try: - return pkg_resources.get_distribution(package_name).version - except pkg_resources.DistributionNotFound: + import pkg_resources # type: ignore + return pkg_resources.get_distribution( + package_name).version # type: ignore + except (ImportError, pkg_resources.DistributionNotFound): return None From fd9b285580267d89656b63124dd794088e748d5c Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 11 Nov 2024 16:25:42 +0000 Subject: [PATCH 02/23] conditionally add pkg_resources --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 325d6b3d..38e2519d 100644 --- a/tox.ini +++ b/tox.ini @@ -64,7 +64,7 @@ deps= exceptiongroup: exceptiongroup lint: flake8 lint: mypy - lint: types-pkg_resources + lint: types-pkg_resources; python_version < '3.12' lint: types-requests lint: types-Flask lint: types-contextvars From 09c0f411bd72fc209c8fed5612ad7146c50b7695 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 11 Nov 2024 16:44:36 +0000 Subject: [PATCH 03/23] update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a856b6d3..de416556 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## TBD + +### Enhancements + +* Remove depricated `datetime.utcnow()` method call from utils class + [#394](https://github.com/bugsnag/bugsnag-python/pull/394). + ## v4.7.1 (2024-05-22) ### Bug fixes From c55bb0a9f0ad0a48cba83cbb28afdb1db886542e Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Thu, 5 Dec 2024 16:22:35 +0000 Subject: [PATCH 04/23] Add v3.13 to test matrices --- .github/workflows/maze-runner.yml | 2 +- .github/workflows/python-package.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maze-runner.yml b/.github/workflows/maze-runner.yml index 9d89beb0..ac3d3e7c 100644 --- a/.github/workflows/maze-runner.yml +++ b/.github/workflows/maze-runner.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 06de675a..a955560e 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] os: ['ubuntu-latest'] include: - python-version: '3.5' From 097708599acbcf5252dd470372a81643d293930f Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Thu, 5 Dec 2024 16:34:11 +0000 Subject: [PATCH 05/23] Skip python 3.13 in celery 4 tests --- features/celery.feature | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/features/celery.feature b/features/celery.feature index c78ce0c9..85ca139e 100644 --- a/features/celery.feature +++ b/features/celery.feature @@ -12,7 +12,7 @@ Scenario Outline: Handled exceptions are delivered in Celery And the event "severityReason.type" equals "handledException" And the event "device.runtimeVersions.celery" matches "\.\d+\.\d+" - @not-python-3.11 @not-python-3.12 + @not-python-3.11 @not-python-3.12 @not-python-3.13 Examples: | celery-version | | 4 | @@ -40,7 +40,7 @@ Scenario Outline: Unhandled exceptions are delivered in Celery And the event "metaData.extra_data.args" string is empty And the event "metaData.extra_data.kwargs" string is empty - @not-python-3.11 @not-python-3.12 + @not-python-3.11 @not-python-3.12 @not-python-3.13 Examples: | celery-version | | 4 | @@ -72,7 +72,7 @@ Scenario Outline: Task arguments are added to metadata in Celery And the event "metaData.extra_data.args.1" equals "0" And the event "metaData.extra_data.kwargs" string is empty - @not-python-3.11 @not-python-3.12 + @not-python-3.11 @not-python-3.12 @not-python-3.13 Examples: | celery-version | | 4 | @@ -116,7 +116,7 @@ Scenario Outline: Successful tasks do not report errors in Celery " Then I should receive no errors - @not-python-3.11 @not-python-3.12 + @not-python-3.11 @not-python-3.12 @not-python-3.13 Examples: | celery-version | | 4 | @@ -131,7 +131,7 @@ Scenario Outline: Successful shared tasks do not report errors in Celery " Then I should receive no errors - @not-python-3.11 @not-python-3.12 + @not-python-3.11 @not-python-3.12 @not-python-3.13 Examples: | celery-version | | 4 | From 145b796ad739509abc29326298b183243005ab84 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Thu, 12 Dec 2024 00:06:58 +0000 Subject: [PATCH 06/23] Update tox.ini for 3.13 tests --- tox.ini | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tox.ini b/tox.ini index 325d6b3d..f1ab306d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,15 +1,15 @@ [tox] envlist= - py{35,36,37,38,39,310,311,312}-{test,requests,flask,tornado,wsgi,bottle} - py{36,37,38,39,310,311,312}-asgi + py{35,36,37,38,39,310,311,312,313}-{test,requests,flask,tornado,wsgi,bottle} + py{36,37,38,39,310,311,312,313}-asgi py{35,36,37}-django{18,19,110,111} py{35,36,37,38,39}-django20 py{35,36,37,38,39,310}-django{21,22} - py{36,37,38,39,310,311,312}-django3 - py{38,39,310,311,312}-django4 - py{38,39,310,311,312}-{asynctest,threadtest} - py{37,38,39,310,311,312}-exceptiongroup - py{35,312}-{lint} + py{36,37,38,39,310,311,312,313}-django3 + py{38,39,310,311,312,313}-django4 + py{38,39,310,311,312,313}-{asynctest,threadtest} + py{37,38,39,310,311,312,313}-exceptiongroup + py{35,312,313}-{lint} [pytest] testpaths = tests @@ -32,6 +32,7 @@ basepython = py310: python3.10 py311: python3.11 py312: python3.12 + py313: python3.13 whitelist_externals= {toxinidir}/scripts/lint.sh deps= From f5e02367778a7e74d1cf47e2320d5f435644b805 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Thu, 12 Dec 2024 10:09:34 +0000 Subject: [PATCH 07/23] Push lint to the latest version and stop using yanked packages --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index f1ab306d..4de4bbd6 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,7 @@ envlist= py{38,39,310,311,312,313}-django4 py{38,39,310,311,312,313}-{asynctest,threadtest} py{37,38,39,310,311,312,313}-exceptiongroup - py{35,312,313}-{lint} + py{35,313}-{lint} [pytest] testpaths = tests @@ -65,7 +65,7 @@ deps= exceptiongroup: exceptiongroup lint: flake8 lint: mypy - lint: types-pkg_resources + lint: types-setuptools lint: types-requests lint: types-Flask lint: types-contextvars From d1e11308ecd8ab1e1cb8f8522d4d7c5e25548460 Mon Sep 17 00:00:00 2001 From: Tom Longridge Date: Fri, 13 Dec 2024 10:17:48 +0000 Subject: [PATCH 08/23] test: remove gemfile lock --- .gitignore | 1 + Gemfile.lock | 143 --------------------------------------------------- 2 files changed, 1 insertion(+), 143 deletions(-) delete mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore index 93f38ee8..6d71be16 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .aws-sam maze-runner.log maze_output +Gemfile.lock *.py[co] diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 57b86365..00000000 --- a/Gemfile.lock +++ /dev/null @@ -1,143 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - appium_lib (12.0.1) - appium_lib_core (~> 5.0) - nokogiri (~> 1.8, >= 1.8.1) - tomlrb (>= 1.1, < 3.0) - appium_lib_core (5.4.0) - faye-websocket (~> 0.11.0) - selenium-webdriver (~> 4.2, < 4.6) - bugsnag (6.27.1) - concurrent-ruby (~> 1.0) - bugsnag-maze-runner (9.9.0) - appium_lib (~> 12.0.0) - appium_lib_core (~> 5.4.0) - bugsnag (~> 6.24) - cucumber (~> 7.1) - cucumber-expressions (~> 6.0.0) - curb (~> 0.9.6) - dogstatsd-ruby (~> 5.5.0) - json_schemer (~> 0.2.24) - optimist (~> 3.0.1) - os (~> 1.0.0) - rack (~> 2.2) - rake (~> 12.3.3) - rubyzip (~> 2.3.2) - selenium-webdriver (~> 4.0) - test-unit (~> 3.5.2) - webrick (~> 1.7.0) - builder (3.3.0) - childprocess (4.1.0) - concurrent-ruby (1.3.3) - cucumber (7.1.0) - builder (~> 3.2, >= 3.2.4) - cucumber-core (~> 10.1, >= 10.1.0) - cucumber-create-meta (~> 6.0, >= 6.0.1) - cucumber-cucumber-expressions (~> 14.0, >= 14.0.0) - cucumber-gherkin (~> 22.0, >= 22.0.0) - cucumber-html-formatter (~> 17.0, >= 17.0.0) - cucumber-messages (~> 17.1, >= 17.1.1) - cucumber-wire (~> 6.2, >= 6.2.0) - diff-lcs (~> 1.4, >= 1.4.4) - mime-types (~> 3.3, >= 3.3.1) - multi_test (~> 0.1, >= 0.1.2) - sys-uname (~> 1.2, >= 1.2.2) - cucumber-core (10.1.1) - cucumber-gherkin (~> 22.0, >= 22.0.0) - cucumber-messages (~> 17.1, >= 17.1.1) - cucumber-tag-expressions (~> 4.1, >= 4.1.0) - cucumber-create-meta (6.0.4) - cucumber-messages (~> 17.1, >= 17.1.1) - sys-uname (~> 1.2, >= 1.2.2) - cucumber-cucumber-expressions (14.0.0) - cucumber-expressions (6.0.1) - cucumber-gherkin (22.0.0) - cucumber-messages (~> 17.1, >= 17.1.1) - cucumber-html-formatter (17.0.0) - cucumber-messages (~> 17.1, >= 17.1.0) - cucumber-messages (17.1.1) - cucumber-tag-expressions (4.1.0) - cucumber-wire (6.2.1) - cucumber-core (~> 10.1, >= 10.1.0) - cucumber-cucumber-expressions (~> 14.0, >= 14.0.0) - curb (0.9.11) - diff-lcs (1.5.1) - dogstatsd-ruby (5.5.0) - ecma-re-validator (0.4.0) - regexp_parser (~> 2.2) - eventmachine (1.2.7) - faye-websocket (0.11.3) - eventmachine (>= 0.12.0) - websocket-driver (>= 0.5.1) - ffi (1.17.0-aarch64-linux-gnu) - ffi (1.17.0-arm-linux-gnu) - ffi (1.17.0-arm64-darwin) - ffi (1.17.0-x86-linux-gnu) - ffi (1.17.0-x86_64-darwin) - ffi (1.17.0-x86_64-linux-gnu) - hana (1.3.7) - json_schemer (0.2.25) - ecma-re-validator (~> 0.3) - hana (~> 1.3) - regexp_parser (~> 2.0) - simpleidn (~> 0.2) - uri_template (~> 0.7) - mime-types (3.5.2) - mime-types-data (~> 3.2015) - mime-types-data (3.2024.0604) - multi_test (0.1.2) - nokogiri (1.16.6-aarch64-linux) - racc (~> 1.4) - nokogiri (1.16.6-arm-linux) - racc (~> 1.4) - nokogiri (1.16.6-arm64-darwin) - racc (~> 1.4) - nokogiri (1.16.6-x86-linux) - racc (~> 1.4) - nokogiri (1.16.6-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.16.6-x86_64-linux) - racc (~> 1.4) - optimist (3.0.1) - os (1.0.1) - power_assert (2.0.3) - racc (1.8.0) - rack (2.2.9) - rake (12.3.3) - regexp_parser (2.9.2) - rexml (3.3.0) - strscan - rubyzip (2.3.2) - selenium-webdriver (4.5.0) - childprocess (>= 0.5, < 5.0) - rexml (~> 3.2, >= 3.2.5) - rubyzip (>= 1.2.2, < 3.0) - websocket (~> 1.0) - simpleidn (0.2.3) - strscan (3.1.0) - sys-uname (1.3.0) - ffi (~> 1.1) - test-unit (3.5.9) - power_assert - tomlrb (2.0.3) - uri_template (0.7.0) - webrick (1.7.0) - websocket (1.2.10) - websocket-driver (0.7.6) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.5) - -PLATFORMS - aarch64-linux - arm-linux - arm64-darwin - x86-linux - x86_64-darwin - x86_64-linux - -DEPENDENCIES - bugsnag-maze-runner (~> 9.6) - -BUNDLED WITH - 2.5.1 From 5a6036a7f8858de1309ab29e85881388d785bd77 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Wed, 18 Dec 2024 13:39:02 +0000 Subject: [PATCH 09/23] Ensure Python 3.7 tests are run on an older ubuntu version --- .github/workflows/python-package.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index a955560e..8f807294 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] os: ['ubuntu-latest'] include: - python-version: '3.5' @@ -17,6 +17,8 @@ jobs: pip-trusted-host: 'pypi.python.org pypi.org files.pythonhosted.org' - python-version: '3.6' os: 'ubuntu-20.04' + - python-version: '3.7' + os: 'ubuntu-22.04' steps: - uses: actions/checkout@v4 From 699ea1748db97860ec8b6532a1df501798561eab Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Wed, 18 Jun 2025 10:57:01 +0100 Subject: [PATCH 10/23] Remove license checker (failing to detect valid licenses) --- .github/workflows/license-audit.yml | 39 ----------------------------- 1 file changed, 39 deletions(-) delete mode 100644 .github/workflows/license-audit.yml diff --git a/.github/workflows/license-audit.yml b/.github/workflows/license-audit.yml deleted file mode 100644 index 951828ca..00000000 --- a/.github/workflows/license-audit.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Audit bugsnag-python dependency licenses - -on: [push, pull_request] - -jobs: - license-audit: - runs-on: 'ubuntu-latest' - - steps: - - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - # License Finder's Docker image uses Python 3.10 - python-version: '3.10' - - - name: Fetch decisions.yml - run: curl https://raw.githubusercontent.com/bugsnag/license-audit/master/config/decision_files/global.yml -o decisions.yml - - # License Finder doesn't use "install_requires" from setup.py, so won't check - # our dependencies if we don't put them in a requirements.txt file - - name: Set up requirements.txt for License Finder - run: | - pip3 install '.[flask]' - pip3 freeze --local --exclude bugsnag | tee requirements.txt - - - name: Run License Finder - # for some reason license finder doesn't run without a login shell (-l) - run: > - docker run -v $PWD:/scan licensefinder/license_finder /bin/bash -lc " - cd /scan && - apt-get update && - apt-get install -y python3-venv && - python3 -m venv .venv && - source .venv/bin/activate && - pip3 install -r requirements.txt && - license_finder --decisions-file decisions.yml --python-version 3 --enabled-package-managers=pip - " From 265c44b4a6166731858e1cb15416bc361a0863e2 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Wed, 18 Jun 2025 10:57:12 +0100 Subject: [PATCH 11/23] Ignore IntelliJ module files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6d71be16..9a828436 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ htmlcov my_env venv .idea +*.iml From 283a1ea568509c2dc2a2247265115b15a474b3e4 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Wed, 18 Jun 2025 10:57:45 +0100 Subject: [PATCH 12/23] Bump runners - Ubuntu 20 no longer available --- .github/workflows/python-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 8f807294..ca3dda54 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -13,10 +13,10 @@ jobs: os: ['ubuntu-latest'] include: - python-version: '3.5' - os: 'ubuntu-20.04' + os: 'ubuntu-22.04' pip-trusted-host: 'pypi.python.org pypi.org files.pythonhosted.org' - python-version: '3.6' - os: 'ubuntu-20.04' + os: 'ubuntu-22.04' - python-version: '3.7' os: 'ubuntu-22.04' From 940469785960c111fd1dbfa3c1d578baf5c27a0d Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Wed, 18 Jun 2025 18:56:01 +0100 Subject: [PATCH 13/23] Skip test pending further investigation --- tests/integrations/test_asgi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integrations/test_asgi.py b/tests/integrations/test_asgi.py index 47441a5c..e998f32f 100644 --- a/tests/integrations/test_asgi.py +++ b/tests/integrations/test_asgi.py @@ -191,6 +191,7 @@ async def index(req): assert breadcrumbs[0]['metaData'] == {'to': '/'} assert breadcrumbs[0]['type'] == BreadcrumbType.NAVIGATION.value + @pytest.mark.skip(reason="Skipped pending PLAT-14413") def test_websocket_crash(self): async def app(scope, receive, send): websocket = WebSocket(scope, receive=receive, send=send) From 015d4192ed952cd80fc2bbb1c296cb7e617f6297 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Wed, 18 Jun 2025 19:04:07 +0100 Subject: [PATCH 14/23] Skip unit/integration tests with Python 3.5/3.6 --- .github/workflows/python-package.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index ca3dda54..1e1cb2a7 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -12,11 +12,12 @@ jobs: python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] os: ['ubuntu-latest'] include: - - python-version: '3.5' - os: 'ubuntu-22.04' - pip-trusted-host: 'pypi.python.org pypi.org files.pythonhosted.org' - - python-version: '3.6' - os: 'ubuntu-22.04' +# Python 3.5 and 3.6 tests skipped pending PLAT-14414 +# - python-version: '3.5' +# os: 'ubuntu-22.04' +# pip-trusted-host: 'pypi.python.org pypi.org files.pythonhosted.org' +# - python-version: '3.6' +# os: 'ubuntu-22.04' - python-version: '3.7' os: 'ubuntu-22.04' From bffdb6658b7239d483c627209c301ed95cfa126f Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Thu, 19 Jun 2025 15:48:03 +0100 Subject: [PATCH 15/23] WIP --- CHANGELOG.md | 4 +++- bugsnag/configuration.py | 40 +++++++++++++++++++++++++++---------- bugsnag/delivery.py | 6 ++++-- tests/test_configuration.py | 2 +- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de416556..3495a5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,10 @@ Changelog ### Enhancements -* Remove depricated `datetime.utcnow()` method call from utils class +* Remove deprecated `datetime.utcnow()` method call from utils class [#394](https://github.com/bugsnag/bugsnag-python/pull/394). +* Set default endpoints based on API key + [#399](https://github.com/bugsnag/bugsnag-php/pull/399) ## v4.7.1 (2024-05-22) diff --git a/bugsnag/configuration.py b/bugsnag/configuration.py index 114de35b..15bf9cc6 100644 --- a/bugsnag/configuration.py +++ b/bugsnag/configuration.py @@ -30,8 +30,11 @@ validate_int_setter, validate_path_setter ) -from bugsnag.delivery import (create_default_delivery, DEFAULT_ENDPOINT, - DEFAULT_SESSIONS_ENDPOINT) +from bugsnag.delivery import (create_default_delivery, + DEFAULT_ENDPOINT, + DEFAULT_SESSIONS_ENDPOINT, + HUB_ENDPOINT, + HUB_SESSIONS_ENDPOINT) from bugsnag.uwsgi import warn_if_running_uwsgi_without_threads from bugsnag.error import Error @@ -55,6 +58,11 @@ _sentinel = object() +def _is_hub_api_key(api_key: str) -> bool: + hub_prefix = "00000" + return api_key is not None and api_key.startswith(hub_prefix) + + class Configuration: """ Global app-level Bugsnag configuration settings. @@ -83,8 +91,8 @@ def __init__(self, logger=_sentinel): "django.http.Http404", "django.http.response.Http404", ] - self.endpoint = DEFAULT_ENDPOINT - self.session_endpoint = DEFAULT_SESSIONS_ENDPOINT + self.endpoint = None + self.session_endpoint = None self.auto_capture_sessions = True self.traceback_exclude_modules = [] @@ -126,8 +134,6 @@ def configure(self, api_key=None, app_type=None, app_version=None, Validate and set configuration options. Will warn if an option is of an incorrect type. """ - if api_key is not None: - self.api_key = api_key if app_type is not None: self.app_type = app_type if app_version is not None: @@ -140,8 +146,6 @@ def configure(self, api_key=None, app_type=None, app_version=None, self.auto_capture_sessions = auto_capture_sessions if delivery is not None: self.delivery = delivery - if endpoint is not None: - self.endpoint = endpoint if hostname is not None: self.hostname = hostname if ignore_classes is not None: @@ -162,8 +166,6 @@ def configure(self, api_key=None, app_type=None, app_version=None, self.send_code = send_code if send_environment is not None: self.send_environment = send_environment - if session_endpoint is not None: - self.session_endpoint = session_endpoint if traceback_exclude_modules is not None: self.traceback_exclude_modules = traceback_exclude_modules if logger is not _sentinel: @@ -175,6 +177,11 @@ def configure(self, api_key=None, app_type=None, app_version=None, if max_breadcrumbs is not None: self.max_breadcrumbs = max_breadcrumbs + # Default endpoints depend on the API key + if api_key is not None: + self.api_key = api_key + self._initialize_endpoints(endpoint, session_endpoint, self.api_key) + return self def get(self, name): @@ -584,6 +591,19 @@ def _create_null_logger(self) -> logging.Logger: return logger + def _initialize_endpoints(self, endpoint, session_endpoint, api_key): + if endpoint is None and session_endpoint is None: + if _is_hub_api_key(api_key): + self.endpoint = HUB_ENDPOINT + self.session_endpoint = HUB_SESSIONS_ENDPOINT + else: + self.endpoint = DEFAULT_ENDPOINT + self.session_endpoint = DEFAULT_SESSIONS_ENDPOINT + if endpoint is not None: + self.endpoint = endpoint + if session_endpoint is not None: + self.session_endpoint = session_endpoint + class RequestConfiguration: """ diff --git a/bugsnag/delivery.py b/bugsnag/delivery.py index 6dc8cd05..f36d121a 100644 --- a/bugsnag/delivery.py +++ b/bugsnag/delivery.py @@ -28,6 +28,8 @@ DEFAULT_ENDPOINT = 'https://notify.bugsnag.com' DEFAULT_SESSIONS_ENDPOINT = 'https://sessions.bugsnag.com' +HUB_ENDPOINT = 'https://notify.insighthub.smartbear.com' +HUB_SESSIONS_ENDPOINT = 'https://sessions.insighthub.smartbear.com' __all__ = ('default_headers', 'Delivery') @@ -82,8 +84,8 @@ def deliver_sessions(self, config, payload: Any, options=None): """ Sends sessions to Bugsnag """ - if (config.endpoint != DEFAULT_ENDPOINT and config.session_endpoint == - DEFAULT_SESSIONS_ENDPOINT): + if ((config.endpoint != DEFAULT_ENDPOINT and config.session_endpoint == DEFAULT_SESSIONS_ENDPOINT) or + (config.endpoint != HUB_ENDPOINT and config.session_endpoint == HUB_SESSIONS_ENDPOINT)): if not self.sent_session_warning: warnings.warn('The session endpoint has not been configured. ' 'No sessions will be sent to Bugsnag.') diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 03266940..439da5c9 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -83,7 +83,7 @@ def test_hostname(self): def test_session_tracking_defaults(self): c = Configuration() self.assertTrue(c.auto_capture_sessions) - self.assertEqual(c.session_endpoint, "https://sessions.bugsnag.com") + self.assertEqual(c.session_endpoint, None) def test_default_middleware_location(self): c = Configuration() From 8e7508c53a743a7753a4646648af42641ba823e3 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Thu, 19 Jun 2025 17:17:39 +0100 Subject: [PATCH 16/23] Only default endpoints if not already set --- bugsnag/configuration.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bugsnag/configuration.py b/bugsnag/configuration.py index 15bf9cc6..f6bb9a11 100644 --- a/bugsnag/configuration.py +++ b/bugsnag/configuration.py @@ -592,13 +592,16 @@ def _create_null_logger(self) -> logging.Logger: return logger def _initialize_endpoints(self, endpoint, session_endpoint, api_key): - if endpoint is None and session_endpoint is None: + # Default endpoints depending on the API key, if not already set + if (endpoint is None and session_endpoint is None and + self.endpoint is None and self.session_endpoint is None): if _is_hub_api_key(api_key): self.endpoint = HUB_ENDPOINT self.session_endpoint = HUB_SESSIONS_ENDPOINT else: self.endpoint = DEFAULT_ENDPOINT self.session_endpoint = DEFAULT_SESSIONS_ENDPOINT + # Do set endpoints if explicitly provided if endpoint is not None: self.endpoint = endpoint if session_endpoint is not None: From e5b0227c83c176a0fa3deaad25923d176287bd82 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Thu, 19 Jun 2025 18:46:59 +0100 Subject: [PATCH 17/23] Fix linter errors --- bugsnag/configuration.py | 2 +- bugsnag/delivery.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bugsnag/configuration.py b/bugsnag/configuration.py index f6bb9a11..be5df9e4 100644 --- a/bugsnag/configuration.py +++ b/bugsnag/configuration.py @@ -594,7 +594,7 @@ def _create_null_logger(self) -> logging.Logger: def _initialize_endpoints(self, endpoint, session_endpoint, api_key): # Default endpoints depending on the API key, if not already set if (endpoint is None and session_endpoint is None and - self.endpoint is None and self.session_endpoint is None): + self.endpoint is None and self.session_endpoint is None): if _is_hub_api_key(api_key): self.endpoint = HUB_ENDPOINT self.session_endpoint = HUB_SESSIONS_ENDPOINT diff --git a/bugsnag/delivery.py b/bugsnag/delivery.py index f36d121a..1a8f03cb 100644 --- a/bugsnag/delivery.py +++ b/bugsnag/delivery.py @@ -84,8 +84,10 @@ def deliver_sessions(self, config, payload: Any, options=None): """ Sends sessions to Bugsnag """ - if ((config.endpoint != DEFAULT_ENDPOINT and config.session_endpoint == DEFAULT_SESSIONS_ENDPOINT) or - (config.endpoint != HUB_ENDPOINT and config.session_endpoint == HUB_SESSIONS_ENDPOINT)): + if ((config.endpoint != DEFAULT_ENDPOINT and + config.session_endpoint == DEFAULT_SESSIONS_ENDPOINT) or + (config.endpoint != HUB_ENDPOINT and + config.session_endpoint == HUB_SESSIONS_ENDPOINT)): if not self.sent_session_warning: warnings.warn('The session endpoint has not been configured. ' 'No sessions will be sent to Bugsnag.') From 2fae759d62f07f133d06608cf8762fce7fc4e046 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Thu, 19 Jun 2025 18:57:26 +0100 Subject: [PATCH 18/23] 5 space indent --- bugsnag/configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugsnag/configuration.py b/bugsnag/configuration.py index be5df9e4..239786da 100644 --- a/bugsnag/configuration.py +++ b/bugsnag/configuration.py @@ -594,7 +594,7 @@ def _create_null_logger(self) -> logging.Logger: def _initialize_endpoints(self, endpoint, session_endpoint, api_key): # Default endpoints depending on the API key, if not already set if (endpoint is None and session_endpoint is None and - self.endpoint is None and self.session_endpoint is None): + self.endpoint is None and self.session_endpoint is None): if _is_hub_api_key(api_key): self.endpoint = HUB_ENDPOINT self.session_endpoint = HUB_SESSIONS_ENDPOINT From 654734fbce0b7ab38426bd1675c20c4d525c39e7 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Thu, 19 Jun 2025 19:02:38 +0100 Subject: [PATCH 19/23] 4 space indent --- bugsnag/configuration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bugsnag/configuration.py b/bugsnag/configuration.py index 239786da..8e94fb1d 100644 --- a/bugsnag/configuration.py +++ b/bugsnag/configuration.py @@ -594,7 +594,8 @@ def _create_null_logger(self) -> logging.Logger: def _initialize_endpoints(self, endpoint, session_endpoint, api_key): # Default endpoints depending on the API key, if not already set if (endpoint is None and session_endpoint is None and - self.endpoint is None and self.session_endpoint is None): + self.endpoint is None and self.session_endpoint is None): + if _is_hub_api_key(api_key): self.endpoint = HUB_ENDPOINT self.session_endpoint = HUB_SESSIONS_ENDPOINT From 794a08909b8c03f1c1ec74c054b902c31c80f5f5 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Thu, 19 Jun 2025 19:09:58 +0100 Subject: [PATCH 20/23] bracket on new line --- bugsnag/configuration.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bugsnag/configuration.py b/bugsnag/configuration.py index 8e94fb1d..20125937 100644 --- a/bugsnag/configuration.py +++ b/bugsnag/configuration.py @@ -593,9 +593,12 @@ def _create_null_logger(self) -> logging.Logger: def _initialize_endpoints(self, endpoint, session_endpoint, api_key): # Default endpoints depending on the API key, if not already set - if (endpoint is None and session_endpoint is None and - self.endpoint is None and self.session_endpoint is None): - + if ( + endpoint is None and + session_endpoint is None and + self.endpoint is None and + self.session_endpoint is None + ): if _is_hub_api_key(api_key): self.endpoint = HUB_ENDPOINT self.session_endpoint = HUB_SESSIONS_ENDPOINT From 0b44ee06878c6056efe5d788bd8f3553a25679e9 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Fri, 20 Jun 2025 13:55:11 +0100 Subject: [PATCH 21/23] Unit tests added --- tests/test_configuration.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 439da5c9..57dfe03e 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -114,6 +114,16 @@ def test_validate_endpoint(self): c.configure(endpoint='https://notify.example.com') assert c.endpoint == 'https://notify.example.com' + def test_validate_endpoint_bugsnag_api_key(self): + c = Configuration() + c.configure(api_key='12312312312312312312312312321312') + assert c.endpoint == 'https://notify.bugsnag.com' + + def test_validate_endpoint_hub_api_key(self): + c = Configuration() + c.configure(api_key='00000312312312312312312312321312') + assert c.endpoint == 'https://notify.insighthub.smartbear.com' + def test_validate_app_type(self): c = Configuration() assert c.app_type is None @@ -410,6 +420,17 @@ def test_validate_session_endpoint(self): c.configure(session_endpoint='https://sessions.example.com') assert c.session_endpoint == 'https://sessions.example.com' + def test_validate_session_endpoint_bugsnag_api_key(self): + c = Configuration() + c.configure(api_key='12312312312312312312312312321312') + assert c.session_endpoint == 'https://sessions.bugsnag.com' + + def test_validate_session_endpoint_hub_api_key(self): + c = Configuration() + c.configure(api_key='00000312312312312312312312321312') + assert (c.session_endpoint == + 'https://sessions.insighthub.smartbear.com') + def test_validate_traceback_exclude_modules(self): c = Configuration() with pytest.warns(RuntimeWarning) as record: From 46a749c5b12210ba71a95b5b736d6b0b62b7f387 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Fri, 20 Jun 2025 18:15:44 +0100 Subject: [PATCH 22/23] Do not configure Client when initialised for singleton clients --- bugsnag/client.py | 5 +++-- bugsnag/legacy.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bugsnag/client.py b/bugsnag/client.py index 558b074a..9717ce9a 100644 --- a/bugsnag/client.py +++ b/bugsnag/client.py @@ -32,10 +32,11 @@ class Client: """ def __init__(self, configuration: Optional[Configuration] = None, - install_sys_hook=True, **kwargs): + install_sys_hook=True, configure=True, **kwargs): self.configuration = configuration or Configuration() # type: Configuration # noqa: E501 self.session_tracker = SessionTracker(self.configuration) - self.configuration.configure(**kwargs) + if configure: + self.configuration.configure(**kwargs) self._context = ContextLocalState(self) self._request_tracker = RequestTracker() diff --git a/bugsnag/legacy.py b/bugsnag/legacy.py index 018e6735..32848b52 100644 --- a/bugsnag/legacy.py +++ b/bugsnag/legacy.py @@ -7,7 +7,7 @@ from bugsnag.configuration import RequestConfiguration from bugsnag.client import Client -default_client = Client() +default_client = Client(configure=False) configuration = default_client.configuration logger = configuration.logger ExcInfoType = Tuple[Type, Exception, types.TracebackType] From 1e09d28388bc34f82e924d4c268ae924d15eda25 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Tue, 8 Jul 2025 17:22:58 +0100 Subject: [PATCH 23/23] Release v4.8.0 --- CHANGELOG.md | 2 +- bugsnag/notifier.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3495a5ba..315b7f61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Changelog ========= -## TBD +## v4.8.0 (2024-07-08) ### Enhancements diff --git a/bugsnag/notifier.py b/bugsnag/notifier.py index 6f011f0e..c9b3b212 100644 --- a/bugsnag/notifier.py +++ b/bugsnag/notifier.py @@ -1,5 +1,5 @@ _NOTIFIER_INFORMATION = { 'name': 'Python Bugsnag Notifier', 'url': 'https://github.com/bugsnag/bugsnag-python', - 'version': '4.7.1' + 'version': '4.8.0' } diff --git a/setup.py b/setup.py index ddd999a0..7c0b5e2e 100755 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ setup( name='bugsnag', - version='4.7.1', + version='4.8.0', description='Automatic error monitoring for django, flask, etc.', long_description=__doc__, author='Simon Maynard',