• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IIIF / presentation-validator / 23321119381

19 Mar 2026 11:04PM UTC coverage: 76.786% (+1.9%) from 74.85%
23321119381

Pull #199

github

glenrobson
Adding schema to package
Pull Request #199: Refactoring and using uv

418 of 509 new or added lines in 9 files covered. (82.12%)

4 existing lines in 1 file now uncovered.

688 of 896 relevant lines covered (76.79%)

3.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.83
/presentation_validator/cli.py
1
import argparse
4✔
2
import sys
4✔
3
from urllib.parse import urlparse
4✔
4
from presentation_validator.web import create_app
4✔
5
from presentation_validator.validator import check_manifest, fetch_manifest
4✔
6
from bottle import run
4✔
7
from pathlib import Path
4✔
8

9
import json
4✔
10
import requests
4✔
11

12
def is_url(value: str) -> bool:
4✔
NEW
13
    parsed = urlparse(value)
×
NEW
14
    return parsed.scheme in ("http", "https")
×
15

16
def load_input(source: str):
4✔
NEW
17
    warnings = []
×
NEW
18
    """Load JSON from a file path or URL."""
×
NEW
19
    if is_url(source):
×
NEW
20
        manifest, warnings = fetch_manifest(source, True, None)
×
NEW
21
        return manifest, warnings
×
22
    else:
NEW
23
        with open(source, "r", encoding="utf-8") as f:
×
NEW
24
            return json.load(f), warnings
×
25

26

27
def run_validate(args):
4✔
NEW
28
    try:
×
NEW
29
        data, warnings = load_input(args.source)
×
NEW
30
    except Exception as e:
×
NEW
31
        print(f"❌ Failed to load input: {e}", file=sys.stderr)
×
NEW
32
        sys.exit(1)
×
33

NEW
34
    version = args.version
×
35

NEW
36
    try:
×
NEW
37
        result = check_manifest(data, version, args.source, warnings)
×
NEW
38
    except Exception as e:
×
NEW
39
        print(f"❌ Validation error: {e}", file=sys.stderr)
×
NEW
40
        sys.exit(1)
×
41

42
    # Pretty print JSON result
NEW
43
    print(json.dumps(result, indent=2))
×
44

45
    # Optional: exit non-zero if invalid
NEW
46
    if isinstance(result, dict) and result.get("okay") is False:
×
NEW
47
        sys.exit(2)
×
48

49

50
def run_serve(args):
4✔
NEW
51
    app = create_app()
×
52

NEW
53
    run(
×
54
        app,
55
        host=args.host,
56
        port=args.port,
57
        debug=args.debug,
58
        reloader=args.reload,
59
    )
60

61
def run_validate_dir(args):
4✔
62
    # open up the directory
63
    base = Path(args.directory)
4✔
64

65
    if not base.exists() or not base.is_dir():
4✔
NEW
66
        print(f"Error: '{base}' is not a valid directory")
×
NEW
67
        return 1
×
68

69
    # find all files with the specified extension
70
    json_files = list(base.rglob(f"*{args.extension.replace('*','')}"))
4✔
71

72
    print(f"Found {len(json_files)} files with extension '{args.extension}'\n")
4✔
73

74
    # validate each file
75
    total = 0
4✔
76
    passed = 0
4✔
77
    failed = 0
4✔
78

79
    for path in json_files:
4✔
80
        total += 1
4✔
81
        print(f"Validating: {path}")
4✔
82

83
        try:
4✔
84
            with path.open("r", encoding="utf-8") as f:
4✔
85
                data = json.load(f)
4✔
86

87
            result = check_manifest(data, args.version)
4✔
88

89
            # print results
90
            if result.passed:
4✔
91
                passed += 1
4✔
92
                print("  ✓ OK\n")
4✔
93
            else:
94
                failed += 1
4✔
95
                if result.error:
4✔
NEW
96
                    print(f"  ✗ FAIL: {result.error}\n")
×
97
                else:
98
                    print("  ✗ FAIL\n")
4✔
99
                    for error in result.errorList:
4✔
100
                        print(str(error))
4✔
101

102
                for w in result.warnings:
4✔
NEW
103
                    print(f"    ⚠ {w}")
×
104

NEW
105
        except Exception as e:
×
NEW
106
            failed += 1
×
NEW
107
            print(f"  ✗ ERROR: {e}")
×
108

109
    # summary
110
    print("\n--- Summary ---")
4✔
111
    print(f"Total:  {total}")
4✔
112
    print(f"Passed: {passed}")
4✔
113
    print(f"Failed: {failed}")
4✔
114

115
    return 0 if failed == 0 else 1
4✔
116

117
def main():
4✔
NEW
118
    parser = argparse.ArgumentParser(
×
119
        prog="iiif-validator",
120
        description="IIIF Presentation Validator",
121
    )
122

NEW
123
    subparsers = parser.add_subparsers(dest="command", required=True)
×
124

125
    # ---- validate ----
NEW
126
    validate_parser = subparsers.add_parser(
×
127
        "validate", help="Validate a IIIF manifest from file or URL"
128
    )
NEW
129
    validate_parser.add_argument(
×
130
        "source",
131
        help="Path or URL to IIIF manifest JSON",
132
    )
NEW
133
    validate_parser.add_argument(
×
134
        "--version",
135
        help="IIIF Presentation version (e.g. 2.1, 3.0)",
136
        default=None,
137
    )
NEW
138
    validate_parser.set_defaults(func=run_validate)
×
139

140
    # ---- serve ----
NEW
141
    serve_parser = subparsers.add_parser(
×
142
        "serve", help="Run the validator web server"
143
    )
NEW
144
    serve_parser.add_argument("--host", default="127.0.0.1")
×
NEW
145
    serve_parser.add_argument("--port", type=int, default=8080)
×
NEW
146
    serve_parser.add_argument("--debug", action="store_true")
×
NEW
147
    serve_parser.add_argument("--reload", action="store_true")
×
NEW
148
    serve_parser.set_defaults(func=run_serve)
×
149

150
    # --- validate dir --- 
NEW
151
    dir_parser = subparsers.add_parser(
×
152
        "validate-dir",
153
        help="Validate all JSON manifests in a directory (recursively)",
154
    )
155

NEW
156
    dir_parser.add_argument(
×
157
        "directory",
158
        help="Directory to search for JSON files",
159
    )
160

NEW
161
    dir_parser.add_argument(
×
162
        "--version",
163
        help="IIIF Presentation version (e.g. 2.1, 3.0)",
164
        default=None,
165
    )
166

NEW
167
    dir_parser.add_argument(
×
168
        "--extension",
169
        default=".json",
170
        help="File extension to look for (default: .json)",
171
    )
172

NEW
173
    dir_parser.set_defaults(func=run_validate_dir)
×
174

NEW
175
    args = parser.parse_args()
×
NEW
176
    args.func(args)
×
177

178

179
if __name__ == "__main__":
4✔
NEW
180
    main()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc