diff --git a/.sampo/changesets/noble-iceseeker-aurelien.md b/.sampo/changesets/noble-iceseeker-aurelien.md new file mode 100644 index 00000000..4bfaf092 --- /dev/null +++ b/.sampo/changesets/noble-iceseeker-aurelien.md @@ -0,0 +1,5 @@ +--- +pypi/posthog: patch +--- + +Fix module-level settings propagation to the default client diff --git a/posthog/__init__.py b/posthog/__init__.py index ff2dc7eb..2094536d 100644 --- a/posthog/__init__.py +++ b/posthog/__init__.py @@ -303,8 +303,10 @@ def get_tags() -> Dict[str, Any]: the corresponding constructor arguments. Attributes: - api_key: Project API key/token used by the global client. Missing or blank - values create a disabled no-op global client. + api_key: Deprecated legacy alias for ``project_api_key``. + project_api_key: Preferred project API key setting. It takes precedence when + both it and ``api_key`` are configured; missing or blank values create a + disabled no-op global client. host: PostHog ingestion host. Defaults to the US ingestion endpoint when not set. on_error: Optional callback invoked by background consumers when event upload @@ -335,6 +337,8 @@ def get_tags() -> Dict[str, Any]: call if the metrics API hasn't been used yet. enable_exception_autocapture: Automatically capture uncaught exceptions. log_captured_exceptions: Also log exceptions captured by error tracking. + project_root: Root path used to determine in-app exception stack frames. + privacy_mode: Capture AI usage metadata without prompt inputs or outputs. before_send: Optional callback that can modify or drop events before upload. Return ``None`` to drop an event. enable_local_evaluation: Whether to poll feature flag definitions for local @@ -362,6 +366,7 @@ def get_tags() -> Dict[str, Any]: exception_autocapture_refill_interval_seconds: Seconds between token refills for autocaptured exception rate limiting. """ +# Deprecated legacy alias for project_api_key. Kept for backwards compatibility. api_key = None # type: Optional[str] host = None # type: Optional[str] on_error = None # type: Optional[Callable] @@ -371,6 +376,7 @@ def get_tags() -> Dict[str, Any]: disabled = False # type: bool secret_key = None # type: Optional[str] personal_api_key = None # type: Optional[str] # Deprecated: use secret_key +# Preferred project token setting; takes precedence over the legacy api_key alias. project_api_key = None # type: Optional[str] poll_interval = 30 # type: int disable_geoip = True # type: bool @@ -1157,15 +1163,16 @@ def setup() -> Client: ``setup()`` is called automatically by global APIs such as ``capture()``. Returns: - The global ``Client`` instance. If ``api_key`` is missing or blank, - the client is disabled and module-level calls become no-ops. + The global ``Client`` instance. If both ``api_key`` and + ``project_api_key`` are missing or blank, the client is disabled and + module-level calls become no-ops. Category: Initialization """ global default_client if not default_client: - configured_api_key = api_key.strip() if api_key else "" + configured_api_key = (project_api_key or api_key or "").strip() default_client = Client( configured_api_key, host=host, @@ -1188,6 +1195,8 @@ def setup() -> Client: # 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) enable_exception_autocapture=enable_exception_autocapture, log_captured_exceptions=log_captured_exceptions, + project_root=project_root, + privacy_mode=privacy_mode, before_send=before_send, enable_local_evaluation=enable_local_evaluation, flag_definition_cache_provider=flag_definition_cache_provider, @@ -1208,6 +1217,7 @@ def setup() -> Client: # for API keys that become empty after trimming. default_client.disabled = disabled or not default_client.api_key default_client.debug = debug + default_client.privacy_mode = bool(privacy_mode) default_client._set_before_send(before_send) default_client._use_ai_lane = bool(_use_ai_lane) default_client._enable_multimodal_capture = bool(_enable_multimodal_capture) diff --git a/posthog/test/test_module.py b/posthog/test/test_module.py index ec3031c4..3ef50945 100644 --- a/posthog/test/test_module.py +++ b/posthog/test/test_module.py @@ -46,15 +46,25 @@ class TestModuleLevelSetup(unittest.TestCase): def setUp(self): self._original_default_client = posthog.default_client self._original_api_key = posthog.api_key + self._original_project_api_key = posthog.project_api_key + self._original_project_root = posthog.project_root + self._original_privacy_mode = posthog.privacy_mode self._original_disabled = posthog.disabled self._original_send = posthog.send posthog.default_client = None + posthog.api_key = None + posthog.project_api_key = None + posthog.project_root = None + posthog.privacy_mode = False posthog.disabled = False posthog.send = False def tearDown(self): posthog.default_client = self._original_default_client posthog.api_key = self._original_api_key + posthog.project_api_key = self._original_project_api_key + posthog.project_root = self._original_project_root + posthog.privacy_mode = self._original_privacy_mode posthog.disabled = self._original_disabled posthog.send = self._original_send @@ -77,6 +87,43 @@ def test_setup_configures_disabled_client_when_api_key_is_blank( self.assertTrue(client.disabled) self.assertIsNone(posthog.capture("Module Python Event", distinct_id="john")) + def test_setup_uses_project_api_key_when_api_key_is_unset(self): + posthog.project_api_key = " phc_project_key " + + client = posthog.setup() + + self.assertEqual(client.api_key, "phc_project_key") + self.assertFalse(client.disabled) + + def test_setup_prefers_project_api_key_over_api_key(self): + posthog.api_key = "phc_api_key" + posthog.project_api_key = "phc_project_key" + + client = posthog.setup() + + self.assertEqual(client.api_key, "phc_project_key") + + def test_setup_propagates_project_root(self): + posthog.api_key = "phc_test" + posthog.project_root = "/path/to/project" + + client = posthog.setup() + + self.assertEqual(client.project_root, "/path/to/project") + + def test_setup_propagates_and_updates_privacy_mode(self): + posthog.api_key = "phc_test" + posthog.privacy_mode = True + + client = posthog.setup() + + self.assertTrue(client.privacy_mode) + + posthog.privacy_mode = False + + self.assertIs(posthog.setup(), client) + self.assertFalse(client.privacy_mode) + class TestModuleLevelWrappers(unittest.TestCase): """Test that module-level wrapper functions in posthog/__init__.py