posthoganalytics 6.5.0__py3-none-any.whl → 6.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -215,7 +215,7 @@ class TestLocalEvaluation(unittest.TestCase):
215
215
  )
216
216
  self.assertEqual(patch_flags.call_count, 0)
217
217
 
218
- # Now group type mappings are gone, so fall back to /decide/
218
+ # Now group type mappings are gone, so fall back to /flags/
219
219
  patch_flags.return_value = {
220
220
  "featureFlags": {"group-flag": "decide-fallback-value"}
221
221
  }
@@ -311,7 +311,7 @@ class TestLocalEvaluation(unittest.TestCase):
311
311
  )
312
312
  self.assertEqual(patch_flags.call_count, 0)
313
313
 
314
- # will fall back on `/decide`, as all properties present for second group, but that group resolves to false
314
+ # will fall back on `/flags`, as all properties present for second group, but that group resolves to false
315
315
  self.assertEqual(
316
316
  client.get_feature_flag(
317
317
  "complex-flag",
@@ -651,7 +651,7 @@ class TestLocalEvaluation(unittest.TestCase):
651
651
  },
652
652
  },
653
653
  ]
654
- # beta-feature value overridden by /decide
654
+ # beta-feature value overridden by /flags
655
655
  self.assertEqual(
656
656
  client.get_all_flags("distinct_id"),
657
657
  {
@@ -725,7 +725,7 @@ class TestLocalEvaluation(unittest.TestCase):
725
725
  },
726
726
  },
727
727
  ]
