88from pathlib import Path
99from concurrent .futures import ThreadPoolExecutor
1010
11- from compare_output import comparable_file , compare_files
1211from flask import Flask , send_from_directory , send_file
1312import watchdog .observers
1413import watchdog .events
1514
15+ from compare_output import comparable_file , compare_files
1616from html_render_diff import get_browser , html_render_diff
1717
1818
1919class Config :
20- path_a = None
21- path_b = None
22- driver = None
20+ path_a : Path = None
21+ path_b : Path = None
22+ driver : str = None
2323 observer = None
2424 comparator = None
2525 browser = None
@@ -46,7 +46,12 @@ def dispatch(self, event):
4646 def start (self ):
4747 self ._observer .start ()
4848
49- def init_compare (a , b ):
49+ def init_compare (a : Path , b : Path ):
50+ if not isinstance (a , Path ) or not isinstance (b , Path ):
51+ raise TypeError ("Paths must be of type Path" )
52+ if not a .is_dir () or not b .is_dir ():
53+ raise ValueError ("Both paths must be directories" )
54+
5055 common_path = a / Config .path_a
5156
5257 left = sorted (p .name for p in a .iterdir ())
@@ -70,7 +75,7 @@ def join(self):
7075
7176
7277class Comparator :
73- def __init__ (self , max_workers ):
78+ def __init__ (self , max_workers : int ):
7479 def initializer ():
7580 browser = getattr (Config .thread_local , "browser" , None )
7681 if browser is None :
@@ -83,7 +88,10 @@ def initializer():
8388 self ._result = {}
8489 self ._future = {}
8590
86- def submit (self , path ):
91+ def submit (self , path : Path ):
92+ if not isinstance (path , Path ):
93+ raise TypeError ("Path must be of type Path" )
94+
8795 if path in self ._future :
8896 try :
8997 self ._future [path ].cancel ()
@@ -95,7 +103,14 @@ def submit(self, path):
95103 self ._result [path ] = "pending"
96104 self ._future [path ] = self ._executor .submit (self .compare , path )
97105
98- def compare (self , path ):
106+ def compare (self , path : Path ):
107+ if not isinstance (path , Path ):
108+ raise TypeError ("Path must be of type Path" )
109+ if not path .is_file ():
110+ raise ValueError ("Path must be a file" )
111+ if path not in self ._future :
112+ raise RuntimeError ("Path not submitted for comparison" )
113+
99114 browser = getattr (Config .thread_local , "browser" , None )
100115 result = compare_files (
101116 Config .path_a / path ,
@@ -105,12 +120,18 @@ def compare(self, path):
105120 self ._result [path ] = "same" if result else "different"
106121 self ._future .pop (path )
107122
108- def result (self , path ):
123+ def result (self , path : Path ):
124+ if not isinstance (path , Path ):
125+ raise TypeError ("Path must be of type Path" )
126+
109127 if path in self ._result :
110128 return self ._result [path ]
111129 return "unknown"
112130
113- def result_symbol (self , path ):
131+ def result_symbol (self , path : Path ):
132+ if not isinstance (path , Path ):
133+ raise TypeError ("Path must be of type Path" )
134+
114135 result = self .result (path )
115136 if result == "pending" :
116137 return "🔄"
@@ -120,7 +141,10 @@ def result_symbol(self, path):
120141 return "❌"
121142 return "⛔"
122143
123- def result_css (self , path ):
144+ def result_css (self , path : Path ):
145+ if not isinstance (path , Path ):
146+ raise TypeError ("Path must be of type Path" )
147+
124148 result = self .result (path )
125149 if result == "pending" :
126150 return "color:blue;"
@@ -136,7 +160,12 @@ def result_css(self, path):
136160
137161@app .route ("/" )
138162def root ():
139- def print_tree (a , b ):
163+ def print_tree (a : Path , b : Path ):
164+ if not isinstance (a , Path ) or not isinstance (b , Path ):
165+ raise TypeError ("Paths must be of type Path" )
166+ if not a .is_dir () or not b .is_dir ():
167+ raise ValueError ("Both paths must be directories" )
168+
140169 common_path = a / Config .path_a
141170
142171 left = sorted (p .name for p in a .iterdir ())
@@ -209,7 +238,10 @@ def print_tree(a, b):
209238
210239
211240@app .route ("/compare/<path:path>" )
212- def compare (path ):
241+ def compare (path : str ):
242+ if not isinstance (path , str ):
243+ raise TypeError ("Path must be a string" )
244+
213245 return f"""<!DOCTYPE html>
214246<html>
215247<head>
@@ -219,7 +251,7 @@ def compare(path):
219251</head>
220252<body style="display:flex;flex-flow:row;">
221253<div style="display:flex;flex:1;flex-flow:column;margin:5px;">
222- <a href="/file/a/{ path } ">{ Config .path_a / path } </a>
254+ <a href="/file/a/{ path } ">{ Config .path_a / path } </a>
223255 <iframe id="a" src="/file/a/{ path } " title="a" frameborder="0" align="left" style="flex:1;"></iframe>
224256</div>
225257<div style="display:flex;flex:0 0 50px;flex-flow:column;">
@@ -246,7 +278,10 @@ def compare(path):
246278
247279
248280@app .route ("/image_diff/<path:path>" )
249- def image_diff (path ):
281+ def image_diff (path : str ):
282+ if not isinstance (path , str ):
283+ raise TypeError ("Path must be a string" )
284+
250285 diff , _ = html_render_diff (
251286 Config .path_a / path ,
252287 Config .path_b / path ,
@@ -259,7 +294,12 @@ def image_diff(path):
259294
260295
261296@app .route ("/file/<variant>/<path:path>" )
262- def file (variant , path ):
297+ def file (variant : str , path : str ):
298+ if not isinstance (variant , str ) or not isinstance (path , str ):
299+ raise TypeError ("Variant and path must be strings" )
300+ if variant not in ["a" , "b" ]:
301+ raise ValueError ("Variant must be 'a' or 'b'" )
302+
263303 variant_root = Config .path_a if variant == "a" else Config .path_b
264304 return send_from_directory (variant_root , path )
265305
0 commit comments