1313from playwright .sync_api import sync_playwright
1414
1515from ._server import jupyter_server
16- from ._utils import clear_notebook , isotime
16+ from ._utils import clear_notebook , isotime , max_uint8_difference
1717
1818__all__ = ["monitor" , "monitor_group" ]
1919
@@ -50,8 +50,16 @@ def monitor_group():
5050 default = 10 ,
5151 help = "Time in s to wait after executing each cell" ,
5252)
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+ )
5361@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 ):
5563 if output is None :
5664 output = f"output-{ iso_to_path (isotime ())} "
5765
@@ -73,12 +81,12 @@ def monitor(notebook, url, output, wait_after_execute, headless):
7381 clear_notebook (notebook , os .path .join (notebook_dir , "notebook.ipynb" ))
7482 with jupyter_server (notebook_dir ) as server :
7583 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 )
7785 else :
78- _monitor_output (url , output , wait_after_execute , headless )
86+ _monitor_output (url , output , wait_after_execute , atol , headless )
7987
8088
81- def _monitor_output (url , output , wait_after_execute , headless ):
89+ def _monitor_output (url , output , wait_after_execute , atol , headless ):
8290 # Index of the current last screenshot, by output index
8391 last_screenshot = {}
8492
@@ -129,13 +137,15 @@ def _monitor_output(url, output, wait_after_execute, headless):
129137 # Check if server is asking us to select a kernel
130138 dialogs = list (page .query_selector_all (".jp-Dialog-header" ))
131139 for dialog in dialogs :
132- if ' Select Kernel' in dialog .inner_text ():
140+ if " Select Kernel" in dialog .inner_text ():
133141 print ("Server is asking to select a kernel, accepting default" )
134142 accept = list (page .query_selector_all (".jp-mod-accept" ))
135143 if len (accept ) == 1 :
136144 accept [0 ].click ()
137145 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+ )
139149 sys .exit (1 )
140150
141151 last_screenshot = {}
@@ -222,25 +232,43 @@ def _monitor_output(url, output, wait_after_execute, headless):
222232 ):
223233 print (" -> change detected!" )
224234
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+ )
244272
245273 print ("Stopping monitoring output and moving on to next input cell" )
246274
0 commit comments