From 45f54506f87735f7cf0d5a81447868fdb191c2d4 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 16:36:00 +0000 Subject: [PATCH 1/2] feat: Add Config.with_wrapper_information Co-Authored-By: rlamb@launchdarkly.com <4955475+kinyoklion@users.noreply.github.com> --- ldclient/config.py | 16 ++++++++++++++++ ldclient/testing/test_config.py | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/ldclient/config.py b/ldclient/config.py index a58cc70b..a78b50cd 100644 --- a/ldclient/config.py +++ b/ldclient/config.py @@ -4,6 +4,7 @@ Note that the same class can also be imported from the ``ldclient.client`` submodule. """ +import copy import warnings from dataclasses import dataclass from threading import Event @@ -472,6 +473,21 @@ def copy_with_new_sdk_key(self, new_sdk_key: str) -> 'Config': big_segments=self.__big_segments, ) + def with_wrapper_information(self, wrapper_name: Optional[str], wrapper_version: Optional[str] = None) -> 'Config': + """Returns a new ``Config`` instance that is the same as this one, except for having different wrapper information. + + This is intended for use by wrapper libraries, such as the LaunchDarkly OpenFeature providers, which need to + identify themselves rather than the application that configured the client. + + :param wrapper_name: an identifying name for the wrapper being used; see :py:attr:`~wrapper_name` + :param wrapper_version: the version of the wrapper being used; see :py:attr:`~wrapper_version` + """ + updated = copy.copy(self) + updated.__wrapper_name = wrapper_name + updated.__wrapper_version = wrapper_version + + return updated + # for internal use only - probably should be part of the client logic def get_default(self, key, default): return default if key not in self.__defaults else self.__defaults[key] diff --git a/ldclient/testing/test_config.py b/ldclient/testing/test_config.py index b5321d36..3b18c3cf 100644 --- a/ldclient/testing/test_config.py +++ b/ldclient/testing/test_config.py @@ -17,6 +17,27 @@ def test_copy_config(): assert new_config.stream is False +def test_with_wrapper_information(): + config = Config(sdk_key="SDK_KEY", stream=False, wrapper_name="original", wrapper_version="1.0.0") + + wrapped = config.with_wrapper_information("wrapper", "2.0.0") + assert wrapped.wrapper_name == "wrapper" + assert wrapped.wrapper_version == "2.0.0" + assert wrapped.sdk_key == "SDK_KEY" + assert wrapped.stream is False + + assert config.wrapper_name == "original" + assert config.wrapper_version == "1.0.0" + + +def test_with_wrapper_information_defaults_the_version(): + config = Config(sdk_key="SDK_KEY", wrapper_name="original", wrapper_version="1.0.0") + + wrapped = config.with_wrapper_information("wrapper") + assert wrapped.wrapper_name == "wrapper" + assert wrapped.wrapper_version is None + + def test_can_set_valid_poll_interval(): config = Config(sdk_key="SDK_KEY", poll_interval=31) assert config.poll_interval == 31 From 6db02bf317f4aa1ffd633a928f5386e04f5e2322 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2026 17:17:56 +0000 Subject: [PATCH 2/2] docs: Note that the wrapper configuration copy is shallow Co-Authored-By: rlamb@launchdarkly.com <4955475+kinyoklion@users.noreply.github.com> --- ldclient/config.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ldclient/config.py b/ldclient/config.py index a78b50cd..24ce58af 100644 --- a/ldclient/config.py +++ b/ldclient/config.py @@ -479,6 +479,10 @@ def with_wrapper_information(self, wrapper_name: Optional[str], wrapper_version: This is intended for use by wrapper libraries, such as the LaunchDarkly OpenFeature providers, which need to identify themselves rather than the application that configured the client. + The new instance is a shallow copy: it shares the objects the original configuration references, such as the + feature store, the logger, and the HTTP configuration. Mutating one of those objects affects both + configurations. + :param wrapper_name: an identifying name for the wrapper being used; see :py:attr:`~wrapper_name` :param wrapper_version: the version of the wrapper being used; see :py:attr:`~wrapper_version` """