From 805861066fe7be69adff95e9fcdc0d691c3f8c54 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 6 Feb 2019 00:04:51 +0900 Subject: [PATCH 1/2] Add a helper function; get_node_line() --- sphinx/builders/linkcheck.py | 8 ++------ sphinx/util/nodes.py | 8 ++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 95892dbaf..230a573d2 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -25,7 +25,7 @@ from sphinx.util import encode_uri, requests, logging from sphinx.util.console import ( # type: ignore purple, red, darkgreen, darkgray, darkred, turquoise ) -from sphinx.util.nodes import traverse_parent +from sphinx.util.nodes import get_node_line from sphinx.util.requests import is_ssl_error if False: @@ -275,11 +275,7 @@ class CheckExternalLinksBuilder(Builder): if 'refuri' not in node: continue uri = node['refuri'] - lineno = None - for parent in traverse_parent(node): - if parent.line: - lineno = parent.line - break + lineno = get_node_line(node) self.wqueue.put((uri, docname, lineno), False) n += 1 done = 0 diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 484616b03..7870438f7 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -285,6 +285,14 @@ def find_source_node(node): return None +def get_node_line(node): + # type: (nodes.Element) -> int + for pnode in traverse_parent(node): + if pnode.line: + return pnode.line + return None + + def traverse_parent(node, cls=None): # type: (nodes.Element, Any) -> Iterable[nodes.Element] while node: From f551915e9fdbb8dc5a7df0e1769f539f61af215d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 6 Feb 2019 00:21:52 +0900 Subject: [PATCH 2/2] Close #5196: linkcheck also checks remote images exist --- CHANGES | 1 + sphinx/builders/linkcheck.py | 19 +++++++++++++++---- tests/roots/test-linkcheck/links.txt | 3 +++ tests/test_build_linkcheck.py | 9 +++++++-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 9eb401173..7a9ece74b 100644 --- a/CHANGES +++ b/CHANGES @@ -167,6 +167,7 @@ Features added * #1341 the HTML search considers words that contain a search term of length three or longer a match. * #4611: epub: Show warning for duplicated ToC entries +* #5196: linkcheck also checks remote images exist Bugs fixed ---------- diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 230a573d2..1161edb9d 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -271,13 +271,24 @@ class CheckExternalLinksBuilder(Builder): # type: (str, nodes.Node) -> None logger.info('') n = 0 - for node in doctree.traverse(nodes.reference): - if 'refuri' not in node: + + # reference nodes + for refnode in doctree.traverse(nodes.reference): + if 'refuri' not in refnode: continue - uri = node['refuri'] - lineno = get_node_line(node) + uri = refnode['refuri'] + lineno = get_node_line(refnode) self.wqueue.put((uri, docname, lineno), False) n += 1 + + # image nodes + for imgnode in doctree.traverse(nodes.image): + uri = imgnode['candidates'].get('?') + if uri and '://' in uri: + lineno = get_node_line(imgnode) + self.wqueue.put((uri, docname, lineno), False) + n += 1 + done = 0 while done < n: self.process_result(self.rqueue.get()) diff --git a/tests/roots/test-linkcheck/links.txt b/tests/roots/test-linkcheck/links.txt index d5057e08e..ac5ed3246 100644 --- a/tests/roots/test-linkcheck/links.txt +++ b/tests/roots/test-linkcheck/links.txt @@ -11,3 +11,6 @@ Some additional anchors to exercise ignore code * `Example Bar invalid `_ * `Example anchor invalid `_ * `Complete nonsense `_ + +.. image:: http://example.com/image.png +.. figure:: http://example.com/image2.png diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py index 6e50bd758..6d25058eb 100644 --- a/tests/test_build_linkcheck.py +++ b/tests/test_build_linkcheck.py @@ -24,7 +24,10 @@ def test_defaults(app, status, warning): assert "Anchor 'does-not-exist' not found" in content # looking for non-existent URL should fail assert " Max retries exceeded with url: /doesnotexist" in content - assert len(content.splitlines()) == 3 + # images should fail + assert "Not Found for url: http://example.com/image.png" in content + assert "Not Found for url: http://example.com/image2.png" in content + assert len(content.splitlines()) == 5 @pytest.mark.sphinx( @@ -32,7 +35,9 @@ def test_defaults(app, status, warning): confoverrides={'linkcheck_anchors_ignore': ["^!", "^top$"], 'linkcheck_ignore': [ 'https://localhost:7777/doesnotexist', - 'http://www.sphinx-doc.org/en/1.7/intro.html#'] + 'http://www.sphinx-doc.org/en/1.7/intro.html#', + 'http://example.com/image.png', + 'http://example.com/image2.png'] }) def test_anchors_ignored(app, status, warning): app.builder.build_all()