Skip to content

Commit 8d19138

Browse files
committed
Make index publisher HTTP timeout configurable
1 parent 8184083 commit 8d19138

7 files changed

Lines changed: 36 additions & 2 deletions

File tree

docs/history/hatch.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
88

99
## Unreleased
1010

11+
***Added:***
12+
13+
- Allow configuring the HTTP timeout for the index publisher via the `timeout` config option, the `HATCH_INDEX_TIMEOUT` environment variable, or the `--timeout` CLI flag
14+
1115
## [1.16.5](https://github.com/pypa/hatch/releases/tag/hatch-v1.16.5) - 2026-02-26 ## {: #hatch-v1.16.5 }
1216

1317
***Fixed:***

docs/plugins/publisher/package-index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ See the documentation for [publishing](../../publish.md).
1414
| `--ca-cert` | `ca-cert` | The path to a CA bundle |
1515
| `--client-cert` | `client-cert` | The path to a client certificate, optionally containing the private key |
1616
| `--client-key` | `client-key` | The path to the client certificate's private key |
17+
| `--timeout` | `timeout` | The HTTP timeout in seconds for index requests |
1718
| | `repos` | A table of named [repositories](#repositories) to their respective options |
1819

1920
## Configuration

src/hatch/cli/publish/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@
3535
envvar=PublishEnvVars.CLIENT_KEY,
3636
help="The path to the client certificate's private key [env var: `HATCH_INDEX_CLIENT_KEY`]",
3737
)
38+
@click.option(
39+
"--timeout",
40+
envvar=PublishEnvVars.TIMEOUT,
41+
type=int,
42+
help="The HTTP timeout in seconds for index requests [env var: `HATCH_INDEX_TIMEOUT`]",
43+
)
3844
@click.option("--no-prompt", "-n", is_flag=True, help="Disable prompts, such as for missing required fields")
3945
@click.option(
4046
"--initialize-auth", is_flag=True, help="Save first-time authentication information even if nothing was published"
@@ -69,6 +75,7 @@ def publish(
6975
ca_cert,
7076
client_cert,
7177
client_key,
78+
timeout,
7279
no_prompt,
7380
initialize_auth,
7481
publisher_name,
@@ -93,6 +100,8 @@ def publish(
93100
option_map["client_cert"] = client_cert
94101
if client_key:
95102
option_map["client_key"] = client_key
103+
if timeout is not None:
104+
option_map["timeout"] = timeout
96105
else: # no cov
97106
for option in options:
98107
key, _, value = option.partition("=")

src/hatch/config/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class PublishEnvVars:
2626
CA_CERT = "HATCH_INDEX_CA_CERT"
2727
CLIENT_CERT = "HATCH_INDEX_CLIENT_CERT"
2828
CLIENT_KEY = "HATCH_INDEX_CLIENT_KEY"
29+
TIMEOUT = "HATCH_INDEX_TIMEOUT"
2930
PUBLISHER = "HATCH_PUBLISHER"
3031
OPTIONS = "HATCH_PUBLISHER_OPTIONS"
3132

src/hatch/index/core.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def __init__(self, repo: str):
3232

3333

3434
class PackageIndex:
35-
def __init__(self, repo: str, *, user="", auth="", ca_cert=None, client_cert=None, client_key=None):
35+
def __init__(self, repo: str, *, user="", auth="", ca_cert=None, client_cert=None, client_key=None, timeout=None):
3636
self.urls = IndexURLs(repo)
3737
self.repo = str(self.urls.repo)
3838
self.user = user
@@ -48,6 +48,8 @@ def __init__(self, repo: str, *, user="", auth="", ca_cert=None, client_cert=Non
4848
if ca_cert:
4949
self.__verify = ca_cert
5050

51+
self.__timeout = timeout
52+
5153
@cached_property
5254
def client(self) -> httpx.Client:
5355
import httpx
@@ -62,7 +64,7 @@ def client(self) -> httpx.Client:
6264
return httpx.Client(
6365
headers={"User-Agent": user_agent},
6466
transport=httpx.HTTPTransport(retries=3, verify=self.__verify, cert=self.__cert),
65-
timeout=DEFAULT_TIMEOUT,
67+
timeout=DEFAULT_TIMEOUT if self.__timeout is None else self.__timeout,
6668
)
6769

6870
def upload_artifact(self, artifact: Path, data: dict):

src/hatch/publish/index.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def publish(self, artifacts: list, options: dict):
7878
ca_cert=options.get("ca_cert", repo_config.get("ca-cert")),
7979
client_cert=options.get("client_cert", repo_config.get("client-cert")),
8080
client_key=options.get("client_key", repo_config.get("client-key")),
81+
timeout=options.get("timeout", repo_config.get("timeout")),
8182
)
8283

8384
existing_artifacts: dict[str, set[str]] = {}

tests/index/test_core.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,22 @@ def test_client_cert_with_key(self, mocker):
7373
mock.assert_called_once_with(verify=True, cert=("foo", "bar"), trust_env=True)
7474

7575

76+
class TestTimeout:
77+
def test_default(self):
78+
from hatch.utils.network import DEFAULT_TIMEOUT
79+
80+
index = PackageIndex("https://foo.internal/a/b/")
81+
client = index.client
82+
83+
assert client.timeout == httpx.Timeout(DEFAULT_TIMEOUT)
84+
85+
def test_explicit(self):
86+
index = PackageIndex("https://foo.internal/a/b/", timeout=42)
87+
client = index.client
88+
89+
assert client.timeout == httpx.Timeout(42)
90+
91+
7692
class TestUserAgent:
7793
def test_user_agent_header_format(self):
7894
index = PackageIndex("https://foo.internal/a/b/")

0 commit comments

Comments
 (0)