Skip to content

Commit 93e0f3f

Browse files
Anon metrics (#113)
1 parent d8bef93 commit 93e0f3f

File tree

4 files changed

+108
-2
lines changed

4 files changed

+108
-2
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,12 @@ repos:
219219

220220
See https://pre-commit.com/ for more...
221221

222+
## Disable anonymous metrics
223+
224+
We collect anonymous usage metrics to improve nbmake.
225+
226+
You can disable them by setting the environment variable NBMAKE_METRICS=0
227+
222228
## Disable Nbmake
223229

224230
Implicitly:

src/nbmake/metrics.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import json
2+
import os
3+
import platform
4+
import sys
5+
import time
6+
import urllib.request
7+
8+
9+
def submit_event():
10+
"""We collect anonymous usage metrics to improve nbmake.
11+
12+
You can disable them by setting the environment variable NBMAKE_METRICS=0.
13+
"""
14+
mixpanel_token = "8440a5d8fa0ec1d43b6bcaf76037fae7"
15+
url = "https://api-eu.mixpanel.com/track"
16+
17+
payload = [
18+
{
19+
"event": "Invocation",
20+
"properties": {
21+
"token": mixpanel_token,
22+
"$os": platform.system(),
23+
"time": int(time.time()),
24+
"platform": platform.platform(),
25+
"python": sys.version,
26+
"ci": os.getenv("CI", False) and True,
27+
},
28+
}
29+
]
30+
headers = {
31+
"accept": "text/plain",
32+
"content-type": "application/json",
33+
}
34+
req = urllib.request.Request(
35+
url, data=json.dumps(payload).encode("utf8"), headers=headers
36+
)
37+
response = urllib.request.urlopen(req, timeout=1.0)
38+
39+
return response.read().decode("utf8")

src/nbmake/pytest_plugin.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import asyncio
2+
import os
23
import sys
34
from pathlib import Path
45
from typing import Any, Optional
56

7+
import nbmake.metrics
8+
69
try:
710
from importlib.metadata import version
811
except ImportError:
9-
from importlib_metadata import version
12+
from importlib_metadata import version # type: ignore
1013

1114
from .pytest_items import NotebookFile
1215

@@ -65,3 +68,16 @@ def pytest_collect_file(path: str, parent: Any) -> Optional[Any]:
6568
return NotebookFile.from_parent(parent, path=p)
6669

6770
return None
71+
72+
73+
def pytest_terminal_summary(terminalreporter: Any, exitstatus: int, config: Any):
74+
if not config.option.nbmake:
75+
return
76+
77+
if os.getenv("NBMAKE_METRICS", "1") != "1":
78+
return
79+
80+
try:
81+
nbmake.metrics.submit_event()
82+
except:
83+
pass

tests/test_pytest_plugin.py

+46-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from __future__ import print_function
22

33
import os
4-
from importlib import import_module, reload
4+
from importlib import import_module, invalidate_caches, reload
55
from pathlib import Path
6+
from unittest.mock import patch
67

78
from nbformat import read
89
from pytest import ExitCode, Pytester
@@ -207,3 +208,47 @@ def test_when_not_json_then_correct_err_msg(pytester: Pytester, testdir2: Never)
207208
assert longrepr is not None
208209
assert "NBMAKE INTERNAL ERROR" not in longrepr.term
209210
assert "json" in longrepr.term
211+
212+
213+
def test_when_testing_nbs_then_submit_metrics(pytester: Pytester):
214+
write_nb(failing_nb, Path(pytester.path) / "a.ipynb")
215+
216+
with patch("nbmake.metrics.submit_event", return_value="1") as mock_method:
217+
hook_recorder = pytester.inline_run("--nbmake", "-v")
218+
assert hook_recorder.ret == ExitCode.TESTS_FAILED
219+
mock_method.assert_called_once()
220+
221+
222+
def test_when_metrics_fail_then_ignore(pytester: Pytester):
223+
write_nb(failing_nb, Path(pytester.path) / "a.ipynb")
224+
225+
with patch("nbmake.metrics.submit_event", return_value="1") as mock_method:
226+
mock_method.side_effect = Exception("test")
227+
write_nb(passing_nb, Path(pytester.path) / "a.ipynb")
228+
229+
items, hook_recorder = pytester.inline_genitems("--nbmake")
230+
231+
assert hook_recorder.ret == ExitCode.OK
232+
assert len(items) == 1
233+
mock_method.assert_called_once()
234+
235+
236+
def test_when_metrics_disabled_dont_log_metrics(pytester: Pytester):
237+
write_nb(failing_nb, Path(pytester.path) / "a.ipynb")
238+
# not thread-safe
239+
os.environ["NBMAKE_METRICS"] = "0"
240+
241+
with patch("nbmake.metrics.submit_event", return_value="1") as mock_method:
242+
hook_recorder = pytester.inline_run("--nbmake", "-v")
243+
assert hook_recorder.ret == ExitCode.TESTS_FAILED
244+
mock_method.assert_not_called()
245+
246+
del os.environ["NBMAKE_METRICS"]
247+
248+
249+
def test_when_no_nbmake_flag_then_no_metrics(pytester: Pytester):
250+
write_nb(failing_nb, Path(pytester.path) / "a.ipynb")
251+
with patch("nbmake.metrics.submit_event", return_value="1") as mock_method:
252+
hook_recorder = pytester.inline_run("-v")
253+
assert hook_recorder.ret == ExitCode.NO_TESTS_COLLECTED
254+
mock_method.assert_not_called()

0 commit comments

Comments
 (0)