728
- # beta-feature value overridden by /decide
728
+ # beta-feature value overridden by /flags
729
729
  self.assertEqual(
730
730
  client.get_all_flags_and_payloads("distinct_id")["featureFlagPayloads"],
731
731
  {
@@ -746,7 +746,7 @@ class TestLocalEvaluation(unittest.TestCase):
746
746
  }
747
747
  client = self.client
748
748
  client.feature_flags = []
749
- # beta-feature value overridden by /decide
749
+ # beta-feature value overridden by /flags
750
750
  self.assertEqual(
751
751
  client.get_all_flags("distinct_id"),
752
752
  {"beta-feature": "variant-1", "beta-feature2": "variant-2"},
@@ -765,7 +765,7 @@ class TestLocalEvaluation(unittest.TestCase):
765
765
  }
766
766
  client = self.client
767
767
  client.feature_flags = []
768
- # beta-feature value overridden by /decide
768
+ # beta-feature value overridden by /flags
769
769
  self.assertEqual(
770
770
  client.get_all_flags_and_payloads("distinct_id")["featureFlagPayloads"],
771
771
  {"beta-feature": 100, "beta-feature2": 300},
@@ -1361,6 +1361,8 @@ class TestLocalEvaluation(unittest.TestCase):
1361
1361
  def test_feature_flags_with_flag_dependencies(
1362
1362
  self, patch_get, patch_flags, mock_log
1363
1363
  ):
1364
+ # Mock remote flags call to return empty for this flag (fallback returns None)
1365
+ patch_flags.return_value = {"featureFlags": {}}
1364
1366
  client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
1365
1367
  client.feature_flags = [
1366
1368
  {
@@ -1377,6 +1379,7 @@ class TestLocalEvaluation(unittest.TestCase):
1377
1379
  "operator": "exact",
1378
1380
  "value": True,
1379
1381
  "type": "flag",
1382
+ "dependency_chain": ["beta-feature"],
1380
1383
  },
1381
1384
  {
1382
1385
  "key": "email",
@@ -1392,39 +1395,445 @@ class TestLocalEvaluation(unittest.TestCase):
1392
1395
  }
1393
1396
  ]
1394
1397
 
1395
- # Test that flag evaluation doesn't fail when encountering a flag dependency
1396
- # The flag should evaluate based on other conditions (email contains @example.com)
1397
- # Since flag dependencies aren't implemented, it should skip the flag condition
1398
- # and evaluate based on the email condition only
1398
+ # Test that flag evaluation handles flag dependencies properly
1399
+ # The flag has a dependency on "beta-feature" which doesn't exist locally
1400
+ # Since the dependency doesn't exist, local evaluation should fail and fall back to remote
1401
+ # Remote returns empty result, so final result is None
1399
1402
  feature_flag_match = client.get_feature_flag(
1400
1403
  "flag-with-dependencies",
1401
1404
  "test-user",
1402
1405
  person_properties={"email": "test@example.com"},
1403
1406
  )
1404
- self.assertEqual(feature_flag_match, True)
1405
- self.assertEqual(patch_flags.call_count, 0)
1407
+ self.assertIsNone(feature_flag_match)
1408
+ self.assertEqual(patch_flags.call_count, 1)
1406
1409
  self.assertEqual(patch_get.call_count, 0)
1407
1410
 
1408
- # Verify warning was logged for flag dependency
1409
- mock_log.warning.assert_called_with(
1410
- "Flag dependency filters are not supported in local evaluation. "
1411
- "Skipping condition for flag '%s' with dependency on flag '%s'",
1412
- "flag-with-dependencies",
1413
- "beta-feature",
1414
- )
1415
-
1416
- # Test with email that doesn't match
1411
+ # Test with email that doesn't match (should also fall back to remote due to missing dependency)
1417
1412
  feature_flag_match = client.get_feature_flag(
1418
1413
  "flag-with-dependencies",
1419
1414
  "test-user-2",
1420
1415
  person_properties={"email": "test@other.com"},
1421
1416
  )
1422
- self.assertEqual(feature_flag_match, False)
1423
- self.assertEqual(patch_flags.call_count, 0)
1417
+ self.assertIsNone(feature_flag_match)
1418
+ self.assertEqual(patch_flags.call_count, 2) # Called twice now
1424
1419
  self.assertEqual(patch_get.call_count, 0)
1425
1420
 
1426
- # Verify warning was logged again for the second evaluation
1427
- self.assertEqual(mock_log.warning.call_count, 2)
1421
+ @mock.patch("posthog.client.flags")
1422
+ @mock.patch("posthog.client.get")
1423
+ def test_flag_dependencies_simple_chain(self, patch_get, patch_flags):
1424
+ """Test basic flag dependency: flag-b depends on flag-a"""
1425
+ client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
1426
+ client.feature_flags = [
1427
+ {
1428
+ "id": 1,
1429
+ "name": "Flag A",
1430
+ "key": "flag-a",
1431
+ "active": True,
1432
+ "filters": {
1433
+ "groups": [
1434
+ {
1435
+ "properties": [
1436
+ {
1437
+ "key": "email",
1438
+ "operator": "icontains",
1439
+ "value": "@example.com",
1440
+ "type": "person",
1441
+ }
1442
+ ],
1443
+ "rollout_percentage": 100,
1444
+ }
1445
+ ],
1446
+ },
1447
+ },
1448
+ {
1449
+ "id": 2,
1450
+ "name": "Flag B",
1451
+ "key": "flag-b",
1452
+ "active": True,
1453
+ "filters": {
1454
+ "groups": [
1455
+ {
1456
+ "properties": [
1457
+ {
1458
+ "key": "flag-a",
1459
+ "operator": "exact",
1460
+ "value": True,
1461
+ "type": "flag",
1462
+ "dependency_chain": ["flag-a"],
1463
+ }
1464
+ ],
1465
+ "rollout_percentage": 100,
1466
+ }
1467
+ ],
1468
+ },
1469
+ },
1470
+ ]
1471
+
1472
+ # Test when dependency is satisfied
1473
+ result = client.get_feature_flag(
1474
+ "flag-b",
1475
+ "test-user",
1476
+ person_properties={"email": "test@example.com"},
1477
+ )
1478
+ self.assertEqual(result, True)
1479
+
1480
+ # Test when dependency is not satisfied
1481
+ result = client.get_feature_flag(
1482
+ "flag-b",
1483
+ "test-user-2",
1484
+ person_properties={"email": "test@other.com"},
1485
+ )
1486
+ self.assertEqual(result, False)
1487
+
1488
+ @mock.patch("posthog.client.flags")
1489
+ @mock.patch("posthog.client.get")
1490
+ def test_flag_dependencies_circular_dependency(self, patch_get, patch_flags):
1491
+ """Test circular dependency handling: flag-a depends on flag-b, flag-b depends on flag-a"""
1492
+ # Mock remote flags call to return empty for these flags (fallback returns None)
1493
+ patch_flags.return_value = {"featureFlags": {}}
1494
+ client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
1495
+ client.feature_flags = [
1496
+ {
1497
+ "id": 1,
1498
+ "name": "Flag A",
1499
+ "key": "flag-a",
1500
+ "active": True,
1501
+ "filters": {
1502
+ "groups": [
1503
+ {
1504
+ "properties": [
1505
+ {
1506
+ "key": "flag-b",
1507
+ "operator": "exact",
1508
+ "value": True,
1509
+ "type": "flag",
1510
+ "dependency_chain": [], # Empty chain indicates circular dependency
1511
+ }
1512
+ ],
1513
+ "rollout_percentage": 100,
1514
+ }
1515
+ ],
1516
+ },
1517
+ },
1518
+ {
1519
+ "id": 2,
1520
+ "name": "Flag B",
1521
+ "key": "flag-b",
1522
+ "active": True,
1523
+ "filters": {
1524
+ "groups": [
1525
+ {
1526
+ "properties": [
1527
+ {
1528
+ "key": "flag-a",
1529
+ "operator": "exact",
1530
+ "value": True,
1531
+ "type": "flag",
1532
+ "dependency_chain": [], # Empty chain indicates circular dependency
1533
+ }
1534
+ ],
1535
+ "rollout_percentage": 100,
1536
+ }
1537
+ ],
1538
+ },
1539
+ },
1540
+ ]
1541
+
1542
+ # Both flags should fall back to remote evaluation due to circular dependency
1543
+ # Since we're not mocking the remote call, both should return None
1544
+ result_a = client.get_feature_flag("flag-a", "test-user")
1545
+ self.assertIsNone(result_a)
1546
+
1547
+ result_b = client.get_feature_flag("flag-b", "test-user")
1548
+ self.assertIsNone(result_b)
1549
+
1550
+ @mock.patch("posthog.client.flags")
1551
+ @mock.patch("posthog.client.get")
1552
+ def test_flag_dependencies_missing_flag(self, patch_get, patch_flags):
1553
+ """Test handling of missing flag dependency"""
1554
+ # Mock remote flags call to return empty for this flag (fallback returns None)
1555
+ patch_flags.return_value = {"featureFlags": {}}
1556
+ client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
1557
+ client.feature_flags = [
1558
+ {
1559
+ "id": 1,
1560
+ "name": "Flag A",
1561
+ "key": "flag-a",
1562
+ "active": True,
1563
+ "filters": {
1564
+ "groups": [
1565
+ {
1566
+ "properties": [
1567
+ {
1568
+ "key": "non-existent-flag",
1569
+ "operator": "exact",
1570
+ "value": True,
1571
+ "type": "flag",
1572
+ "dependency_chain": ["non-existent-flag"],
1573
+ }
1574
+ ],
1575
+ "rollout_percentage": 100,
1576
+ }
1577
+ ],
1578
+ },
1579
+ }
1580
+ ]
1581
+
1582
+ # Should fall back to remote evaluation because dependency doesn't exist
1583
+ # Since we're not mocking the remote call, should return None
1584
+ result = client.get_feature_flag("flag-a", "test-user")
1585
+ self.assertIsNone(result)
1586
+
1587
+ @mock.patch("posthog.client.flags")
1588
+ @mock.patch("posthog.client.get")
1589
+ def test_flag_dependencies_complex_chain(self, patch_get, patch_flags):
1590
+ """Test complex dependency chain: flag-d -> flag-c -> [flag-a, flag-b]"""
1591
+ client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
1592
+ client.feature_flags = [
1593
+ {
1594
+ "id": 1,
1595
+ "name": "Flag A",
1596
+ "key": "flag-a",
1597
+ "active": True,
1598
+ "filters": {
1599
+ "groups": [
1600
+ {
1601
+ "properties": [],
1602
+ "rollout_percentage": 100,
1603
+ }
1604
+ ],
1605
+ },
1606
+ },
1607
+ {
1608
+ "id": 2,
1609
+ "name": "Flag B",
1610
+ "key": "flag-b",
1611
+ "active": True,
1612
+ "filters": {
1613
+ "groups": [
1614
+ {
1615
+ "properties": [],
1616
+ "rollout_percentage": 100,
1617
+ }
1618
+ ],
1619
+ },
1620
+ },
1621
+ {
1622
+ "id": 3,
1623
+ "name": "Flag C",
1624
+ "key": "flag-c",
1625
+ "active": True,
1626
+ "filters": {
1627
+ "groups": [
1628
+ {
1629
+ "properties": [
1630
+ {
1631
+ "key": "flag-a",
1632
+ "operator": "exact",
1633
+ "value": True,
1634
+ "type": "flag",
1635
+ "dependency_chain": ["flag-a"],
1636
+ },
1637
+ {
1638
+ "key": "flag-b",
1639
+ "operator": "exact",
1640
+ "value": True,
1641
+ "type": "flag",
1642
+ "dependency_chain": ["flag-b"],
1643
+ },
1644
+ ],
1645
+ "rollout_percentage": 100,
1646
+ }
1647
+ ],
1648
+ },
1649
+ },
1650
+ {
1651
+ "id": 4,
1652
+ "name": "Flag D",
1653
+ "key": "flag-d",
1654
+ "active": True,
1655
+ "filters": {
1656
+ "groups": [
1657
+ {
1658
+ "properties": [
1659
+ {
1660
+ "key": "flag-c",
1661
+ "operator": "exact",
1662
+ "value": True,
1663
+ "type": "flag",
1664
+ "dependency_chain": ["flag-a", "flag-b", "flag-c"],
1665
+ }
1666
+ ],
1667
+ "rollout_percentage": 100,
1668
+ }
1669
+ ],
1670
+ },
1671
+ },
1672
+ ]
1673
+
1674
+ # All dependencies satisfied - should return True
1675
+ result = client.get_feature_flag("flag-d", "test-user")
1676
+ self.assertEqual(result, True)
1677
+
1678
+ # Make flag-a inactive - should break the chain
1679
+ client.feature_flags[0]["active"] = False
1680
+ result = client.get_feature_flag("flag-d", "test-user")
1681
+ self.assertEqual(result, False)
1682
+
1683
+ @mock.patch("posthog.client.flags")
1684
+ @mock.patch("posthog.client.get")
1685
+ def test_flag_dependencies_mixed_conditions(self, patch_get, patch_flags):
1686
+ """Test flag dependency mixed with other property conditions"""
1687
+ client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
1688
+ client.feature_flags = [
1689
+ {
1690
+ "id": 1,
1691
+ "name": "Base Flag",
1692
+ "key": "base-flag",
1693
+ "active": True,
1694
+ "filters": {
1695
+ "groups": [
1696
+ {
1697
+ "properties": [],
1698
+ "rollout_percentage": 100,
1699
+ }
1700
+ ],
1701
+ },
1702
+ },
1703
+ {
1704
+ "id": 2,
1705
+ "name": "Mixed Flag",
1706
+ "key": "mixed-flag",
1707
+ "active": True,
1708
+ "filters": {
1709
+ "groups": [
1710
+ {
1711
+ "properties": [
1712
+ {
1713
+ "key": "base-flag",
1714
+ "operator": "exact",
1715
+ "value": True,
1716
+ "type": "flag",
1717
+ "dependency_chain": ["base-flag"],
1718
+ },
1719
+ {
1720
+ "key": "email",
1721
+ "operator": "icontains",
1722
+ "value": "@example.com",
1723
+ "type": "person",
1724
+ },
1725
+ ],
1726
+ "rollout_percentage": 100,
1727
+ }
1728
+ ],
1729
+ },
1730
+ },
1731
+ ]
1732
+
1733
+ # Both flag dependency and email condition satisfied
1734
+ result = client.get_feature_flag(
1735
+ "mixed-flag",
1736
+ "test-user",
1737
+ person_properties={"email": "test@example.com"},
1738
+ )
1739
+ self.assertEqual(result, True)
1740
+
1741
+ # Flag dependency satisfied but email condition not satisfied
1742
+ result = client.get_feature_flag(
1743
+ "mixed-flag",
1744
+ "test-user-2",
1745
+ person_properties={"email": "test@other.com"},
1746
+ )
1747
+ self.assertEqual(result, False)
1748
+
1749
+ # Email condition satisfied but flag dependency not satisfied
1750
+ client.feature_flags[0]["active"] = False
1751
+ result = client.get_feature_flag(
1752
+ "mixed-flag",
1753
+ "test-user-3",
1754
+ person_properties={"email": "test@example.com"},
1755
+ )
1756
+ self.assertEqual(result, False)
1757
+
1758
+ @mock.patch("posthog.client.flags")
1759
+ @mock.patch("posthog.client.get")
1760
+ def test_flag_dependencies_malformed_chain(self, patch_get, patch_flags):
1761
+ """Test handling of malformed dependency chains"""
1762
+ # Mock remote flags call to return empty for this flag (fallback returns None)
1763
+ patch_flags.return_value = {"featureFlags": {}}
1764
+ client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
1765
+ client.feature_flags = [
1766
+ {
1767
+ "id": 1,
1768
+ "name": "Base Flag",
1769
+ "key": "base-flag",
1770
+ "active": True,
1771
+ "filters": {
1772
+ "groups": [
1773
+ {
1774
+ "properties": [],
1775
+ "rollout_percentage": 100,
1776
+ }
1777
+ ],
1778
+ },
1779
+ },
1780
+ {
1781
+ "id": 2,
1782
+ "name": "Missing Chain Flag",
1783
+ "key": "missing-chain-flag",
1784
+ "active": True,
1785
+ "filters": {
1786
+ "groups": [
1787
+ {
1788
+ "properties": [
1789
+ {
1790
+ "key": "base-flag",
1791
+ "operator": "exact",
1792
+ "value": True,
1793
+ "type": "flag",
1794
+ # No dependency_chain property - should handle gracefully
1795
+ }
1796
+ ],
1797
+ "rollout_percentage": 100,
1798
+ }
1799
+ ],
1800
+ },
1801
+ },
1802
+ ]
1803
+
1804
+ # Should fall back to remote evaluation when dependency_chain is missing
1805
+ # Since we're not mocking the remote call, should return None
1806
+ result = client.get_feature_flag("missing-chain-flag", "test-user")
1807
+ self.assertIsNone(result)
1808
+
1809
+ def test_flag_dependencies_without_context_raises_inconclusive(self):
1810
+ """Test that missing flags_by_key raises InconclusiveMatchError"""
1811
+ from posthoganalytics.feature_flags import (
1812
+ evaluate_flag_dependency,
1813
+ InconclusiveMatchError,
1814
+ )
1815
+
1816
+ property_with_flag_dep = {
1817
+ "key": "some-flag",
1818
+ "operator": "exact",
1819
+ "value": True,
1820
+ "type": "flag",
1821
+ "dependency_chain": ["some-flag"],
1822
+ }
1823
+
1824
+ # Should raise InconclusiveMatchError when flags_by_key is None
1825
+ with self.assertRaises(InconclusiveMatchError) as cm:
1826
+ evaluate_flag_dependency(
1827
+ property_with_flag_dep,
1828
+ flags_by_key=None, # This should trigger the error
1829
+ evaluation_cache={},
1830
+ distinct_id="test-user",
1831
+ properties={},
1832
+ cohort_properties={},
1833
+ )
1834
+
1835
+ self.assertIn("Cannot evaluate flag dependency", str(cm.exception))
1836
+ self.assertIn("some-flag", str(cm.exception))
1428
1837
 
1429
1838
  @mock.patch("posthog.client.Poller")
1430
1839
  @mock.patch("posthog.client.get")
@@ -5387,4 +5796,115 @@ class TestConsistency(unittest.TestCase):
5387
5796
  test_cases = ["beta-feature", "BETA-FEATURE", "bEtA-FeAtUrE"]
5388
5797
  for case in test_cases:
5389
5798
  self.assertFalse(client.feature_enabled(case, "user1"))
5390
- self.assertIsNone(client.get_feature_flag_payload(case, "user1"))
5799
+
5800
+ @mock.patch("posthog.client.flags")
5801
+ def test_get_all_flags_with_flag_keys_to_evaluate(self, mock_flags):
5802
+ """Test that get_all_flags with flag_keys_to_evaluate only evaluates specified flags"""
5803
+ mock_flags.return_value = {
5804
+ "featureFlags": {
5805
+ "flag1": "value1",
5806
+ "flag2": True,
5807
+ }
5808
+ }
5809
+
5810
+ client = Client(
5811
+ project_api_key=FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY
5812
+ )
5813
+
5814
+ # Call get_all_flags with flag_keys_to_evaluate
5815
+ result = client.get_all_flags(
5816
+ "user123",
5817
+ flag_keys_to_evaluate=["flag1", "flag2"],
5818
+ person_properties={"region": "USA"},
5819
+ )
5820
+
5821
+ # Verify flags() was called with flag_keys_to_evaluate
5822
+ mock_flags.assert_called_once()
5823
+ call_args = mock_flags.call_args[1]
5824
+ self.assertEqual(call_args["flag_keys_to_evaluate"], ["flag1", "flag2"])
5825
+ self.assertEqual(
5826
+ call_args["person_properties"], {"distinct_id": "user123", "region": "USA"}
5827
+ )
5828
+
5829
+ # Check the result
5830
+ self.assertEqual(result, {"flag1": "value1", "flag2": True})
5831
+
5832
+ @mock.patch("posthog.client.flags")
5833
+ def test_get_all_flags_and_payloads_with_flag_keys_to_evaluate(self, mock_flags):
5834
+ """Test that get_all_flags_and_payloads with flag_keys_to_evaluate only evaluates specified flags"""
5835
+ mock_flags.return_value = {
5836
+ "featureFlags": {
5837
+ "flag1": "variant1",
5838
+ "flag3": True,
5839
+ },
5840
+ "featureFlagPayloads": {
5841
+ "flag1": {"data": "payload1"},
5842
+ "flag3": {"data": "payload3"},
5843
+ },
5844
+ }
5845
+
5846
+ client = Client(
5847
+ project_api_key=FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY
5848
+ )
5849
+
5850
+ # Call get_all_flags_and_payloads with flag_keys_to_evaluate
5851
+ result = client.get_all_flags_and_payloads(
5852
+ "user123",
5853
+ flag_keys_to_evaluate=["flag1", "flag3"],
5854
+ person_properties={"subscription": "pro"},
5855
+ )
5856
+
5857
+ # Verify flags() was called with flag_keys_to_evaluate
5858
+ mock_flags.assert_called_once()
5859
+ call_args = mock_flags.call_args[1]
5860
+ self.assertEqual(call_args["flag_keys_to_evaluate"], ["flag1", "flag3"])
5861
+ self.assertEqual(
5862
+ call_args["person_properties"],
5863
+ {"distinct_id": "user123", "subscription": "pro"},
5864
+ )
5865
+
5866
+ # Check the result
5867
+ self.assertEqual(result["featureFlags"], {"flag1": "variant1", "flag3": True})
5868
+ self.assertEqual(
5869
+ result["featureFlagPayloads"],
5870
+ {"flag1": {"data": "payload1"}, "flag3": {"data": "payload3"}},
5871
+ )
5872
+
5873
+ def test_get_all_flags_locally_with_flag_keys_to_evaluate(self):
5874
+ """Test that local evaluation with flag_keys_to_evaluate only evaluates specified flags"""
5875
+ client = Client(
5876
+ project_api_key=FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY
5877
+ )
5878
+
5879
+ # Set up multiple flags
5880
+ client.feature_flags = [
5881
+ {
5882
+ "id": 1,
5883
+ "key": "flag1",
5884
+ "active": True,
5885
+ "filters": {"groups": [{"properties": [], "rollout_percentage": 100}]},
5886
+ },
5887
+ {
5888
+ "id": 2,
5889
+ "key": "flag2",
5890
+ "active": True,
5891
+ "filters": {"groups": [{"properties": [], "rollout_percentage": 100}]},
5892
+ },
5893
+ {
5894
+ "id": 3,
5895
+ "key": "flag3",
5896
+ "active": True,
5897
+ "filters": {"groups": [{"properties": [], "rollout_percentage": 100}]},
5898
+ },
5899
+ ]
5900
+
5901
+ # Call get_all_flags with flag_keys_to_evaluate
5902
+ result = client.get_all_flags(
5903
+ "user123",
5904
+ flag_keys_to_evaluate=["flag1", "flag3"],
5905
+ only_evaluate_locally=True,
5906
+ )
5907
+
5908
+ # Should only return flag1 and flag3
5909
+ self.assertEqual(result, {"flag1": True, "flag3": True})
5910
+ self.assertNotIn("flag2", result)
posthoganalytics/types.py CHANGED
@@ -9,6 +9,7 @@ FlagValue = Union[bool, str]
9
9
  BeforeSendCallback = Callable[[dict[str, Any]], Optional[dict[str, Any]]]
10
10
 
11
11
 
12
+ # Type alias for the send_feature_flags parameter
12
13
  class SendFeatureFlagsOptions(TypedDict, total=False):
13
14
  """Options for sending feature flags with capture events.
14
15
 
@@ -22,9 +23,11 @@ class SendFeatureFlagsOptions(TypedDict, total=False):
22
23
  Format: { group_type_name: { group_properties } }
23
24
  """
24
25
 
26
+ should_send: bool
25
27
  only_evaluate_locally: Optional[bool]
26
28
  person_properties: Optional[dict[str, Any]]
27
29
  group_properties: Optional[dict[str, dict[str, Any]]]
30
+ flag_keys_filter: Optional[list[str]]
28
31
 
29
32
 
30
33
  @dataclass(frozen=True)
@@ -1,4 +1,4 @@
1
- VERSION = "6.5.0"
1
+ VERSION = "6.6.1"
2
2
 
3
3
  if __name__ == "__main__":
4
4
  print(VERSION, end="") # noqa: T201
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: posthoganalytics
3
- Version: 6.5.0
3
+ Version: 6.6.1
4
4
  Summary: Integrate PostHog into any python application.
5
5
  Home-page: https://github.com/posthog/posthog-python
6
6
  Author: Posthog