From 405d24a94688ee19d9a68caae9c80a229583d53c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Jul 2026 00:16:10 +0000 Subject: [PATCH 1/4] Initial plan From b420c7bab1c563ae544af2c1c97aa60a29f26206 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Jul 2026 00:24:28 +0000 Subject: [PATCH 2/4] fix: defer default otel resource creation to init runtime --- solarwinds_apm/apm_config.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/solarwinds_apm/apm_config.py b/solarwinds_apm/apm_config.py index ab04481ea..bae464ce7 100644 --- a/solarwinds_apm/apm_config.py +++ b/solarwinds_apm/apm_config.py @@ -91,17 +91,20 @@ class SolarWindsApmConfig: def __init__( self, - otel_resource: Resource = Resource.create(), + otel_resource: Resource | None = None, **kwargs: int, ) -> None: """Initialize SolarWinds APM configuration. Parameters: - otel_resource (Resource): OpenTelemetry resource with detector attributes. + otel_resource (Resource | None): OpenTelemetry resource with detector attributes. In normal usage, passed from Configurator after detector resource created. - Defaults to Resource.create() for backward compatibility. + Defaults to None; Resource.create() is called at runtime for backward compatibility. **kwargs (int): Additional configuration keyword arguments. """ + if otel_resource is None: + otel_resource = Resource.create() + self.__config = {} # Update the config with default values self.__config = { From e0582e538d065d5251f6545af72ced1e7cf4e182 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Jul 2026 00:25:46 +0000 Subject: [PATCH 3/4] fix: avoid import-time default resource creation --- solarwinds_apm/apm_config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solarwinds_apm/apm_config.py b/solarwinds_apm/apm_config.py index bae464ce7..ab1c5a531 100644 --- a/solarwinds_apm/apm_config.py +++ b/solarwinds_apm/apm_config.py @@ -15,7 +15,7 @@ import os import re from functools import reduce -from typing import Any +from typing import Any, Optional from opentelemetry.environment_variables import OTEL_PROPAGATORS from opentelemetry.sdk.resources import Resource @@ -91,13 +91,13 @@ class SolarWindsApmConfig: def __init__( self, - otel_resource: Resource | None = None, + otel_resource: Optional[Resource] = None, # noqa: UP045 **kwargs: int, ) -> None: """Initialize SolarWinds APM configuration. Parameters: - otel_resource (Resource | None): OpenTelemetry resource with detector attributes. + otel_resource (Optional[Resource]): OpenTelemetry resource with detector attributes. In normal usage, passed from Configurator after detector resource created. Defaults to None; Resource.create() is called at runtime for backward compatibility. **kwargs (int): Additional configuration keyword arguments. From c5f731528263228bd71fe948a09d8cce81968ab4 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Thu, 9 Jul 2026 08:57:39 -0700 Subject: [PATCH 4/4] Modern union syntax, fix tests, update docstring --- solarwinds_apm/apm_config.py | 9 +++---- tests/unit/test_apm_config/test_apm_config.py | 26 ++++++++++++++++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/solarwinds_apm/apm_config.py b/solarwinds_apm/apm_config.py index ab1c5a531..a0b893772 100644 --- a/solarwinds_apm/apm_config.py +++ b/solarwinds_apm/apm_config.py @@ -15,7 +15,7 @@ import os import re from functools import reduce -from typing import Any, Optional +from typing import Any from opentelemetry.environment_variables import OTEL_PROPAGATORS from opentelemetry.sdk.resources import Resource @@ -91,15 +91,14 @@ class SolarWindsApmConfig: def __init__( self, - otel_resource: Optional[Resource] = None, # noqa: UP045 + otel_resource: Resource | None = None, **kwargs: int, ) -> None: """Initialize SolarWinds APM configuration. Parameters: - otel_resource (Optional[Resource]): OpenTelemetry resource with detector attributes. - In normal usage, passed from Configurator after detector resource created. - Defaults to None; Resource.create() is called at runtime for backward compatibility. + otel_resource (optional): OpenTelemetry resource with detector attributes. + In normal distro usage, passed from Configurator after resource detectors created. **kwargs (int): Additional configuration keyword arguments. """ if otel_resource is None: diff --git a/tests/unit/test_apm_config/test_apm_config.py b/tests/unit/test_apm_config/test_apm_config.py index 22a5090c5..2371bd2a5 100644 --- a/tests/unit/test_apm_config/test_apm_config.py +++ b/tests/unit/test_apm_config/test_apm_config.py @@ -157,7 +157,7 @@ def test__init_valid_service_key_format_otel_service_name( "SW_APM_SERVICE_KEY": "service_key_with:sw_service_name", "OTEL_SERVICE_NAME": "from_otel_env" }) - # Otel picks up os mock if Resource.create here (same as default arg) + # Explicitly pass Resource.create() to pick up env vars set above test_config = apm_config.SolarWindsApmConfig(Resource.create()) assert test_config.agent_enabled assert test_config.service_name == "from_otel_env" @@ -504,7 +504,11 @@ def test__update_service_key_name_agent_enabled_and_service_name_ok_service_key_ assert result == "weird-key:bar-service" def test__validate_log_filepath_none(self, mocker): - mock_exists = mocker.patch("solarwinds_apm.apm_config.os.path.exists") + mocker.patch( + "solarwinds_apm.apm_config.Resource.create", + return_value=Resource.get_empty() + ) + mock_exists = mocker.patch("solarwinds_apm.apm_config.os.path.exists", return_value=False) mock_makedirs = mocker.patch("solarwinds_apm.apm_config.os.makedirs") test_config = apm_config.SolarWindsApmConfig() @@ -515,7 +519,11 @@ def test__validate_log_filepath_none(self, mocker): assert test_config.get("log_filepath") == "" def test__validate_log_filepath_no_parent_path(self, mocker): - mock_exists = mocker.patch("solarwinds_apm.apm_config.os.path.exists") + mocker.patch( + "solarwinds_apm.apm_config.Resource.create", + return_value=Resource.get_empty() + ) + mock_exists = mocker.patch("solarwinds_apm.apm_config.os.path.exists", return_value=False) mock_makedirs = mocker.patch("solarwinds_apm.apm_config.os.makedirs") test_config = apm_config.SolarWindsApmConfig() @@ -526,6 +534,10 @@ def test__validate_log_filepath_no_parent_path(self, mocker): assert test_config.get("log_filepath") == "foo" def test__validate_log_filepath_path_exists(self, mocker): + mocker.patch( + "solarwinds_apm.apm_config.Resource.create", + return_value=Resource.get_empty() + ) mock_exists = mocker.patch("solarwinds_apm.apm_config.os.path.exists", return_value=True) mock_makedirs = mocker.patch("solarwinds_apm.apm_config.os.makedirs") @@ -537,6 +549,10 @@ def test__validate_log_filepath_path_exists(self, mocker): assert test_config.get("log_filepath") == "/path/to/foo" def test__validate_log_filepath_create_path(self, mocker): + mocker.patch( + "solarwinds_apm.apm_config.Resource.create", + return_value=Resource.get_empty() + ) mock_exists = mocker.patch("solarwinds_apm.apm_config.os.path.exists", return_value=False) mock_makedirs = mocker.patch("solarwinds_apm.apm_config.os.makedirs") @@ -548,6 +564,10 @@ def test__validate_log_filepath_create_path(self, mocker): assert test_config.get("log_filepath") == "/path/to/foo" def test__validate_log_filepath_cannot_create_reset_settings(self, mocker): + mocker.patch( + "solarwinds_apm.apm_config.Resource.create", + return_value=Resource.get_empty() + ) mock_exists = mocker.patch("solarwinds_apm.apm_config.os.path.exists", return_value=False) mock_makedirs = mocker.patch( "solarwinds_apm.apm_config.os.makedirs",