From e7e3668c18856c131e4fb33bfb92eee0176a86af Mon Sep 17 00:00:00 2001 From: Kshitij Mishra Date: Tue, 2 Jun 2026 15:19:04 -0400 Subject: [PATCH 1/3] fix(consumer): remove legacy Python 2 imports and type-guard retry status math --- posthog/consumer.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/posthog/consumer.py b/posthog/consumer.py index 6a9becbe..956e53a2 100644 --- a/posthog/consumer.py +++ b/posthog/consumer.py @@ -6,10 +6,7 @@ from posthog.request import APIError, DatetimeSerializer, batch_post -try: - from queue import Empty -except ImportError: - from Queue import Empty +from queue import Empty MAX_MSG_SIZE = 900 * 1024 # 900KiB per event @@ -133,9 +130,11 @@ def is_retryable(exc): # retry on server errors and client errors # with 408 (request timeout) or 429 (rate limited), # don't retry on other client errors - if exc.status == "N/A": - return False - return not ((400 <= exc.status < 500) and exc.status not in (408, 429)) + if isinstance(exc.status, int): + return not ( + (400 <= exc.status < 500) and exc.status not in (408, 429) + ) + return False else: # retry on all other errors (eg. network) return True From 4025388d065844599671268de20737bffd71b02d Mon Sep 17 00:00:00 2001 From: Kshitij Mishra Date: Tue, 2 Jun 2026 15:30:42 -0400 Subject: [PATCH 2/3] fix(request): resolve type-shifting assignment, secure path concatenation, and strip dead ignore --- posthog/request.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/posthog/request.py b/posthog/request.py index 76f0a9fe..3fe21e7c 100644 --- a/posthog/request.py +++ b/posthog/request.py @@ -9,7 +9,7 @@ from typing import Any, List, Optional, Tuple, Union import requests -from requests.adapters import HTTPAdapter # type: ignore[import-untyped] +from requests.adapters import HTTPAdapter from urllib3.connection import HTTPConnection from urllib3.util.retry import Retry @@ -219,7 +219,7 @@ def determine_server_host(host: Optional[str]) -> str: def post( api_key: str, host: Optional[str] = None, - path=None, + path: str = "", gzip: bool = False, timeout: int = 15, session: Optional[requests.Session] = None, @@ -235,6 +235,7 @@ def post( data = json.dumps(body, cls=DatetimeSerializer) log.debug("making request: %s to url: %s", data, url) headers = {"Content-Type": "application/json", "User-Agent": USER_AGENT} + payload: str | bytes = data if gzip: headers["Content-Encoding"] = "gzip" buf = BytesIO() @@ -242,10 +243,10 @@ def post( # 'data' was produced by json.dumps(), # whose default encoding is utf-8. gz.write(data.encode("utf-8")) - data = buf.getvalue() + payload = buf.getvalue() res = (session or _get_session()).post( - url, data=data, headers=headers, timeout=timeout + url, data=payload, headers=headers, timeout=timeout ) if res.status_code == 200: From 01810bcadeda6f8bbcf27ba065acf2749b154525 Mon Sep 17 00:00:00 2001 From: Kshitij Mishra Date: Tue, 2 Jun 2026 15:48:43 -0400 Subject: [PATCH 3/3] fix(client, flags): handle optional before_send type variance and drop dead code signatures --- posthog/client.py | 23 ++++++++++------------- posthog/feature_flags.py | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/posthog/client.py b/posthog/client.py index d2cccc26..eba8702a 100644 --- a/posthog/client.py +++ b/posthog/client.py @@ -90,10 +90,8 @@ ) from posthog.version import VERSION -try: - import queue -except ImportError: - import Queue as queue + +from queue import Queue, Full MAX_DICT_SIZE = 50_000 @@ -279,7 +277,7 @@ def __init__( Initialization """ self._max_queue_size = max_queue_size - self.queue = queue.Queue(max_queue_size) + self.queue: Queue = Queue(max_queue_size) # api_key: This should be the Team API Key (token), public self.api_key = (project_api_key or "").strip() @@ -293,8 +291,10 @@ def __init__( self.host = determine_server_host(host) self.gzip = gzip self.timeout = timeout - self._feature_flags = None # private variable to store flags - self.feature_flags_by_key = None + self._feature_flags: Optional[list[Any]] = ( + None # private variable to store flags + ) + self.feature_flags_by_key: Optional[dict[str, Any]] = None self.group_type_mapping: Optional[dict[str, str]] = None self.cohorts: Optional[dict[str, Any]] = None self.poll_interval = poll_interval @@ -1245,7 +1245,7 @@ def _reinit_after_fork(self): as they'll be handled by the parent process's consumers. """ if self.consumers: - self.queue = queue.Queue(self._max_queue_size) + self.queue = Queue(self._max_queue_size) new_consumers = [] for old in self.consumers: @@ -1366,7 +1366,7 @@ def _enqueue(self, msg, disable_geoip): self.queue.put(msg, block=False) self.log.debug("enqueued %s.", msg["event"]) return sent_uuid - except queue.Full: + except Full: self.log.warning("analytics-python queue is full") return None @@ -2795,10 +2795,7 @@ def _initialize_flag_cache(self, cache_url): if not cache_url: return None - try: - from urllib.parse import parse_qs, urlparse - except ImportError: - from urlparse import parse_qs, urlparse + from urllib.parse import parse_qs, urlparse try: parsed = urlparse(cache_url) diff --git a/posthog/feature_flags.py b/posthog/feature_flags.py index 845f1d04..3b4f43f0 100644 --- a/posthog/feature_flags.py +++ b/posthog/feature_flags.py @@ -511,7 +511,7 @@ def compare(lhs, rhs, operator): parsed_value = None try: - parsed_value = float(value) # type: ignore + parsed_value = float(value) except Exception: pass