Repository: sphinx-doc/sphinx

Issue:
Strict type of `linkcheck_rate_limit_timeout`
### Describe the bug

The [documentation](https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-linkcheck_rate_limit_timeout) for `linkcheck_rate_limit_timeout` says: “By default, the timeout is 300 seconds and custom timeouts should be given in seconds”, without mentioning its type (although “300” looks like `int`). However, setting something like
```python
linkcheck_rate_limit_timeout = 60
```
in `conf.py` results in the following warning:
```
WARNING: The config value `linkcheck_rate_limit_timeout' has type `int', defaults to `float'.
```
I guess, instead of issuing a warning, an `int` should be silently converted to `float`. Or the documentation must state that a `float` is required.
(By the way, there also must be a comma: “...seconds<b>,</b> and custom...”, or better would be to split this into separate sentences.)

### How to Reproduce

Add
```python
linkcheck_rate_limit_timeout = 60
```
to `conf.py`.


### Environment Information

```text
Platform:              linux; (Linux-6.1.0-9-amd64-x86_64-with-glibc2.38)
Python version:        3.13.0b2 (main, Jun 11 2024, 14:14:35) [GCC 13.2.0])
Python implementation: CPython
Sphinx version:        7.3.7
Docutils version:      0.20.1
Jinja2 version:        3.1.4
Pygments version:      2.18.0
```


### Sphinx extensions

_No response_

### Additional context

_No response_

Discussion:

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 0a31a6746..be3293da1 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -58,6 +58,9 @@ Bugs fixed
   Patch by Bénédikt Tran.
 * #12220: Fix loading custom template translations for ``en`` locale.
   Patch by Nicolas Peugnet.
+* #12459: Add valid-type arguments to the ``linkcheck_rate_limit_timeout``
+  configuration setting.
+  Patch by James Addison.
 
 Improvements
 ------------
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index a27107f86..795dc42b1 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -2950,8 +2950,8 @@ Options for the linkcheck builder
 
    Otherwise, ``linkcheck`` waits for a minute before to retry and keeps
    doubling the wait time between attempts until it succeeds or exceeds the
-   ``linkcheck_rate_limit_timeout``. By default, the timeout is 300 seconds
-   and custom timeouts should be given in seconds.
+   ``linkcheck_rate_limit_timeout``. By default, the timeout is 300 seconds.
+   Custom timeouts should be given as a number of seconds.
 
    .. _Retry-After: https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.3
 
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index 42be58974..266c964ae 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -708,7 +708,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
     # commonly used for dynamic pages
     app.add_config_value('linkcheck_anchors_ignore', ['^!'], '')
     app.add_config_value('linkcheck_anchors_ignore_for_url', (), '', (tuple, list))
-    app.add_config_value('linkcheck_rate_limit_timeout', 300.0, '')
+    app.add_config_value('linkcheck_rate_limit_timeout', 300.0, '', (int, float))
     app.add_config_value('linkcheck_allow_unauthorized', True, '')
     app.add_config_value('linkcheck_report_timeouts_as_broken', True, '', bool)
 


Test patch:
diff --git a/tests/test_builders/test_build_linkcheck.py b/tests/test_builders/test_build_linkcheck.py
index 3d048c774..171988000 100644
--- a/tests/test_builders/test_build_linkcheck.py
+++ b/tests/test_builders/test_build_linkcheck.py
@@ -936,21 +936,23 @@ def test_limit_rate_doubles_previous_wait_time(app: Sphinx) -> None:
     assert next_check == 120.0
 
 
-@pytest.mark.sphinx(confoverrides={'linkcheck_rate_limit_timeout': 90.0})
-def test_limit_rate_clips_wait_time_to_max_time(app: Sphinx) -> None:
+@pytest.mark.sphinx(confoverrides={'linkcheck_rate_limit_timeout': 90})
+def test_limit_rate_clips_wait_time_to_max_time(app: Sphinx, warning: StringIO) -> None:
     rate_limits = {"localhost": RateLimit(60.0, 0.0)}
     worker = HyperlinkAvailabilityCheckWorker(app.config, Queue(), Queue(), rate_limits)
     with mock.patch('time.time', return_value=0.0):
         next_check = worker.limit_rate(FakeResponse.url, FakeResponse.headers.get("Retry-After"))
     assert next_check == 90.0
+    assert warning.getvalue() == ''
 
 
 @pytest.mark.sphinx(confoverrides={'linkcheck_rate_limit_timeout': 90.0})
-def test_limit_rate_bails_out_after_waiting_max_time(app: Sphinx) -> None:
+def test_limit_rate_bails_out_after_waiting_max_time(app: Sphinx, warning: StringIO) -> None:
     rate_limits = {"localhost": RateLimit(90.0, 0.0)}
     worker = HyperlinkAvailabilityCheckWorker(app.config, Queue(), Queue(), rate_limits)
     next_check = worker.limit_rate(FakeResponse.url, FakeResponse.headers.get("Retry-After"))
     assert next_check is None
+    assert warning.getvalue() == ''
 
 
 @mock.patch('sphinx.util.requests.requests.Session.get_adapter')