Skip to content

Commit af2f83e

Browse files
committed
Merge branch 'master' into fix/feature-flag-payload-events
2 parents c30d17b + 8ae3f2b commit af2f83e

File tree

8 files changed

+78
-14
lines changed

8 files changed

+78
-14
lines changed

CHANGELOG.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
## 3.7.2 - 2024-11-19
2+
3+
1. Add `type` property to exception stacks.
4+
5+
## 3.7.1 - 2024-10-24
6+
7+
1. Add `platform` property to each frame of exception stacks.
8+
9+
## 3.7.0 - 2024-10-03
10+
11+
1. Adds a new `super_properties` parameter on the client that are appended to every /capture call.
12+
13+
## 3.6.7 - 2024-09-24
14+
15+
1. Remove deprecated datetime.utcnow() in favour of datetime.now(tz=tzutc())
16+
17+
## 3.6.6 - 2024-09-16
18+
19+
1. Fix manual capture support for in app frames
20+
121
## 3.6.5 - 2024-09-10
222

323
1. Fix django integration support for manual exception capture.
@@ -58,7 +78,6 @@
5878

5979
1. Return success/failure with all capture calls from module functions
6080

61-
6281
## 3.3.1 - 2024-01-10
6382

6483
1. Make sure we don't override any existing feature flag properties when adding locally evaluated feature flag properties.
@@ -84,6 +103,7 @@
84103

85104
1. Restore how feature flags work when the client library is disabled: All requests return `None` and no events are sent when the client is disabled.
86105
2. Add a `feature_flag_definitions()` debug option, which returns currently loaded feature flag definitions. You can use this to more cleverly decide when to request local evaluation of feature flags.
106+
87107
## 3.0.0 - 2023-04-14
88108

89109
Breaking change:
@@ -124,7 +144,6 @@ posthog = Posthog('api_key', disable_geoip=False)
124144
1. Log instead of raise error on posthog personal api key errors
125145
2. Remove upper bound on backoff dependency
126146

127-
128147
## 2.3.0 - 2023-01-31
129148

130149
1. Add support for returning payloads of matched feature flags
@@ -140,6 +159,7 @@ Changes:
140159
Changes:
141160

142161
1. Fixes issues with date comparison.
162+
143163
## 2.1.1 - 2022-09-14
144164

145165
Changes:
@@ -152,6 +172,7 @@ Changes:
152172

153173
1. Feature flag defaults have been removed
154174
2. Setup logging only when debug mode is enabled.
175+
155176
## 2.0.1 - 2022-08-04
156177

157178
- Make poll_interval configurable
@@ -164,26 +185,28 @@ Breaking changes:
164185

165186
1. The minimum version requirement for PostHog servers is now 1.38. If you're using PostHog Cloud, you satisfy this requirement automatically.
166187
2. Feature flag defaults apply only when there's an error fetching feature flag results. Earlier, if the default was set to `True`, even if a flag resolved to `False`, the default would override this.
167-
**Note: These are removed in 2.0.2**
188+
**Note: These are removed in 2.0.2**
168189
3. Feature flag remote evaluation doesn't require a personal API key.
169190

170191
New Changes:
171192

172193
1. You can now evaluate feature flags locally (i.e. without sending a request to your PostHog servers) by setting a personal API key, and passing in groups and person properties to `is_feature_enabled` and `get_feature_flag` calls.
173194
2. Introduces a `get_all_flags` method that returns all feature flags. This is useful for when you want to seed your frontend with some initial flags, given a user ID.
174195

175-
176-
177196
## 1.4.9 - 2022-06-13
197+
178198
- Support for sending feature flags with capture calls
179199

180200
## 1.4.8 - 2022-05-12
201+
181202
- Support multi variate feature flags
182203

183204
## 1.4.7 - 2022-04-25
205+
184206
- Allow feature flags usage without project_api_key
185207

186208
## 1.4.1 - 2021-05-28
209+
187210
- Fix packaging issues with Sentry integrations
188211

189212
## 1.4.0 - 2021-05-18

posthog/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
2020
poll_interval = 30 # type: int
2121
disable_geoip = True # type: bool
2222
feature_flags_request_timeout_seconds = 3 # type: int
23+
super_properties = None # type: Optional[Dict]
2324
# Currently alpha, use at your own risk
2425
enable_exception_autocapture = False # type: bool
2526
exception_autocapture_integrations = [] # type: List[Integrations]
27+
# Used to determine in app paths for exception autocapture. Defaults to the current working directory
28+
project_root = None # type: Optional[str]
2629

