htmlcmp 1.1.1__tar.gz → 1.2.1__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.1.1 → htmlcmp-1.2.1}/PKG-INFO +1 -1
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/pyproject.toml +1 -1
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp/compare_output.py +1 -3
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp/compare_output_server.py +52 -38
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp/html_render_diff.py +2 -8
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp/tidy_output.py +30 -8
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp.egg-info/PKG-INFO +1 -1
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/tests/test_html_render_diff.py +0 -6
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/README.md +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/setup.cfg +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp/__init__.py +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp/common.py +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp.egg-info/SOURCES.txt +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp.egg-info/dependency_links.txt +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp.egg-info/entry_points.txt +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp.egg-info/requires.txt +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.1}/src/htmlcmp.egg-info/top_level.txt +0 -0
|
@@ -241,9 +241,7 @@ def main():
|
|
|
241
241
|
parser = argparse.ArgumentParser()
|
|
242
242
|
parser.add_argument("a", type=Path, help="Path to the first directory")
|
|
243
243
|
parser.add_argument("b", type=Path, help="Path to the second directory")
|
|
244
|
-
parser.add_argument(
|
|
245
|
-
"--driver", choices=["chrome", "firefox", "phantomjs"], default="firefox"
|
|
246
|
-
)
|
|
244
|
+
parser.add_argument("--driver", choices=["chrome", "firefox"], default="firefox")
|
|
247
245
|
parser.add_argument(
|
|
248
246
|
"--diff-output", type=Path, help="Output directory for diff images"
|
|
249
247
|
)
|
|
@@ -18,7 +18,6 @@ import watchdog.events
|
|
|
18
18
|
from htmlcmp.compare_output import comparable_file, compare_files
|
|
19
19
|
from htmlcmp.html_render_diff import get_browser, html_render_diff
|
|
20
20
|
|
|
21
|
-
|
|
22
21
|
logger = logging.getLogger(__name__)
|
|
23
22
|
|
|
24
23
|
|
|
@@ -253,6 +252,28 @@ class Comparator:
|
|
|
253
252
|
app = Flask("compare")
|
|
254
253
|
|
|
255
254
|
|
|
255
|
+
@app.route("/script.js")
|
|
256
|
+
def script_js():
|
|
257
|
+
logger.debug("Serving script.js")
|
|
258
|
+
|
|
259
|
+
return """
|
|
260
|
+
function updateRef(path) {
|
|
261
|
+
fetch(`/update_ref/${path}`)
|
|
262
|
+
.then(response => {
|
|
263
|
+
if (response.ok) {
|
|
264
|
+
alert(`Reference updated for ${path}`);
|
|
265
|
+
location.reload();
|
|
266
|
+
} else {
|
|
267
|
+
alert(`Failed to update reference for ${path}: ${response.statusText}`);
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
.catch(error => {
|
|
271
|
+
alert(`Error updating reference for ${path}: ${error}`);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
|
|
256
277
|
@app.route("/")
|
|
257
278
|
def root():
|
|
258
279
|
logger.debug("Generating root directory listing")
|
|
@@ -444,6 +465,7 @@ tr {
|
|
|
444
465
|
padding-left: calc(1.0rem * var(--depth));
|
|
445
466
|
}
|
|
446
467
|
</style>
|
|
468
|
+
<script src="/script.js"></script>
|
|
447
469
|
</head>
|
|
448
470
|
<body>
|
|
449
471
|
"""
|
|
@@ -493,6 +515,11 @@ function toggleChildren(parentId, show) {
|
|
|
493
515
|
document.querySelectorAll(`tr[data-parent-id="${parentId}"]`)
|
|
494
516
|
.forEach(child => {
|
|
495
517
|
child.hidden = !show;
|
|
518
|
+
if (!show) {
|
|
519
|
+
toggleChildren(child.dataset.entryId, false);
|
|
520
|
+
const toggle = child.querySelector(".toggle");
|
|
521
|
+
if (toggle) toggle.textContent = "▶";
|
|
522
|
+
}
|
|
496
523
|
});
|
|
497
524
|
}
|
|
498
525
|
|
|
@@ -507,21 +534,6 @@ function toggleAll(show) {
|
|
|
507
534
|
}
|
|
508
535
|
});
|
|
509
536
|
}
|
|
510
|
-
|
|
511
|
-
function updateRef(path) {
|
|
512
|
-
fetch(`/update_ref/${path}`)
|
|
513
|
-
.then(response => {
|
|
514
|
-
if (response.ok) {
|
|
515
|
-
alert(`Reference updated for ${path}`);
|
|
516
|
-
location.reload();
|
|
517
|
-
} else {
|
|
518
|
-
alert(`Failed to update reference for ${path}: ${response.statusText}`);
|
|
519
|
-
}
|
|
520
|
-
})
|
|
521
|
-
.catch(error => {
|
|
522
|
-
alert(`Error updating reference for ${path}: ${error}`);
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
537
|
</script>
|
|
526
538
|
</body>
|
|
527
539
|
</html>
|
|
@@ -553,6 +565,7 @@ def compare(path: str):
|
|
|
553
565
|
<style>
|
|
554
566
|
html,body {{height:100%;margin:0;}}
|
|
555
567
|
</style>
|
|
568
|
+
<script src="/script.js"></script>
|
|
556
569
|
</head>
|
|
557
570
|
<body style="display:flex;flex-flow:row;">
|
|
558
571
|
<div style="display:flex;flex:1;flex-flow:column;margin:5px;">
|
|
@@ -561,6 +574,7 @@ html,body {{height:100%;margin:0;}}
|
|
|
561
574
|
</div>
|
|
562
575
|
<div style="display:flex;flex:0 0 50px;flex-flow:column;">
|
|
563
576
|
<a href="/image_diff/{path}" target="_blank">diff</a>
|
|
577
|
+
<button onclick="updateRef('{path}')">▶</button>
|
|
564
578
|
<img src="/image_diff/{path}" width="50" height="0" style="flex:1;">
|
|
565
579
|
</div>
|
|
566
580
|
<div style="display:flex;flex:1;flex-flow:column;margin:5px;">
|
|
@@ -616,6 +630,27 @@ def file(variant: str, path: str):
|
|
|
616
630
|
return send_from_directory(variant_root, path)
|
|
617
631
|
|
|
618
632
|
|
|
633
|
+
@app.route("/update_ref/<path:path>")
|
|
634
|
+
def update_ref(path: str):
|
|
635
|
+
logger.debug(f"Updating reference for path: {path}")
|
|
636
|
+
|
|
637
|
+
if not isinstance(path, str):
|
|
638
|
+
raise TypeError("Path must be a string")
|
|
639
|
+
|
|
640
|
+
src = Config.path_b / path
|
|
641
|
+
dst = Config.path_a / path
|
|
642
|
+
|
|
643
|
+
if not src.exists():
|
|
644
|
+
return f"Source file does not exist: {src}", 404
|
|
645
|
+
|
|
646
|
+
if src.is_file():
|
|
647
|
+
shutil.copy2(src, dst)
|
|
648
|
+
else:
|
|
649
|
+
shutil.copytree(src, dst, dirs_exist_ok=True)
|
|
650
|
+
|
|
651
|
+
return "Reference updated", 200
|
|
652
|
+
|
|
653
|
+
|
|
619
654
|
def verbosity_to_level(verbosity: int) -> int:
|
|
620
655
|
if verbosity >= 3:
|
|
621
656
|
return logging.DEBUG
|
|
@@ -659,32 +694,11 @@ def setup_logging(
|
|
|
659
694
|
root_logger.addHandler(file_handler)
|
|
660
695
|
|
|
661
696
|
|
|
662
|
-
@app.route("/update_ref/<path:path>")
|
|
663
|
-
def update_ref(path: str):
|
|
664
|
-
logger.debug(f"Updating reference for path: {path}")
|
|
665
|
-
|
|
666
|
-
if not isinstance(path, str):
|
|
667
|
-
raise TypeError("Path must be a string")
|
|
668
|
-
|
|
669
|
-
src = Config.path_b / path
|
|
670
|
-
dst = Config.path_a / path
|
|
671
|
-
|
|
672
|
-
if not src.exists():
|
|
673
|
-
return f"Source file does not exist: {src}", 404
|
|
674
|
-
|
|
675
|
-
if src.is_file():
|
|
676
|
-
shutil.copy2(src, dst)
|
|
677
|
-
else:
|
|
678
|
-
shutil.copytree(src, dst, dirs_exist_ok=True)
|
|
679
|
-
|
|
680
|
-
return "Reference updated", 200
|
|
681
|
-
|
|
682
|
-
|
|
683
697
|
def main():
|
|
684
698
|
parser = argparse.ArgumentParser()
|
|
685
699
|
parser.add_argument("ref", type=Path, help="Path to the reference directory")
|
|
686
700
|
parser.add_argument("mon", type=Path, help="Path to the monitored directory")
|
|
687
|
-
parser.add_argument("--driver", choices=["chrome", "firefox"
|
|
701
|
+
parser.add_argument("--driver", choices=["chrome", "firefox"])
|
|
688
702
|
parser.add_argument("--max-workers", type=int, default=1)
|
|
689
703
|
parser.add_argument("--compare", action="store_true")
|
|
690
704
|
parser.add_argument("--port", type=int, default=5000)
|
|
@@ -70,11 +70,7 @@ def get_browser(
|
|
|
70
70
|
f"Expected int for max_width and max_height, got {type(max_width)} and {type(max_height)}"
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
-
if driver == "
|
|
74
|
-
if shutil.which("phantomjs") is None:
|
|
75
|
-
raise EnvironmentError("PhantomJS is not installed or not found in PATH")
|
|
76
|
-
browser = webdriver.PhantomJS()
|
|
77
|
-
elif driver == "firefox":
|
|
73
|
+
if driver == "firefox":
|
|
78
74
|
options = webdriver.FirefoxOptions()
|
|
79
75
|
options.add_argument("--headless")
|
|
80
76
|
browser = webdriver.Firefox(options=options)
|
|
@@ -109,9 +105,7 @@ def main():
|
|
|
109
105
|
parser = argparse.ArgumentParser()
|
|
110
106
|
parser.add_argument("a", type=Path, help="Path to the first HTML file")
|
|
111
107
|
parser.add_argument("b", type=Path, help="Path to the second HTML file")
|
|
112
|
-
parser.add_argument(
|
|
113
|
-
"--driver", choices=["chrome", "firefox", "phantomjs"], default="firefox"
|
|
114
|
-
)
|
|
108
|
+
parser.add_argument("--driver", choices=["chrome", "firefox"], default="firefox")
|
|
115
109
|
parser.add_argument("--max-width", default=1000)
|
|
116
110
|
parser.add_argument("--max-height", default=10000)
|
|
117
111
|
args = parser.parse_args()
|
|
@@ -10,7 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
from htmlcmp.common import bcolors
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def tidy_json(path: Path) -> int:
|
|
13
|
+
def tidy_json(path: Path, verbose: bool = False) -> int:
|
|
14
14
|
if not isinstance(path, Path):
|
|
15
15
|
raise TypeError("path must be a Path object")
|
|
16
16
|
if not path.is_file():
|
|
@@ -21,10 +21,11 @@ def tidy_json(path: Path) -> int:
|
|
|
21
21
|
json.load(f)
|
|
22
22
|
return 0
|
|
23
23
|
except ValueError:
|
|
24
|
+
print(f"{bcolors.FAIL}Error: {path} is not a valid JSON file{bcolors.ENDC}")
|
|
24
25
|
return 1
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
def tidy_html(path: Path, html_tidy_config: Path = None) -> int:
|
|
28
|
+
def tidy_html(path: Path, html_tidy_config: Path = None, verbose: bool = False) -> int:
|
|
28
29
|
if not isinstance(path, Path):
|
|
29
30
|
raise TypeError("path must be a Path object")
|
|
30
31
|
if not path.is_file():
|
|
@@ -41,6 +42,15 @@ def tidy_html(path: Path, html_tidy_config: Path = None) -> int:
|
|
|
41
42
|
result = subprocess.run(
|
|
42
43
|
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
|
|
43
44
|
)
|
|
45
|
+
if result.stdout:
|
|
46
|
+
if verbose and result.returncode == 0:
|
|
47
|
+
print(result.stdout)
|
|
48
|
+
elif verbose and result.returncode == 1:
|
|
49
|
+
print(f"{bcolors.WARNING}Warning: {path} has warnings{bcolors.ENDC}")
|
|
50
|
+
print(f"{bcolors.WARNING}{result.stdout}{bcolors.ENDC}")
|
|
51
|
+
elif verbose or result.returncode > 1:
|
|
52
|
+
print(f"{bcolors.FAIL}Error: {path} has errors{bcolors.ENDC}")
|
|
53
|
+
print(f"{bcolors.FAIL}{result.stdout}{bcolors.ENDC}")
|
|
44
54
|
if result.returncode == 1:
|
|
45
55
|
return 1
|
|
46
56
|
if result.returncode > 1:
|
|
@@ -48,16 +58,16 @@ def tidy_html(path: Path, html_tidy_config: Path = None) -> int:
|
|
|
48
58
|
return 0
|
|
49
59
|
|
|
50
60
|
|
|
51
|
-
def tidy_file(path: Path, html_tidy_config: Path = None) -> int:
|
|
61
|
+
def tidy_file(path: Path, html_tidy_config: Path = None, verbose: bool = False) -> int:
|
|
52
62
|
if not isinstance(path, Path):
|
|
53
63
|
raise TypeError("path must be a Path object")
|
|
54
64
|
if not path.is_file():
|
|
55
65
|
raise FileNotFoundError(f"{path} is not a file")
|
|
56
66
|
|
|
57
67
|
if path.suffix == ".json":
|
|
58
|
-
return tidy_json(path)
|
|
68
|
+
return tidy_json(path, verbose=verbose)
|
|
59
69
|
elif path.suffix == ".html":
|
|
60
|
-
return tidy_html(path, html_tidy_config=html_tidy_config)
|
|
70
|
+
return tidy_html(path, html_tidy_config=html_tidy_config, verbose=verbose)
|
|
61
71
|
|
|
62
72
|
|
|
63
73
|
def tidyable_file(path: Path) -> bool:
|
|
@@ -74,7 +84,11 @@ def tidyable_file(path: Path) -> bool:
|
|
|
74
84
|
|
|
75
85
|
|
|
76
86
|
def tidy_dir(
|
|
77
|
-
path: Path,
|
|
87
|
+
path: Path,
|
|
88
|
+
level: int = 0,
|
|
89
|
+
prefix: str = "",
|
|
90
|
+
html_tidy_config: Path = None,
|
|
91
|
+
verbose: bool = False,
|
|
78
92
|
) -> dict[str, list[Path]]:
|
|
79
93
|
if not isinstance(path, Path):
|
|
80
94
|
raise TypeError("path must be a Path object")
|
|
@@ -104,7 +118,7 @@ def tidy_dir(
|
|
|
104
118
|
|
|
105
119
|
for filename in [path.name for path in files]:
|
|
106
120
|
filepath = path / filename
|
|
107
|
-
tidy = tidy_file(filepath, html_tidy_config=html_tidy_config)
|
|
121
|
+
tidy = tidy_file(filepath, html_tidy_config=html_tidy_config, verbose=verbose)
|
|
108
122
|
if tidy == 0:
|
|
109
123
|
print(f"{prefix_file}{bcolors.OKGREEN}{filename} ✓{bcolors.ENDC}")
|
|
110
124
|
elif tidy == 1:
|
|
@@ -121,6 +135,7 @@ def tidy_dir(
|
|
|
121
135
|
level=level + 1,
|
|
122
136
|
prefix=prefix + "│ ",
|
|
123
137
|
html_tidy_config=html_tidy_config,
|
|
138
|
+
verbose=verbose,
|
|
124
139
|
)
|
|
125
140
|
result["warning"].extend(subresult["warning"])
|
|
126
141
|
result["error"].extend(subresult["error"])
|
|
@@ -134,9 +149,16 @@ def main():
|
|
|
134
149
|
parser.add_argument(
|
|
135
150
|
"--html-tidy-config", type=Path, help="Path to tidy config file"
|
|
136
151
|
)
|
|
152
|
+
parser.add_argument(
|
|
153
|
+
"--verbose",
|
|
154
|
+
action="store_true",
|
|
155
|
+
help="Print verbose output (warnings and errors)",
|
|
156
|
+
)
|
|
137
157
|
args = parser.parse_args()
|
|
138
158
|
|
|
139
|
-
result = tidy_dir(
|
|
159
|
+
result = tidy_dir(
|
|
160
|
+
args.path, html_tidy_config=args.html_tidy_config, verbose=args.verbose
|
|
161
|
+
)
|
|
140
162
|
if result["error"]:
|
|
141
163
|
return 1
|
|
142
164
|
|
|
@@ -31,12 +31,6 @@ def test_get_browser():
|
|
|
31
31
|
assert browser.name == "firefox"
|
|
32
32
|
browser.quit()
|
|
33
33
|
|
|
34
|
-
# Test with PhantomJS
|
|
35
|
-
if shutil.which("phantomjs") is not None:
|
|
36
|
-
browser = get_browser("phantomjs")
|
|
37
|
-
assert browser.name == "phantomjs"
|
|
38
|
-
browser.quit()
|
|
39
|
-
|
|
40
34
|
|
|
41
35
|
def test_html_render_diff():
|
|
42
36
|
test1 = Path(__file__).parent / "test1.html"
|
|
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
|