13
13
from playwright .sync_api import sync_playwright
14
14
15
15
from ._server import jupyter_server
16
- from ._utils import clear_notebook , isotime
16
+ from ._utils import clear_notebook , isotime , max_uint8_difference
17
17
18
18
__all__ = ["monitor" , "monitor_group" ]
19
19
@@ -50,8 +50,16 @@ def monitor_group():
50
50
default = 10 ,
51
51
help = "Time in s to wait after executing each cell" ,
52
52
)
53
+ @click .option (
54
+ "--atol" ,
55
+ default = 0 ,
56
+ help = (
57
+ "If an output image for a cell exists, a new image will only be written "
58
+ "out if the maximum uint8 difference between the two exceeds atol"
59
+ ),
60
+ )
53
61
@click .option ("--headless" , is_flag = True , help = "Whether to run in headless mode" )
54
- def monitor (notebook , url , output , wait_after_execute , headless ):
62
+ def monitor (notebook , url , output , wait_after_execute , atol , headless ):
55
63
if output is None :
56
64
output = f"output-{ iso_to_path (isotime ())} "
57
65
@@ -73,12 +81,12 @@ def monitor(notebook, url, output, wait_after_execute, headless):
73
81
clear_notebook (notebook , os .path .join (notebook_dir , "notebook.ipynb" ))
74
82
with jupyter_server (notebook_dir ) as server :
75
83
url = server .base_url + "/lab/tree/notebook.ipynb"
76
- _monitor_output (url , output , wait_after_execute , headless )
84
+ _monitor_output (url , output , wait_after_execute , atol , headless )
77
85
else :
78
- _monitor_output (url , output , wait_after_execute , headless )
86
+ _monitor_output (url , output , wait_after_execute , atol , headless )
79
87
80
88
81
- def _monitor_output (url , output , wait_after_execute , headless ):
89
+ def _monitor_output (url , output , wait_after_execute , atol , headless ):
82
90
# Index of the current last screenshot, by output index
83
91
last_screenshot = {}
84
92
@@ -129,13 +137,15 @@ def _monitor_output(url, output, wait_after_execute, headless):
129
137
# Check if server is asking us to select a kernel
130
138
dialogs = list (page .query_selector_all (".jp-Dialog-header" ))
131
139
for dialog in dialogs :
132
- if ' Select Kernel' in dialog .inner_text ():
140
+ if " Select Kernel" in dialog .inner_text ():
133
141
print ("Server is asking to select a kernel, accepting default" )
134
142
accept = list (page .query_selector_all (".jp-mod-accept" ))
135
143
if len (accept ) == 1 :
136
144
accept [0 ].click ()
137
145
else :
138
- print ("Error: multiple accept buttons found, not sure which to click" )
146
+ print (
147
+ "Error: multiple accept buttons found, not sure which to click" ,
148
+ )
139
149
sys .exit (1 )
140
150
141
151
last_screenshot = {}
@@ -222,25 +232,43 @@ def _monitor_output(url, output, wait_after_execute, headless):
222
232
):
223
233
print (" -> change detected!" )
224
234
225
- timestamp = isotime ()
226
-
227
- screenshot_filename = os .path .join (
228
- output ,
229
- f"output-{ output_index :03d} -{ iso_to_path (timestamp )} .png" ,
230
- )
231
- image = Image .open (BytesIO (screenshot_bytes ))
232
- image .save (screenshot_filename )
233
-
234
- log .write (
235
- f"{ timestamp } ,output-changed,{ output_index } ,{ screenshot_filename } \n " ,
236
- )
237
- log .flush ()
238
-
239
- print (
240
- f"Saving screenshot of output { output_index } at { timestamp } " ,
241
- )
242
-
243
- last_screenshot [output_index ] = screenshot_bytes
235
+ if output_index in last_screenshot :
236
+ max_diff = max_uint8_difference (
237
+ last_screenshot [output_index ],
238
+ screenshot_bytes ,
239
+ )
240
+ else :
241
+ max_diff = 256
242
+
243
+ if max_diff >= atol :
244
+ print (
245
+ f" -> maximum difference ({ max_diff } ) exceeds atol ({ atol } ), writing out image" ,
246
+ )
247
+
248
+ timestamp = isotime ()
249
+
250
+ screenshot_filename = os .path .join (
251
+ output ,
252
+ f"output-{ output_index :03d} -{ iso_to_path (timestamp )} .png" ,
253
+ )
254
+ image = Image .open (BytesIO (screenshot_bytes ))
255
+ image .save (screenshot_filename )
256
+
257
+ log .write (
258
+ f"{ timestamp } ,output-changed,{ output_index } ,{ screenshot_filename } \n " ,
259
+ )
260
+ log .flush ()
261
+
262
+ print (
263
+ f"Saving screenshot of output { output_index } at { timestamp } " ,
264
+ )
265
+
266
+ last_screenshot [output_index ] = screenshot_bytes
267
+
268
+ else :
269
+ print (
270
+ f" -> maximum difference ({ max_diff } ) not does exceed atol ({ atol } ), skipping" ,
271
+ )
244
272
245
273
print ("Stopping monitoring output and moving on to next input cell" )
246
274
0 commit comments