Repository: Unleash/unleash-client-python
Pull request #159 (2021-06-04 21:18:09)

Allow to run unleash clients as context managers
# Description

The

```
with UnleashClient(...) as client:
    client.is_enabled(...)
```

idiom is very pythonic. It reduces the code size over the current
explicit initialization and destruction and ensures the client will be
destroyed even if there are exceptions during its lifetime (i.e, the
user's program fails before destroying its client).

## Type of change

- [ ] New feature (non-breaking change which adds functionality)
- [ ] This change requires a documentation update

# How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

- [ ] Unit tests

# Checklist:

- [x ] My code follows the style guidelines of this project
- [ x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [x ] I have made corresponding changes to the documentation
- [x ] My changes generate no new warnings
- [ x] I have added tests that prove my fix is effective or that my feature works
- [ x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules


Patch:
diff --git a/UnleashClient/__init__.py b/UnleashClient/__init__.py
index bbb587d..873935d 100644
--- a/UnleashClient/__init__.py
+++ b/UnleashClient/__init__.py
@@ -13,7 +13,6 @@ from UnleashClient.constants import METRIC_LAST_SENT_TIME, DISABLED_VARIATION
 from .utils import LOGGER
 from .deprecation_warnings import strategy_v2xx_deprecation_check
 
-
 # pylint: disable=dangerous-default-value
 class UnleashClient:
     """Client implementation."""
@@ -237,3 +236,11 @@ class UnleashClient:
             LOGGER.log(self.unleash_verbose_log_level, "Returning default flag/variation for feature: %s", feature_name)
             LOGGER.log(self.unleash_verbose_log_level, "Attempted to get feature flag/variation %s, but client wasn't initialized!", feature_name)
             return DISABLED_VARIATION
+
+    def __enter__(self) -> "UnleashClient":
+        self.initialize_client()
+        return self
+
+    def __exit__(self, *args, **kwargs):
+        self.destroy()
+        return False
diff --git a/docs/unleashclient.md b/docs/unleashclient.md
index ec19c7e..4f8a78c 100644
--- a/docs/unleashclient.md
+++ b/docs/unleashclient.md
@@ -33,7 +33,17 @@ This kicks off:
 * Provisioning poll
 * Stats poll
 
-This will raise an exception on registration if the URL is invalid.
+This will raise an exception on registration if the URL is invalid. It
+is done automatically if called inside a context manager as in:
+
+``` python
+with UnleashClient(
+    url="https://foo.bar",
+    app_name="myClient1",
+    instance_id="myinstanceid"
+    ) as client:
+    pass
+```
 
 ### `destroy()`
 Gracefully shuts down the Unleash client by stopping jobs, stopping the scheduler, and deleting the cache.


Test patch:
diff --git a/tests/unit_tests/test_client.py b/tests/unit_tests/test_client.py
index ef20468..54c049c 100644
--- a/tests/unit_tests/test_client.py
+++ b/tests/unit_tests/test_client.py
@@ -252,6 +252,16 @@ def test_uc_is_enabled_error_states(unleash_client):
     assert unleash_client.is_enabled("ThisFlagDoesn'tExist", fallback_function=lambda x, y: True)
 
 
+@responses.activate
+def test_uc_context_manager(unleash_client_nodestroy):
+    responses.add(responses.POST, URL + REGISTER_URL, json={}, status=202)
+    responses.add(responses.GET, URL + FEATURES_URL, json=MOCK_FEATURE_RESPONSE, status=200)
+    responses.add(responses.POST, URL + METRICS_URL, json={}, status=202)
+
+    with unleash_client_nodestroy as unleash_client:
+        assert unleash_client.is_initialized
+
+
 @responses.activate
 def test_uc_not_initialized_isenabled():
     unleash_client = UnleashClient(URL, APP_NAME)