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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: htmlcmp
3
- Version: 1.0.15
3
+ Version: 1.0.17
4
4
  Summary: Compare HTML files by rendered output
5
5
  Author: Andreas Stefl
6
6
  Maintainer-email: Andreas Stefl <stefl.andreas@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "htmlcmp"
3
- version = "1.0.15"
3
+ version = "1.0.17"
4
4
  description = "Compare HTML files by rendered output"
5
5
  classifiers = []
6
6
  authors = [
@@ -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
- if event_type in ["opened"]:
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: htmlcmp
3
- Version: 1.0.15
3
+ Version: 1.0.17
4
4
  Summary: Compare HTML files by rendered output
5
5
  Author: Andreas Stefl
6
6
  Maintainer-email: Andreas Stefl <stefl.andreas@gmail.com>
File without changes
File without changes
File without changes