htmlcmp 1.1.1__tar.gz → 1.2.0__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.0}/PKG-INFO +1 -1
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/pyproject.toml +1 -1
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp/compare_output_server.py +51 -36
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp/tidy_output.py +30 -8
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp.egg-info/PKG-INFO +1 -1
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/README.md +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/setup.cfg +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp/__init__.py +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp/common.py +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp/compare_output.py +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp/html_render_diff.py +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp.egg-info/SOURCES.txt +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp.egg-info/dependency_links.txt +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp.egg-info/entry_points.txt +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp.egg-info/requires.txt +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/src/htmlcmp.egg-info/top_level.txt +0 -0
- {htmlcmp-1.1.1 → htmlcmp-1.2.0}/tests/test_html_render_diff.py +0 -0
|
@@ -253,6 +253,28 @@ class Comparator:
|
|
|
253
253
|
app = Flask("compare")
|
|
254
254
|
|
|
255
255
|
|
|
256
|
+
@app.route("/script.js")
|
|
257
|
+
def script_js():
|
|
258
|
+
logger.debug("Serving script.js")
|
|
259
|
+
|
|
260
|
+
return """
|
|
261
|
+
function updateRef(path) {
|
|
262
|
+
fetch(`/update_ref/${path}`)
|
|
263
|
+
.then(response => {
|
|
264
|
+
if (response.ok) {
|
|
265
|
+
alert(`Reference updated for ${path}`);
|
|
266
|
+
location.reload();
|
|
267
|
+
} else {
|
|
268
|
+
alert(`Failed to update reference for ${path}: ${response.statusText}`);
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
.catch(error => {
|
|
272
|
+
alert(`Error updating reference for ${path}: ${error}`);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
|
|
256
278
|
@app.route("/")
|
|
257
279
|
def root():
|
|
258
280
|
logger.debug("Generating root directory listing")
|
|
@@ -444,6 +466,7 @@ tr {
|
|
|
444
466
|
padding-left: calc(1.0rem * var(--depth));
|
|
445
467
|
}
|
|
446
468
|
</style>
|
|
469
|
+
<script src="/script.js"></script>
|
|
447
470
|
</head>
|
|
448
471
|
<body>
|
|
449
472
|
"""
|
|
@@ -493,6 +516,11 @@ function toggleChildren(parentId, show) {
|
|
|
493
516
|
document.querySelectorAll(`tr[data-parent-id="${parentId}"]`)
|
|
494
517
|
.forEach(child => {
|
|
495
518
|
child.hidden = !show;
|
|
519
|
+
if (!show) {
|
|
520
|
+
toggleChildren(child.dataset.entryId, false);
|
|
521
|
+
const toggle = child.querySelector(".toggle");
|
|
522
|
+
if (toggle) toggle.textContent = "▶";
|
|
523
|
+
}
|
|
496
524
|
});
|
|
497
525
|
}
|
|
498
526
|
|
|
@@ -507,21 +535,6 @@ function toggleAll(show) {
|
|
|
507
535
|
}
|
|
508
536
|
});
|
|
509
537
|
}
|
|
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
538
|
</script>
|
|
526
539
|
</body>
|
|
527
540
|
</html>
|
|
@@ -553,6 +566,7 @@ def compare(path: str):
|
|
|
553
566
|
<style>
|
|
554
567
|
html,body {{height:100%;margin:0;}}
|
|
555
568
|
</style>
|
|
569
|
+
<script src="/script.js"></script>
|
|
556
570
|
</head>
|
|
557
571
|
<body style="display:flex;flex-flow:row;">
|
|
558
572
|
<div style="display:flex;flex:1;flex-flow:column;margin:5px;">
|
|
@@ -561,6 +575,7 @@ html,body {{height:100%;margin:0;}}
|
|
|
561
575
|
</div>
|
|
562
576
|
<div style="display:flex;flex:0 0 50px;flex-flow:column;">
|
|
563
577
|
<a href="/image_diff/{path}" target="_blank">diff</a>
|
|
578
|
+
<button onclick="updateRef('{path}')">▶</button>
|
|
564
579
|
<img src="/image_diff/{path}" width="50" height="0" style="flex:1;">
|
|
565
580
|
</div>
|
|
566
581
|
<div style="display:flex;flex:1;flex-flow:column;margin:5px;">
|
|
@@ -616,6 +631,27 @@ def file(variant: str, path: str):
|
|
|
616
631
|
return send_from_directory(variant_root, path)
|
|
617
632
|
|
|
618
633
|
|
|
634
|
+
@app.route("/update_ref/<path:path>")
|
|
635
|
+
def update_ref(path: str):
|
|
636
|
+
logger.debug(f"Updating reference for path: {path}")
|
|
637
|
+
|
|
638
|
+
if not isinstance(path, str):
|
|
639
|
+
raise TypeError("Path must be a string")
|
|
640
|
+
|
|
641
|
+
src = Config.path_b / path
|
|
642
|
+
dst = Config.path_a / path
|
|
643
|
+
|
|
644
|
+
if not src.exists():
|
|
645
|
+
return f"Source file does not exist: {src}", 404
|
|
646
|
+
|
|
647
|
+
if src.is_file():
|
|
648
|
+
shutil.copy2(src, dst)
|
|
649
|
+
else:
|
|
650
|
+
shutil.copytree(src, dst, dirs_exist_ok=True)
|
|
651
|
+
|
|
652
|
+
return "Reference updated", 200
|
|
653
|
+
|
|
654
|
+
|
|
619
655
|
def verbosity_to_level(verbosity: int) -> int:
|
|
620
656
|
if verbosity >= 3:
|
|
621
657
|
return logging.DEBUG
|
|
@@ -659,27 +695,6 @@ def setup_logging(
|
|
|
659
695
|
root_logger.addHandler(file_handler)
|
|
660
696
|
|
|
661
697
|
|
|
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
698
|
def main():
|
|
684
699
|
parser = argparse.ArgumentParser()
|
|
685
700
|
parser.add_argument("ref", type=Path, help="Path to the reference directory")
|
|
@@ -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
|
|
|
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
|