2730
default_client = None # type: Optional[Client]
2831

@@ -502,6 +505,7 @@ def _proxy(method, *args, **kwargs):
502505
disabled=disabled,
503506
disable_geoip=disable_geoip,
504507
feature_flags_request_timeout_seconds=feature_flags_request_timeout_seconds,
508+
super_properties=super_properties,
505509
# TODO: Currently this monitoring begins only when the Client is initialised (which happens when you do something with the SDK)
506510
# This kind of initialisation is very annoying for exception capture. We need to figure out a way around this,
507511
# or deprecate this proxy option fully (it's already in the process of deprecation, no new clients should be using this method since like 5-6 months)

posthog/client.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import atexit
22
import logging
33
import numbers
4+
import os
45
import sys
56
from datetime import datetime, timedelta
67
from uuid import UUID
@@ -54,8 +55,10 @@ def __init__(
5455
disable_geoip=True,
5556
historical_migration=False,
5657
feature_flags_request_timeout_seconds=3,
58+
super_properties=None,
5759
enable_exception_autocapture=False,
5860
exception_autocapture_integrations=None,
61+
project_root=None,
5962
):
6063
self.queue = queue.Queue(max_queue_size)
6164

@@ -84,10 +87,19 @@ def __init__(
8487
self.disabled = disabled
8588
self.disable_geoip = disable_geoip
8689
self.historical_migration = historical_migration
90+
self.super_properties = super_properties
8791
self.enable_exception_autocapture = enable_exception_autocapture
8892
self.exception_autocapture_integrations = exception_autocapture_integrations
8993
self.exception_capture = None
9094

95+
if project_root is None:
96+
try:
97+
project_root = os.getcwd()
98+
except Exception:
99+
project_root = None
100+
101+
self.project_root = project_root
102+
91103
# personal_api_key: This should be a generated Personal API Key, private
92104
self.personal_api_key = personal_api_key
93105
if debug:
@@ -391,7 +403,8 @@ def capture_exception(
391403
"exception": {
392404
"values": all_exceptions_with_trace,
393405
},
394-
}
406+
},
407+
project_root=self.project_root,
395408
)
396409
all_exceptions_with_trace_and_in_app = event["exception"]["values"]
397410

@@ -415,7 +428,7 @@ def _enqueue(self, msg, disable_geoip):
415428

416429
timestamp = msg["timestamp"]
417430
if timestamp is None:
418-
timestamp = datetime.utcnow().replace(tzinfo=tzutc())
431+
timestamp = datetime.now(tz=tzutc())
419432

420433
require("timestamp", timestamp, datetime)
421434
require("context", msg["context"], dict)
@@ -441,6 +454,9 @@ def _enqueue(self, msg, disable_geoip):
441454
if disable_geoip:
442455
msg["properties"]["$geoip_disable"] = True
443456

457+
if self.super_properties:
458+
msg["properties"] = {**msg["properties"], **self.super_properties}
459+
444460
msg["distinct_id"] = stringify_id(msg.get("distinct_id", None))
445461

446462
msg = clean(msg)
@@ -539,7 +555,7 @@ def _load_feature_flags(self):
539555
)
540556
self.log.warning(e)
541557

542-
self._last_feature_flag_poll = datetime.utcnow().replace(tzinfo=tzutc())
558+
self._last_feature_flag_poll = datetime.now(tz=tzutc())
543559

544560
def load_feature_flags(self):
545561
if not self.personal_api_key:

