mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
linkcheck: add a distinct 'timeout' reporting status (#11876)
This commit is contained in:
parent
85400a0430
commit
9e198c70fd
@ -57,6 +57,8 @@ Bugs fixed
|
||||
Set this option to ``False`` to report HTTP 401 (unauthorized) server
|
||||
responses as broken.
|
||||
Patch by James Addison.
|
||||
* #11868: linkcheck: added a distinct ``timeout`` reporting status code.
|
||||
Patch by James Addison.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
@ -17,6 +17,7 @@ from urllib.parse import unquote, urlparse, urlsplit, urlunparse
|
||||
|
||||
from docutils import nodes
|
||||
from requests.exceptions import ConnectionError, HTTPError, SSLError, TooManyRedirects
|
||||
from requests.exceptions import Timeout as RequestTimeout
|
||||
|
||||
from sphinx.builders.dummy import DummyBuilder
|
||||
from sphinx.deprecation import RemovedInSphinx80Warning
|
||||
@ -64,6 +65,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
||||
|
||||
def init(self) -> None:
|
||||
self.broken_hyperlinks = 0
|
||||
self.timed_out_hyperlinks = 0
|
||||
self.hyperlinks: dict[str, Hyperlink] = {}
|
||||
# set a timeout for non-responding servers
|
||||
socket.setdefaulttimeout(5.0)
|
||||
@ -88,7 +90,7 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
||||
for result in checker.check(self.hyperlinks):
|
||||
self.process_result(result)
|
||||
|
||||
if self.broken_hyperlinks:
|
||||
if self.broken_hyperlinks or self.timed_out_hyperlinks:
|
||||
self.app.statuscode = 1
|
||||
|
||||
def process_result(self, result: CheckResult) -> None:
|
||||
@ -115,6 +117,15 @@ class CheckExternalLinksBuilder(DummyBuilder):
|
||||
self.write_entry('local', result.docname, filename, result.lineno, result.uri)
|
||||
elif result.status == 'working':
|
||||
logger.info(darkgreen('ok ') + result.uri + result.message)
|
||||
elif result.status == 'timeout':
|
||||
if self.app.quiet or self.app.warningiserror:
|
||||
logger.warning('timeout ' + result.uri + result.message,
|
||||
location=(result.docname, result.lineno))
|
||||
else:
|
||||
logger.info(red('timeout ') + result.uri + red(' - ' + result.message))
|
||||
self.write_entry('timeout', result.docname, filename, result.lineno,
|
||||
result.uri + ': ' + result.message)
|
||||
self.timed_out_hyperlinks += 1
|
||||
elif result.status == 'broken':
|
||||
if self.app.quiet or self.app.warningiserror:
|
||||
logger.warning(__('broken link: %s (%s)'), result.uri, result.message,
|
||||
@ -436,6 +447,9 @@ class HyperlinkAvailabilityCheckWorker(Thread):
|
||||
del response
|
||||
break
|
||||
|
||||
except RequestTimeout as err:
|
||||
return 'timeout', str(err), 0
|
||||
|
||||
except SSLError as err:
|
||||
# SSL failure; report that the link is broken.
|
||||
return 'broken', str(err), 0
|
||||
|
@ -854,6 +854,27 @@ def test_too_many_requests_retry_after_without_header(app, capsys):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver', freshenv=True)
|
||||
def test_requests_timeout(app):
|
||||
class DelayedResponseHandler(http.server.BaseHTTPRequestHandler):
|
||||
protocol_version = "HTTP/1.1"
|
||||
|
||||
def do_GET(self):
|
||||
time.sleep(0.2) # wait before sending any response data
|
||||
self.send_response(200, "OK")
|
||||
self.send_header("Content-Length", "0")
|
||||
self.end_headers()
|
||||
|
||||
app.config.linkcheck_timeout = 0.01
|
||||
with http_server(DelayedResponseHandler):
|
||||
app.build()
|
||||
|
||||
with open(app.outdir / "output.json", encoding="utf-8") as fp:
|
||||
content = json.load(fp)
|
||||
|
||||
assert content["status"] == "timeout"
|
||||
|
||||
|
||||
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver', freshenv=True)
|
||||
def test_too_many_requests_user_timeout(app):
|
||||
app.config.linkcheck_rate_limit_timeout = 0.0
|
||||
|
Loading…
Reference in New Issue
Block a user