Repository: PostHog/posthog-python

Issue:
Library does not properly handle feature flags with no % rollout
See sentry issue https://sentry.io/organizations/posthog/issues/2220836956/?project=1899813. If a feature flag is enabled for everyone without a % rollout, the client crashes. Notice that this is different from a 100% rollout which works fine.


```
unsupported operand type(s) for /: 'NoneType' and 'int'
```

posthoganalytics/client.py#L276
```python
response = _hash(key, distinct_id) <= (feature_flag['rollout_percentage'] / 100)
```

Discussion:

Patch:
diff --git a/posthog/client.py b/posthog/client.py
index 0588082..22157cf 100644
--- a/posthog/client.py
+++ b/posthog/client.py
@@ -330,8 +330,8 @@ class Client(object):
             except IndexError:
                 return default
 
-            if feature_flag.get("is_simple_flag") and feature_flag.get("rollout_percentage"):
-                response = _hash(key, distinct_id) <= (feature_flag["rollout_percentage"] / 100)
+            if feature_flag.get("is_simple_flag"):
+                response = _hash(key, distinct_id) <= ((feature_flag.get("rollout_percentage", 100) or 100) / 100)
             else:
                 try:
                     request_data = {


Test patch:
diff --git a/posthog/test/test_client.py b/posthog/test/test_client.py
index e4971db..9c867a0 100644
--- a/posthog/test/test_client.py
+++ b/posthog/test/test_client.py
@@ -325,14 +325,28 @@ class TestClient(unittest.TestCase):
         self.assertTrue(client.feature_enabled("beta-feature", "distinct_id"))
 
     @mock.patch("posthog.client.decide")
-    def test_feature_enabled_request(self, patch_get):
-        patch_get.return_value = {"featureFlags": ["beta-feature"]}
+    def test_feature_enabled_request(self, patch_decide):
+        patch_decide.return_value = {"featureFlags": ["beta-feature"]}
         client = Client(TEST_API_KEY)
         client.feature_flags = [
             {"id": 1, "name": "Beta Feature", "key": "beta-feature", "is_simple_flag": False, "rollout_percentage": 100}
         ]
         self.assertTrue(client.feature_enabled("beta-feature", "distinct_id"))
 
+    @mock.patch("posthog.client.get")
+    def test_feature_enabled_simple_without_rollout_percentage(self, patch_get):
+        client = Client(TEST_API_KEY)
+        client.feature_flags = [{"id": 1, "name": "Beta Feature", "key": "beta-feature", "is_simple_flag": True}]
+        self.assertTrue(client.feature_enabled("beta-feature", "distinct_id"))
+
+    @mock.patch("posthog.client.get")
+    def test_feature_enabled_simple_with_none_rollout_percentage(self, patch_get):
+        client = Client(TEST_API_KEY)
+        client.feature_flags = [
+            {"id": 1, "name": "Beta Feature", "key": "beta-feature", "is_simple_flag": True, "rollout_percantage": None}
+        ]
+        self.assertTrue(client.feature_enabled("beta-feature", "distinct_id"))
+
     @mock.patch("posthog.client.Poller")
     @mock.patch("posthog.client.get")
     def test_feature_enabled_doesnt_exist(self, patch_get, patch_poll):