2626
2727# For more information, please refer to <http://unlicense.org>
2828
29- """Check for broken links."""
29+ """Check for broken links.
30+
31+ This finds links like this...
32+
33+ [click here](some-file.md)
34+ [or here](../some/path/another-file.md)
35+ 
36+
37+ ...but not like this:
38+
39+ [some website](http://github.com/)
40+ [another website](https://github.com/)
41+ [local header](#some-header)
42+ """
3043
3144# The markdown files use posix-style paths, so we need posixpath for
3245# processing them. See help('posixpath').
3851import common
3952
4053
41- Link = collections . namedtuple ( ' Link' , 'text target markdown lineno file' )
54+ class Link :
4255
56+ def __init__ (self , regexmatch , filepath , lineno ):
57+ # The .group(0) is not perfect, but it's good enough.
58+ self .markdown = regexmatch .group (0 )
59+ self .text = regexmatch .group (1 )
60+ self .target = regexmatch .group (2 )
61+ self .filepath = filepath
62+ self .lineno = lineno
63+ self .status = None
4364
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+ def _get_status (self ):
66+ if self .target .startswith (('http://' , 'https://' )):
67+ # Checking for http(s) links can be added later, but
68+ # currently it's not needed.
6569 return "ok"
66- return "not a file"
70+
71+ target = self .target
72+ if '#' in target :
73+ where = target .index ('#' )
74+ if where == 0 :
75+ # It's a link to a title in the same file, we need to
76+ # skip it.
77+ return "ok"
78+ target = target [:where ]
79+
80+ path = posixpath .join (posixpath .dirname (self .filepath ), target )
81+ realpath = path .replace ('/' , os .sep )
82+
83+ if not os .path .exists (realpath ):
84+ return "doesn't exist"
85+ if target .endswith ('/' ):
86+ # A directory.
87+ if os .path .isdir (directory ):
88+ return "ok"
89+ return "not a directory"
90+ else :
91+ # A file.
92+ if os .path .isfile (realpath ):
93+ return "ok"
94+ return "not a file"
95+
96+ def check (self ):
97+ """Check if the link's target is like it should be.
98+
99+ Return an error message string or "ok". The return value is also
100+ assigned to the status attribute.
101+ """
102+ self .status = self ._get_status ()
103+ return self .status
104+
105+ def print_status (self ):
106+ print (" file {0.filepath}, line {0.lineno}: {0.status}" .format (self ))
107+ print (" " + self .markdown )
108+ print ()
67109
68110
69111def main ():
@@ -72,35 +114,22 @@ def main():
72114 for path in common .get_markdown_files ():
73115 with open (path .replace ('/' , os .sep ), 'r' ) as f :
74116 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 )
117+ links .append (Link (match , path , lineno ))
118+ print (" found" , len (links ), "links" )
86119
87120 print ("Checking for broken links..." )
88- broken = [] # [(Link, check result), ...]
121+ brokens = 0
89122 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 ()
123+ if link .check () != "ok" :
124+ link .print_status ()
125+ brokens += 1
126+
127+ if brokens == 0 :
128+ print ("All links seem to be OK." )
129+ elif brokens == 1 :
130+ print ("1 link is broken!" )
102131 else :
103- print ("All" , len ( links ), "links seem to be OK. " )
132+ print (brokens , "links are broken! " )
104133
105134
106135if __name__ == '__main__' :
0 commit comments