Skip to content

Commit 650e5a9

Browse files
committed
link checker
1 parent 04d459d commit 650e5a9

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

linkcheck.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env python3
2+
3+
# This is free and unencumbered software released into the public
4+
# domain.
5+
6+
# Anyone is free to copy, modify, publish, use, compile, sell, or
7+
# distribute this software, either in source code form or as a
8+
# compiled binary, for any purpose, commercial or non-commercial, and
9+
# by any means.
10+
11+
# In jurisdictions that recognize copyright laws, the author or
12+
# authors of this software dedicate any and all copyright interest in
13+
# the software to the public domain. We make this dedication for the
14+
# benefit of the public at large and to the detriment of our heirs
15+
# and successors. We intend this dedication to be an overt act of
16+
# relinquishment in perpetuity of all present and future rights to
17+
# this software under copyright law.
18+
19+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
23+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24+
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
27+
# For more information, please refer to <http://unlicense.org>
28+
29+
"""Check for broken links."""
30+
31+
# The markdown files use posix-style paths, so we need posixpath for
32+
# processing them. See help('posixpath').
33+
import collections
34+
import os
35+
import posixpath
36+
import re
37+
38+
import common
39+
40+
41+
Link = collections.namedtuple('Link', 'text target markdown lineno file')
42+
43+
44+
def check_link(link):
45+
"""Check if the link's target exists.
46+
47+
Return an error message string or "ok".
48+
"""
49+
if link.target.startswith(('http://', 'https://')):
50+
# Checking for http(s) links can be added later, but currently
51+
# it's not needed.
52+
return "ok"
53+
path = posixpath.join(posixpath.dirname(link.file), link.target)
54+
realpath = path.replace('/', os.sep)
55+
if not os.path.exists(realpath):
56+
return "doesn't exist"
57+
if path.endswith('/'):
58+
# A directory.
59+
if os.path.isdir(realpath):
60+
return "ok"
61+
return "not a directory"
62+
else:
63+
# A file.
64+
if os.path.isfile(realpath):
65+
return "ok"
66+
return "not a file"
67+
68+
69+
def main():
70+
print("Searching links...")
71+
links = []
72+
for path in common.get_markdown_files():
73+
with open(path.replace('/', os.sep), 'r') as f:
74+
for match, lineno in common.find_links(f):
75+
target = match.group(2)
76+
if '#' in target:
77+
where = target.index('#')
78+
target = target[:where]
79+
link = Link(
80+
text=match.group(1),
81+
target=target,
82+
markdown=match.group(0),
83+
lineno=lineno,
84+
file=path)
85+
links.append(link)
86+
87+
print("Checking for broken links...")
88+
broken = [] # [(Link, check result), ...]
89+
for link in links:
90+
result = check_link(link)
91+
if result != "ok":
92+
broken.append((link, result))
93+
94+
if broken:
95+
print("\n*** %d/%d links seem to be broken! ***\n"
96+
% (len(broken), len(links)))
97+
for link, error in broken:
98+
print(" file {0.file}, line {0.lineno}: {1}"
99+
.format(link, error))
100+
print(" ", link.markdown.replace('\n', ' '))
101+
print()
102+
else:
103+
print("All", len(links), "links seem to be OK.")
104+
105+
106+
if __name__ == '__main__':
107+
main()

0 commit comments

Comments
 (0)