htmlcmp 1.0.15__tar.gz → 1.0.17__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/PKG-INFO +1 -1
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/pyproject.toml +1 -1
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp/compare_output_server.py +79 -2
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp.egg-info/PKG-INFO +1 -1
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/README.md +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/setup.cfg +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp/__init__.py +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp/common.py +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp/compare_output.py +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp/html_render_diff.py +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp/tidy_output.py +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp.egg-info/SOURCES.txt +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp.egg-info/dependency_links.txt +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp.egg-info/entry_points.txt +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp.egg-info/requires.txt +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/src/htmlcmp.egg-info/top_level.txt +0 -0
- {htmlcmp-1.0.15 → htmlcmp-1.0.17}/tests/test_html_render_diff.py +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
|
+
import io
|
|
4
5
|
import sys
|
|
5
6
|
import argparse
|
|
7
|
+
import logging
|
|
6
8
|
import threading
|
|
7
|
-
import io
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from concurrent.futures import ThreadPoolExecutor
|
|
10
11
|
|
|
@@ -16,6 +17,9 @@ from htmlcmp.compare_output import comparable_file, compare_files
|
|
|
16
17
|
from htmlcmp.html_render_diff import get_browser, html_render_diff
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
19
23
|
class Config:
|
|
20
24
|
path_a: Path = None
|
|
21
25
|
path_b: Path = None
|
|
@@ -36,19 +40,32 @@ class Observer:
|
|
|
36
40
|
event_type = event.event_type
|
|
37
41
|
src_path = Path(event.src_path)
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
logger.verbose(f"Watchdog event: {event_type} {src_path}")
|
|
44
|
+
|
|
45
|
+
if event_type not in ["moved", "deleted", "created", "modified"]:
|
|
40
46
|
return
|
|
41
47
|
|
|
42
48
|
if src_path.is_file():
|
|
49
|
+
logger.debug(
|
|
50
|
+
f"Submit watchdog file change: {event_type} {src_path}"
|
|
51
|
+
)
|
|
43
52
|
Config.comparator.submit(src_path.relative_to(self._path))
|
|
53
|
+
|
|
54
|
+
logger.info("Create watchdog for paths:")
|
|
55
|
+
logger.info(f" A: {Config.path_a}")
|
|
56
|
+
logger.info(f" B: {Config.path_b}")
|
|
57
|
+
|
|
44
58
|
self._observer = watchdog.observers.Observer()
|
|
45
59
|
self._observer.schedule(Handler(Config.path_a), Config.path_a, recursive=True)
|
|
46
60
|
self._observer.schedule(Handler(Config.path_b), Config.path_b, recursive=True)
|
|
47
61
|
|
|
48
62
|
def start(self):
|
|
63
|
+
logger.info("Starting watchdog observer")
|
|
49
64
|
self._observer.start()
|
|
50
65
|
|
|
51
66
|
def init_compare(a: Path, b: Path):
|
|
67
|
+
logger.verbose(f"Initial compare: {a} vs {b}")
|
|
68
|
+
|
|
52
69
|
if not isinstance(a, Path) or not isinstance(b, Path):
|
|
53
70
|
raise TypeError("Paths must be of type Path")
|
|
54
71
|
if not a.is_dir() or not b.is_dir():
|
|
@@ -63,16 +80,23 @@ class Observer:
|
|
|
63
80
|
|
|
64
81
|
for name in common:
|
|
65
82
|
if (a / name).is_file() and comparable_file(a / name):
|
|
83
|
+
logger.debug(
|
|
84
|
+
f"Submit initial file comparison: {common_path / name}"
|
|
85
|
+
)
|
|
66
86
|
Config.comparator.submit(common_path / name)
|
|
67
87
|
elif (a / name).is_dir():
|
|
68
88
|
init_compare(a / name, b / name)
|
|
69
89
|
|
|
90
|
+
logger.info("Kick off initial comparison of all files")
|
|
70
91
|
init_compare(Config.path_a, Config.path_b)
|
|
92
|
+
logger.info("Initial comparison submitted")
|
|
71
93
|
|
|
72
94
|
def stop(self):
|
|
95
|
+
logger.info("Stopping watchdog observer")
|
|
73
96
|
self._observer.stop()
|
|
74
97
|
|
|
75
98
|
def join(self):
|
|
99
|
+
logger.info("Joining watchdog observer")
|
|
76
100
|
self._observer.join()
|
|
77
101
|
|
|
78
102
|
|
|
@@ -84,6 +108,8 @@ class Comparator:
|
|
|
84
108
|
browser = get_browser(driver=Config.driver)
|
|
85
109
|
Config.thread_local.browser = browser
|
|
86
110
|
|
|
111
|
+
logger.info(f"Creating comparator with {max_workers} workers")
|
|
112
|
+
|
|
87
113
|
self._executor = ThreadPoolExecutor(
|
|
88
114
|
max_workers=max_workers, initializer=initializer
|
|
89
115
|
)
|
|
@@ -91,6 +117,8 @@ class Comparator:
|
|
|
91
117
|
self._future = {}
|
|
92
118
|
|
|
93
119
|
def submit(self, path: Path):
|
|
120
|
+
logger.debug(f"Submitting comparison for path: {path}")
|
|
121
|
+
|
|
94
122
|
if not isinstance(path, Path):
|
|
95
123
|
raise TypeError("Path must be of type Path")
|
|
96
124
|
|
|
@@ -106,6 +134,8 @@ class Comparator:
|
|
|
106
134
|
self._future[path] = self._executor.submit(self.compare, path)
|
|
107
135
|
|
|
108
136
|
def compare(self, path: Path):
|
|
137
|
+
logger.debug(f"Comparing files for path: {path}")
|
|
138
|
+
|
|
109
139
|
if not isinstance(path, Path):
|
|
110
140
|
raise TypeError("Path must be of type Path")
|
|
111
141
|
if path not in self._future:
|
|
@@ -121,6 +151,8 @@ class Comparator:
|
|
|
121
151
|
self._future.pop(path)
|
|
122
152
|
|
|
123
153
|
def result(self, path: Path):
|
|
154
|
+
logger.debug(f"Getting comparison result for path: {path}")
|
|
155
|
+
|
|
124
156
|
if not isinstance(path, Path):
|
|
125
157
|
raise TypeError("Path must be of type Path")
|
|
126
158
|
|
|
@@ -129,6 +161,8 @@ class Comparator:
|
|
|
129
161
|
return "unknown"
|
|
130
162
|
|
|
131
163
|
def result_symbol(self, path: Path):
|
|
164
|
+
logger.debug(f"Getting comparison result symbol for path: {path}")
|
|
165
|
+
|
|
132
166
|
if not isinstance(path, Path):
|
|
133
167
|
raise TypeError("Path must be of type Path")
|
|
134
168
|
|
|
@@ -142,6 +176,8 @@ class Comparator:
|
|
|
142
176
|
return "⛔"
|
|
143
177
|
|
|
144
178
|
def result_css(self, path: Path):
|
|
179
|
+
logger.debug(f"Getting comparison result CSS for path: {path}")
|
|
180
|
+
|
|
145
181
|
if not isinstance(path, Path):
|
|
146
182
|
raise TypeError("Path must be of type Path")
|
|
147
183
|
|
|
@@ -160,6 +196,8 @@ app = Flask("compare")
|
|
|
160
196
|
|
|
161
197
|
@app.route("/")
|
|
162
198
|
def root():
|
|
199
|
+
logger.debug("Generating root directory listing")
|
|
200
|
+
|
|
163
201
|
def print_tree(a: Path, b: Path):
|
|
164
202
|
if not isinstance(a, Path) or not isinstance(b, Path):
|
|
165
203
|
raise TypeError("Paths must be of type Path")
|
|
@@ -239,6 +277,8 @@ def root():
|
|
|
239
277
|
|
|
240
278
|
@app.route("/compare/<path:path>")
|
|
241
279
|
def compare(path: str):
|
|
280
|
+
logger.debug(f"Generating comparison page for path: {path}")
|
|
281
|
+
|
|
242
282
|
if not isinstance(path, str):
|
|
243
283
|
raise TypeError("Path must be a string")
|
|
244
284
|
|
|
@@ -279,6 +319,8 @@ iframe_b.contentWindow.addEventListener('scroll', function(event) {{
|
|
|
279
319
|
|
|
280
320
|
@app.route("/image_diff/<path:path>")
|
|
281
321
|
def image_diff(path: str):
|
|
322
|
+
logger.debug(f"Generating image diff for path: {path}")
|
|
323
|
+
|
|
282
324
|
if not isinstance(path, str):
|
|
283
325
|
raise TypeError("Path must be a string")
|
|
284
326
|
|
|
@@ -295,6 +337,8 @@ def image_diff(path: str):
|
|
|
295
337
|
|
|
296
338
|
@app.route("/file/<variant>/<path:path>")
|
|
297
339
|
def file(variant: str, path: str):
|
|
340
|
+
logger.debug(f"Serving file for variant: {variant}, path: {path}")
|
|
341
|
+
|
|
298
342
|
if not isinstance(variant, str) or not isinstance(path, str):
|
|
299
343
|
raise TypeError("Variant and path must be strings")
|
|
300
344
|
if variant not in ["a", "b"]:
|
|
@@ -304,6 +348,30 @@ def file(variant: str, path: str):
|
|
|
304
348
|
return send_from_directory(variant_root, path)
|
|
305
349
|
|
|
306
350
|
|
|
351
|
+
def setup_logging(verbosity: int):
|
|
352
|
+
if verbosity >= 3:
|
|
353
|
+
level = logging.VERBOSE
|
|
354
|
+
elif verbosity == 2:
|
|
355
|
+
level = logging.DEBUG
|
|
356
|
+
elif verbosity == 1:
|
|
357
|
+
level = logging.INFO
|
|
358
|
+
else:
|
|
359
|
+
level = logging.WARNING
|
|
360
|
+
|
|
361
|
+
formatter = logging.Formatter(
|
|
362
|
+
fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
363
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
console_handler = logging.StreamHandler(sys.stderr)
|
|
367
|
+
console_handler.setFormatter(formatter)
|
|
368
|
+
|
|
369
|
+
root_logger = logging.getLogger()
|
|
370
|
+
root_logger.setLevel(level)
|
|
371
|
+
root_logger.handlers.clear()
|
|
372
|
+
root_logger.addHandler(console_handler)
|
|
373
|
+
|
|
374
|
+
|
|
307
375
|
def main():
|
|
308
376
|
parser = argparse.ArgumentParser()
|
|
309
377
|
parser.add_argument("a", type=Path, help="Path to the first directory")
|
|
@@ -314,8 +382,17 @@ def main():
|
|
|
314
382
|
parser.add_argument("--max-workers", type=int, default=1)
|
|
315
383
|
parser.add_argument("--compare", action="store_true")
|
|
316
384
|
parser.add_argument("--port", type=int, default=5000)
|
|
385
|
+
parser.add_argument(
|
|
386
|
+
"-v",
|
|
387
|
+
"--verbose",
|
|
388
|
+
action="count",
|
|
389
|
+
default=0,
|
|
390
|
+
help="Increase verbosity (-v, -vv, -vvv)",
|
|
391
|
+
)
|
|
317
392
|
args = parser.parse_args()
|
|
318
393
|
|
|
394
|
+
setup_logging(args.verbose)
|
|
395
|
+
|
|
319
396
|
Config.path_a = args.a
|
|
320
397
|
Config.path_b = args.b
|
|
321
398
|
Config.driver = args.driver
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|