posthog/exception_utils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ def serialize_frame(
369369
tb_lineno = frame.f_lineno
370370

371371
rv = {
372+
"platform": "python",
372373
"filename": filename_for_module(module, abs_path) or None,
373374
"abs_path": os.path.abspath(abs_path) if abs_path else None,
374375
"function": function or "<unknown>",
@@ -417,7 +418,7 @@ def current_stacktrace(
417418

418419
frames.reverse()
419420

420-
return {"frames": frames}
421+
return {"frames": frames, "type": "raw"}
421422

422423

423424
def get_errno(exc_value):
@@ -503,7 +504,7 @@ def single_exception_from_error_tuple(
503504
]
504505

505506
if frames:
506-
exception_value["stacktrace"] = {"frames": frames}
507+
exception_value["stacktrace"] = {"frames": frames, "type": "raw"}
507508

508509
return exception_value
509510

posthog/request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def post(
3737
"""Post the `kwargs` to the API"""
3838
log = logging.getLogger("posthog")
3939
body = kwargs
40-
body["sentAt"] = datetime.utcnow().replace(tzinfo=tzutc()).isoformat()
40+
body["sentAt"] = datetime.now(tz=tzutc()).isoformat()
4141
url = remove_trailing_slash(host or DEFAULT_HOST) + path
4242
body["api_key"] = api_key
4343
data = json.dumps(body, cls=DatetimeSerializer)

posthog/test/test_client.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,21 @@ def test_basic_capture_with_project_api_key(self):
8484
self.assertEqual(msg["properties"]["$lib"], "posthog-python")
8585
self.assertEqual(msg["properties"]["$lib_version"], VERSION)
8686

87+
def test_basic_super_properties(self):
88+
client = Client(FAKE_TEST_API_KEY, super_properties={"source": "repo-name"})
89+
90+
_, msg = client.capture("distinct_id", "python test event")
91+
client.flush()
92+
93+
self.assertEqual(msg["event"], "python test event")
94+
self.assertEqual(msg["properties"]["source"], "repo-name")
95+
96+
_, msg = client.identify("distinct_id", {"trait": "value"})
97+
client.flush()
98+
99+
self.assertEqual(msg["$set"]["trait"], "value")
100+
self.assertEqual(msg["properties"]["source"], "repo-name")
101+
87102
def test_basic_capture_exception(self):
88103

89104
with mock.patch.object(Client, "capture", return_value=None) as patch_capture:
@@ -216,6 +231,10 @@ def test_basic_capture_exception_with_no_exception_given(self):
216231
self.assertEqual(capture_call[2]["$exception_list"][0]["module"], None)
217232
self.assertEqual(capture_call[2]["$exception_list"][0]["type"], "Exception")
218233
self.assertEqual(capture_call[2]["$exception_list"][0]["value"], "test exception")
234+
self.assertEqual(
235+
capture_call[2]["$exception_list"][0]["stacktrace"]["type"],
236+
"raw",
237+
)
219238
self.assertEqual(
220239
capture_call[2]["$exception_list"][0]["stacktrace"]["frames"][0]["filename"],
221240
"posthog/test/test_client.py",
@@ -227,6 +246,7 @@ def test_basic_capture_exception_with_no_exception_given(self):
227246
self.assertEqual(
228247
capture_call[2]["$exception_list"][0]["stacktrace"]["frames"][0]["module"], "posthog.test.test_client"
229248
)
249+
self.assertEqual(capture_call[2]["$exception_list"][0]["stacktrace"]["frames"][0]["in_app"], True)
230250

231251
def test_basic_capture_exception_with_no_exception_happening(self):
232252

posthog/test/test_exception_capture.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_excepthook(tmpdir):
2929
assert b"LOL" in output
3030
assert b"DEBUG:posthog:data uploaded successfully" in output
3131
assert (
32-
b'"$exception_list": [{"mechanism": {"type": "generic", "handled": true}, "module": null, "type": "ZeroDivisionError", "value": "division by zero", "stacktrace": {"frames": [{"filename": "app.py", "abs_path"'
32+
b'"$exception_list": [{"mechanism": {"type": "generic", "handled": true}, "module": null, "type": "ZeroDivisionError", "value": "division by zero", "stacktrace": {"frames": [{"platform": "python", "filename": "app.py", "abs_path"'
3333
in output
3434
)
3535

@@ -58,6 +58,6 @@ def test_trying_to_use_django_integration(tmpdir):
5858
assert b"LOL" in output
5959
assert b"DEBUG:posthog:data uploaded successfully" in output
6060
assert (
61-
b'"$exception_list": [{"mechanism": {"type": "generic", "handled": true}, "module": null, "type": "ZeroDivisionError", "value": "division by zero", "stacktrace": {"frames": [{"filename": "app.py", "abs_path"'
61+
b'"$exception_list": [{"mechanism": {"type": "generic", "handled": true}, "module": null, "type": "ZeroDivisionError", "value": "division by zero", "stacktrace": {"frames": [{"platform": "python", "filename": "app.py", "abs_path"'
6262
in output
6363
)

posthog/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
VERSION = "3.6.5"
1+
VERSION = "3.7.2"
22

33
if __name__ == "__main__":
44
print(VERSION, end="") # noqa: T201

0 commit comments

Comments
 (0)