Compare commits

...
6 Commits
Author SHA1 Message Date
Eric DuongandGitHub 4aa3499527 version 2.5.0 (#97) 2023-04-10 15:13:25 -04:00
Eric DuongandGitHub de7def97e2 chore: change package to be an instantiable client (#96)
* initial try

* change init file to a class

* remove commas

* format

* sort

* restore original and add renamed client

* format

* move disabled

* format

* handle changing var for global instance

* add test
2023-04-10 14:33:07 -04:00
Neil KakkarandGitHub dfefd0a1b6 Fix analytics package dependencies (#95) 2023-03-30 10:34:59 -04:00
Neil KakkarandGitHub 477a688016 Add CI to ensure no prints (#94) 2023-03-17 15:22:59 +00:00
Neil KakkarandGitHub fa474a0fe6 Release for print fix (#93) 2023-03-17 12:41:16 +00:00
Jann KleenandGitHub f8bc3f17eb Remove print() call (#92) 2023-03-17 12:37:01 +00:00
12 changed files with 104 additions and 48 deletions
+4
View File
@@ -33,6 +33,10 @@ jobs:
- name: Check formatting with black
run: |
black --check .
- name: Lint with flake8
run: |
flake8 posthog --ignore E501
- name: Check import order with isort
run: |
+12
View File
@@ -1,3 +1,15 @@
## 2.5.0 - 2023-04-10
1. Add option for instantiating separate client object
## 2.4.2 - 2023-03-30
1. Update backoff dependency for posthoganalytics package to be the same as posthog package
## 2.4.1 - 2023-03-17
1. Removes accidental print call left in for decide response
## 2.4.0 - 2023-03-14
1. Support evaluating all cohorts in feature flags for local evaluation
+20 -12
View File
@@ -1,4 +1,5 @@
from typing import Callable, Dict, Optional
import datetime # noqa: F401
from typing import Callable, Dict, Optional # noqa: F401
from posthog.client import Client
from posthog.version import VERSION
@@ -98,7 +99,7 @@ def identify(
def set(
distinct_id, # type: str,
distinct_id, # type: str
properties=None, # type: Optional[Dict]
context=None, # type: Optional[Dict]
timestamp=None, # type: Optional[datetime.datetime]
@@ -131,7 +132,7 @@ def set(
def set_once(
distinct_id, # type: str,
distinct_id, # type: str
properties=None, # type: Optional[Dict]
context=None, # type: Optional[Dict]
timestamp=None, # type: Optional[datetime.datetime]
@@ -199,8 +200,8 @@ def group_identify(
def alias(
previous_id, # type: str,
distinct_id, # type: str,
previous_id, # type: str
distinct_id, # type: str
context=None, # type: Optional[Dict]
timestamp=None, # type: Optional[datetime.datetime]
uuid=None, # type: Optional[str]
@@ -233,8 +234,8 @@ def alias(
def feature_enabled(
key, # type: str,
distinct_id, # type: str,
key, # type: str
distinct_id, # type: str
groups={}, # type: dict
person_properties={}, # type: dict
group_properties={}, # type: dict
@@ -268,8 +269,8 @@ def feature_enabled(
def get_feature_flag(
key, # type: str,
distinct_id, # type: str,
key, # type: str
distinct_id, # type: str
groups={}, # type: dict
person_properties={}, # type: dict
group_properties={}, # type: dict
@@ -311,7 +312,7 @@ def get_feature_flag(
def get_all_flags(
distinct_id, # type: str,
distinct_id, # type: str
groups={}, # type: dict
person_properties={}, # type: dict
group_properties={}, # type: dict
@@ -405,8 +406,6 @@ def shutdown():
def _proxy(method, *args, **kwargs):
"""Create an analytics client if one doesn't exist and send to it."""
global default_client
if disabled:
return None
if not default_client:
default_client = Client(
api_key,
@@ -418,7 +417,16 @@ def _proxy(method, *args, **kwargs):
personal_api_key=personal_api_key,
project_api_key=project_api_key,
poll_interval=poll_interval,
disabled=disabled,
)
# always set incase user changes it
default_client.disabled = disabled
default_client.debug = debug
fn = getattr(default_client, method)
return fn(*args, **kwargs)
class Posthog(Client):
pass
+11 -6
View File
@@ -47,6 +47,7 @@ class Client(object):
poll_interval=30,
personal_api_key=None,
project_api_key=None,
disabled=False,
):
self.queue = queue.Queue(max_queue_size)
@@ -69,6 +70,7 @@ class Client(object):
self.poll_interval = poll_interval
self.poller = None
self.distinct_ids_feature_flags_reported = SizeLimitedDict(MAX_DICT_SIZE, set)
self.disabled = disabled
# personal_api_key: This should be a generated Personal API Key, private
self.personal_api_key = personal_api_key
@@ -157,7 +159,6 @@ class Client(object):
}
resp_data = decide(self.api_key, self.host, timeout=10, **request_data)
print(resp_data)
return resp_data
def capture(
@@ -300,6 +301,10 @@ class Client(object):
def _enqueue(self, msg):
"""Push a new `msg` onto the queue, return `(success, msg)`"""
if self.disabled:
return False, "disabled"
timestamp = msg["timestamp"]
if timestamp is None:
timestamp = datetime.utcnow().replace(tzinfo=tzutc())
@@ -392,7 +397,7 @@ class Client(object):
except APIError as e:
if e.status == 401:
self.log.error(
f"[FEATURE FLAGS] Error loading feature flags: To use feature flags, please set a valid personal_api_key. More information: https://posthog.com/docs/api/overview"
"[FEATURE FLAGS] Error loading feature flags: To use feature flags, please set a valid personal_api_key. More information: https://posthog.com/docs/api/overview"
)
if self.debug:
raise APIError(
@@ -495,7 +500,7 @@ class Client(object):
require("distinct_id", distinct_id, ID_TYPES)
require("groups", groups, dict)
if self.feature_flags == None and self.personal_api_key:
if self.feature_flags is None and self.personal_api_key:
self.load_feature_flags()
response = None
@@ -535,7 +540,7 @@ class Client(object):
feature_flag_reported_key = f"{key}_{str(response)}"
if (
feature_flag_reported_key not in self.distinct_ids_feature_flags_reported[distinct_id]
and send_feature_flag_events
and send_feature_flag_events # noqa: W503
):
self.capture(
distinct_id,
@@ -631,7 +636,7 @@ class Client(object):
require("distinct_id", distinct_id, ID_TYPES)
require("groups", groups, dict)
if self.feature_flags == None and self.personal_api_key:
if self.feature_flags is None and self.personal_api_key:
self.load_feature_flags()
flags = {}
@@ -651,7 +656,7 @@ class Client(object):
matched_payload = self._compute_payload_locally(flag["key"], flags[flag["key"]])
if matched_payload:
payloads[flag["key"]] = matched_payload
except InconclusiveMatchError as e:
except InconclusiveMatchError:
# No need to log this, since it's just telling us to fall back to `/decide`
fallback_to_decide = True
except Exception as e:
+1 -1
View File
@@ -11,7 +11,7 @@ def get_distinct_id(request):
return None
try:
return GET_DISTINCT_ID(request)
except:
except: # noqa: E722
return None
+2 -3
View File
@@ -1,5 +1,4 @@
from sentry_sdk._types import MYPY
from sentry_sdk.client import Client
from sentry_sdk.hub import Hub
from sentry_sdk.integrations import Integration
from sentry_sdk.scope import add_global_event_processor
@@ -10,9 +9,9 @@ from posthog.request import DEFAULT_HOST
from posthog.sentry import POSTHOG_ID_TAG
if MYPY:
from typing import Any, Dict, Optional
from typing import Optional # noqa: F401
from sentry_sdk._types import Event, Hint
from sentry_sdk._types import Event, Hint # noqa: F401
class PostHogIntegration(Integration):
+28 -3
View File
@@ -1,7 +1,6 @@
import time
import unittest
from datetime import date, datetime
from unittest.mock import MagicMock
from datetime import datetime
from uuid import uuid4
import mock
@@ -28,7 +27,7 @@ class TestClient(unittest.TestCase):
def set_fail(self, e, batch):
"""Mark the failure handler"""
print("FAIL", e, batch)
print("FAIL", e, batch) # noqa: T201
self.failed = True
def setUp(self):
@@ -469,6 +468,32 @@ class TestClient(unittest.TestCase):
for consumer in client.consumers:
self.assertEqual(consumer.timeout, 15)
def test_disabled(self):
client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail, disabled=True)
success, msg = client.capture("distinct_id", "python test event")
client.flush()
self.assertFalse(success)
self.assertFalse(self.failed)
self.assertEqual(msg, "disabled")
def test_enabled_to_disabled(self):
client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail, disabled=False)
success, msg = client.capture("distinct_id", "python test event")
client.flush()
self.assertTrue(success)
self.assertFalse(self.failed)
self.assertEqual(msg["event"], "python test event")
client.disabled = True
success, msg = client.capture("distinct_id", "python test event")
client.flush()
self.assertFalse(success)
self.assertFalse(self.failed)
self.assertEqual(msg, "disabled")
@mock.patch("posthog.client.Poller")
@mock.patch("posthog.client.get")
def test_call_identify_fails(self, patch_get, patch_poll):
+4 -4
View File
@@ -24,7 +24,7 @@ class TestLocalEvaluation(unittest.TestCase):
def set_fail(self, e, batch):
"""Mark the failure handler"""
print("FAIL", e, batch)
print("FAIL", e, batch) # noqa: T201
self.failed = True
def setUp(self):
@@ -1739,7 +1739,7 @@ class TestMatchProperties(unittest.TestCase):
self.assertFalse(match_property(property_b, {"key": "three"}))
def test_match_properties_regex(self):
property_a = self.property(key="key", value="\.com$", operator="regex")
property_a = self.property(key="key", value="\.com$", operator="regex") # noqa: W605
self.assertTrue(match_property(property_a, {"key": "value.com"}))
self.assertTrue(match_property(property_a, {"key": "value2.com"}))
@@ -1747,7 +1747,7 @@ class TestMatchProperties(unittest.TestCase):
self.assertFalse(match_property(property_a, {"key": "Alakazam"}))
self.assertFalse(match_property(property_a, {"key": 123}))
self.assertFalse(match_property(property_a, {"key": "valuecom"}))
self.assertFalse(match_property(property_a, {"key": "value\com"}))
self.assertFalse(match_property(property_a, {"key": "value\com"})) # noqa: W605
property_b = self.property(key="key", value="3", operator="regex")
self.assertTrue(match_property(property_b, {"key": "3"}))
@@ -1997,7 +1997,7 @@ class TestConsistency(unittest.TestCase):
def set_fail(self, e, batch):
"""Mark the failure handler"""
print("FAIL", e, batch)
print("FAIL", e, batch) # noqa: T201
self.failed = True
def setUp(self):
+17 -16
View File
@@ -1,40 +1,41 @@
import unittest
import posthog
from posthog import Posthog
class TestModule(unittest.TestCase):
posthog = None
def failed(self):
self.failed = True
def setUp(self):
self.failed = False
posthog.api_key = "testsecret"
posthog.on_error = self.failed
self.posthog = Posthog("testsecret", host="http://localhost:8000", on_error=self.failed)
def test_no_api_key(self):
posthog.api_key = None
self.assertRaises(Exception, posthog.capture)
self.posthog.api_key = None
self.assertRaises(Exception, self.posthog.capture)
def test_no_host(self):
posthog.host = None
self.assertRaises(Exception, posthog.capture)
self.posthog.host = None
self.assertRaises(Exception, self.posthog.capture)
def test_track(self):
posthog.capture("distinct_id", "python module event")
posthog.flush()
self.posthog.capture("distinct_id", "python module event")
self.posthog.flush()
def test_identify(self):
posthog.identify("distinct_id", {"email": "user@email.com"})
posthog.flush()
self.posthog.identify("distinct_id", {"email": "user@email.com"})
self.posthog.flush()
def test_alias(self):
posthog.alias("previousId", "distinct_id")
posthog.flush()
self.posthog.alias("previousId", "distinct_id")
self.posthog.flush()
def test_page(self):
posthog.page("distinct_id", "https://posthog.com/contact")
posthog.flush()
self.posthog.page("distinct_id", "https://posthog.com/contact")
self.posthog.flush()
def test_flush(self):
posthog.flush()
self.posthog.flush()
+2 -2
View File
@@ -1,4 +1,4 @@
VERSION = "2.4.0"
VERSION = "2.5.0"
if __name__ == "__main__":
print(VERSION, end="")
print(VERSION, end="") # noqa: T201
+2
View File
@@ -20,6 +20,8 @@ extras_require = {
"dev": [
"black",
"isort",
"flake8",
"flake8-print",
"pre-commit",
],
"test": ["mock>=2.0.0", "freezegun==0.3.15", "pylint", "flake8", "coverage", "pytest"],
+1 -1
View File
@@ -14,7 +14,7 @@ long_description = """
PostHog is developer-friendly, self-hosted product analytics. posthog-python is the python package.
"""
install_requires = ["requests>=2.7,<3.0", "six>=1.5", "monotonic>=1.5", "backoff==1.6.0", "python-dateutil>2.1"]
install_requires = ["requests>=2.7,<3.0", "six>=1.5", "monotonic>=1.5", "backoff>=1.10.0", "python-dateutil>2.1"]
tests_require = ["mock>=2.0.0"]