diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index de76528..b599e86 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,7 +6,7 @@ jobs: test: strategy: matrix: - python-version: [3.8, 3.9, '3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12'] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} diff --git a/.readthedocs.yml b/.readthedocs.yml index 99333c8..c45b822 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -14,4 +14,5 @@ conda: sphinx: builder: html - fail_on_warning: true \ No newline at end of file + fail_on_warning: true + configuration: doc/_config.yml \ No newline at end of file diff --git a/setup.py b/setup.py index e5c2a7d..d514fe8 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ REQUIRES = [ "pyyaml", - "posthog", + "posthog>=3.0", 'importlib-metadata;python_version<"3.8"', ] diff --git a/src/ploomber_core/telemetry/telemetry.py b/src/ploomber_core/telemetry/telemetry.py index 72d3f95..ef62ba7 100644 --- a/src/ploomber_core/telemetry/telemetry.py +++ b/src/ploomber_core/telemetry/telemetry.py @@ -419,6 +419,18 @@ def __init__(self, api_key, package_name, version, *, print_cloud_message=True): self.version = version self.print_cloud_message = print_cloud_message + # Initialize PostHog client + try: + self._posthog_client = posthog.Posthog( + api_key, host="https://us.i.posthog.com" + ) + except Exception as e: + raise ImportError( + "Failed to initialize posthog client. This likely means your posthog " + "version is incompatible. To fix this, either upgrade to posthog>=3.0 " + "or downgrade to ploomber-core<=0.2.26" + ) from e + @classmethod def from_package(cls, package_name, *, print_cloud_message=True, api_key=None): """ @@ -440,7 +452,6 @@ def log_api(self, action, client_time=None, total_runtime=None, metadata=None): if missing like timestamp, event id and stats information. """ - posthog.project_api_key = self.api_key metadata = metadata or {} event_id = uuid4() @@ -516,12 +527,19 @@ def log_api(self, action, client_time=None, total_runtime=None, metadata=None): "metadata": metadata, } - if is_install: - posthog.capture( - distinct_id=uid, event="install_success_indirect", properties=props - ) + if self._posthog_client is not None: + if is_install: + self._posthog_client.capture( + distinct_id=uid, + event="install_success_indirect", + properties=props, + ) - posthog.capture(distinct_id=uid, event=action, properties=props) + self._posthog_client.capture( + distinct_id=uid, event=action, properties=props + ) + else: + raise RuntimeError("Log call failed: PostHog client not initialized") # NOTE: should we log differently depending on the error type? # NOTE: how should we handle chained exceptions? diff --git a/tests/conftest.py b/tests/conftest.py index f09290d..9030f5f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -83,5 +83,10 @@ def external_access(request, monkeypatch_session): # https://github.com/pytest-dev/pytest/issues/7061#issuecomment-611892868 external_access = MagicMock() external_access.get_something = MagicMock(return_value="Mock was used.") - monkeypatch_session.setattr(posthog, "capture", external_access.get_something) + + mock_posthog_instance = MagicMock() + mock_posthog_instance.capture = external_access.get_something + mock_posthog_class = MagicMock(return_value=mock_posthog_instance) + + monkeypatch_session.setattr(posthog, "Posthog", mock_posthog_class) yield diff --git a/tests/telemetry/test_telemetry.py b/tests/telemetry/test_telemetry.py index f063556..2ddf791 100644 --- a/tests/telemetry/test_telemetry.py +++ b/tests/telemetry/test_telemetry.py @@ -826,7 +826,11 @@ def fake_capture(*args, **kwargs): log = logging.getLogger("posthog") log.error("some error happened") - monkeypatch.setattr(posthog, "capture", fake_capture) + mock_posthog_instance = Mock() + mock_posthog_instance.capture = fake_capture + mock_posthog_class = Mock(return_value=mock_posthog_instance) + + monkeypatch.setattr(posthog, "Posthog", mock_posthog_class) _telemetry = telemetry.Telemetry(MOCK_API_KEY, "ploomber", "0.14.0") with caplog.at_level(logging.ERROR, logger="posthog"): @@ -838,8 +842,12 @@ def fake_capture(*args, **kwargs): # TODO: test more of the values (I'm adding ANY to many of them) def test_log_api_stored_values(monkeypatch): mock_info = Mock(return_value=(True, "fake-uuid", False)) - mock = Mock() - monkeypatch.setattr(telemetry.posthog, "capture", mock) + mock_capture = Mock() + mock_posthog_instance = Mock() + mock_posthog_instance.capture = mock_capture + mock_posthog_class = Mock(return_value=mock_posthog_instance) + + monkeypatch.setattr(telemetry.posthog, "Posthog", mock_posthog_class) monkeypatch.setattr(telemetry, "_get_telemetry_info", mock_info) _telemetry = telemetry.Telemetry(MOCK_API_KEY, "some-package", "1.2.2") @@ -851,7 +859,7 @@ def test_log_api_stored_values(monkeypatch): f"{sys.version_info.micro}" ) - mock.assert_called_once_with( + mock_capture.assert_called_once_with( distinct_id="fake-uuid", event="some-action", properties={ @@ -876,8 +884,12 @@ def test_log_api_stored_values(monkeypatch): def test_log_call_stored_values(monkeypatch): mock_info = Mock(return_value=(True, "fake-uuid", False)) - mock = Mock() - monkeypatch.setattr(telemetry.posthog, "capture", mock) + mock_capture = Mock() + mock_posthog_instance = Mock() + mock_posthog_instance.capture = mock_capture + mock_posthog_class = Mock(return_value=mock_posthog_instance) + + monkeypatch.setattr(telemetry.posthog, "Posthog", mock_posthog_class) monkeypatch.setattr(telemetry, "_get_telemetry_info", mock_info) monkeypatch.setattr(telemetry.sys, "argv", ["/path/to/bin", "arg2", "arg2"]) @@ -894,7 +906,7 @@ def my_function(): f"{sys.version_info.micro}" ) - assert mock.call_args_list == [ + assert mock_capture.call_args_list == [ call( distinct_id="fake-uuid", event="some-package-some-action-success", diff --git a/tests/telemetry/test_telemetry_log_args.py b/tests/telemetry/test_telemetry_log_args.py index 8b339ee..af7a884 100644 --- a/tests/telemetry/test_telemetry_log_args.py +++ b/tests/telemetry/test_telemetry_log_args.py @@ -7,10 +7,14 @@ @pytest.fixture def mock_posthog(monkeypatch): mock_info = Mock(return_value=(True, "UUID", False)) - mock = Mock() - monkeypatch.setattr(telemetry_module.posthog, "capture", mock) + mock_capture = Mock() + mock_posthog_instance = Mock() + mock_posthog_instance.capture = mock_capture + mock_posthog_class = Mock(return_value=mock_posthog_instance) + + monkeypatch.setattr(telemetry_module.posthog, "Posthog", mock_posthog_class) monkeypatch.setattr(telemetry_module, "_get_telemetry_info", mock_info) - yield mock + yield mock_capture @pytest.mark.parametrize(