Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .sampo/changesets/non-finite-floats-null.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
pypi/posthog: patch
---

Non-finite float property values (`inf`, `-inf`, `nan`, including `Decimal("Infinity")`) are now sent as `null` instead of the invalid JSON tokens `Infinity`/`NaN`, so strict-parsing ingestion no longer rejects the whole batch. This matches posthog-js.
17 changes: 17 additions & 0 deletions posthog/test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,23 @@ def test_clean_fn(self):
cleaned = utils.clean({"fn": lambda x: x, "number": 4})
assert cleaned == {"fn": None, "number": 4}

@parameterized.expand(
[
("inf", float("inf"), None),
("negative_inf", float("-inf"), None),
("nan", float("nan"), None),
("decimal_inf", Decimal("Infinity"), None),
("finite_float", 1.5, 1.5),
("finite_decimal", Decimal("1.25"), 1.25),
]
)
def test_clean_non_finite_floats(self, _name, value, expected):
assert utils.clean(value) == expected

def test_clean_non_finite_floats_nested(self):
cleaned = utils.clean({"a": [float("nan"), {"b": float("inf")}], "ok": 2.0})
assert cleaned == {"a": [None, {"b": None}], "ok": 2.0}

@parameterized.expand(
[
("http://posthog.io/", "http://posthog.io"),
Expand Down
5 changes: 4 additions & 1 deletion posthog/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
import math
import numbers
import re
import threading
Expand Down Expand Up @@ -84,9 +85,11 @@ def remove_trailing_slash(host: str) -> str:

def clean(item):
if isinstance(item, Decimal):
return float(item)
return clean(float(item))
if isinstance(item, UUID):
return str(item)
if isinstance(item, float) and not math.isfinite(item):
return None
if isinstance(item, (str, bool, numbers.Number, datetime, date, type(None))):
return item
if isinstance(item, (set, list, tuple)):
Expand Down