Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions sphinx/builders/linkcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,15 +287,17 @@ def get_request_headers() -> dict[str, str]:

def check_uri() -> tuple[str, str, int]:
# split off anchor
if '#' in uri:
req_url, anchor = uri.split('#', 1)
for rex in self.anchors_ignore:
if rex.match(anchor):
anchor = None
break
uri_pattern = r"(.+/[^/]*)#([^/]*?/?$)"
match = re.search(uri_pattern, uri)
if match:
req_url, anchor = match.groups()
else:
req_url = uri
anchor = None
req_url, anchor = uri, None

for rex in self.anchors_ignore:
if anchor and rex.match(anchor):
anchor = None
break

# handle non-ASCII URIs
try:
Expand Down
48 changes: 47 additions & 1 deletion tests/test_build_linkcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@

import pytest

from sphinx.builders.linkcheck import HyperlinkAvailabilityCheckWorker, RateLimit
from sphinx.builders.linkcheck import (
CheckRequest,
Hyperlink,
HyperlinkAvailabilityCheckWorker,
RateLimit,
)
from sphinx.testing.util import strip_escseq
from sphinx.util.console import strip_colors

Expand Down Expand Up @@ -728,3 +733,44 @@ def test_linkcheck_exclude_documents(app):
'info': 'br0ken_link matched br[0-9]ken_link from linkcheck_exclude_documents',
},
]


urls = [
"https://www.example.com/path/to/resource#123",
"https://www.example.com/path/to/#/resource#123",
]


@pytest.mark.parametrize("test_url", urls)
def test_extract_anchor_from_url(app, test_url):
"""
Tests if anchor is correctly extracted from test_url, by asserting that
HyperlinkAvailabilityCheckWorker.run() calls linkcheck_anchors with the
correct anchor.
"""

with mock.patch("sphinx.builders.linkcheck.requests") as mock_requests:
mock_response = mock.Mock()
mock_response.raise_for_status.return_value = None

# requests.get() returns a context manager whose __enter__ method needs
# to be mocked.
mock_requests.get.return_value.__enter__.return_value.name = mock_response

with mock.patch(
"sphinx.builders.linkcheck.check_anchor",
return_value=True,
) as mock_check_anchor:
worker = HyperlinkAvailabilityCheckWorker(
app.env,
app.config,
Queue(),
Queue(2),
{},
)
worker.config.linkcheck_anchors = True
worker.wqueue.put_nowait(CheckRequest(0, Hyperlink(test_url, "", 0)))
worker.wqueue.put_nowait(CheckRequest(0, None))

worker.run()
mock_check_anchor.assert_called_with(mock.ANY, "123")