outo-models-cli 0.2.2__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/PKG-INFO +1 -1
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/pyproject.toml +1 -1
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/api/lfs_batch.py +2 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/api/lfs_upload.py +2 -1
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/api/upload.py +2 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/errors.py +12 -4
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/http.py +13 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_errors.py +23 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/uv.lock +1 -1
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/.gitignore +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/LICENSE +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/README.md +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/__init__.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/__main__.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/api/__init__.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/api/auth.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/api/lfs.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/api/repos.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/commands/__init__.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/commands/_shared.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/commands/_upload_commit.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/commands/auth.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/commands/download.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/commands/ls.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/commands/repo.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/commands/upload.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/config.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/downloader/__init__.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/downloader/transfer.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/downloader/walk.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/main.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/matchers.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/conftest.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_api_crud.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_api_upload.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_cli_auth.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_cli_errors.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_cli_help.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_cli_repo.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_cli_upload_lfs.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_cli_xfer.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_config.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_download_lfs.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_download_repo.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_download_transfer.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_download_walk.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_lfs.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_lfs_upload.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_matchers.py +0 -0
- {outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/tests/test_upload_commit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: outo-models-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Command-line client for self-hosted outo-models servers — auth, repo, download, and upload.
|
|
5
5
|
Project-URL: Homepage, https://github.com/outo-models/outo-models
|
|
6
6
|
Project-URL: Documentation, https://github.com/outo-models/outo-models/blob/main/omc/README.md
|
|
@@ -23,6 +23,7 @@ import httpx
|
|
|
23
23
|
from outo_models_cli.api import send, unwrap
|
|
24
24
|
from outo_models_cli.api.lfs import LFS_CONTENT_TYPE
|
|
25
25
|
from outo_models_cli.errors import BadResponseError, map_response_error
|
|
26
|
+
from outo_models_cli.http import TIMEOUT_BATCH
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
@dataclass(frozen=True, slots=True)
|
|
@@ -72,6 +73,7 @@ def batch_upload(
|
|
|
72
73
|
f"/{owner}/{name}.git/info/lfs/objects/batch",
|
|
73
74
|
headers={"Accept": LFS_CONTENT_TYPE, "Content-Type": LFS_CONTENT_TYPE},
|
|
74
75
|
json=body,
|
|
76
|
+
timeout=TIMEOUT_BATCH,
|
|
75
77
|
)
|
|
76
78
|
if response.status_code == 404:
|
|
77
79
|
raise BadResponseError(
|
|
@@ -37,6 +37,7 @@ from outo_models_cli.errors import (
|
|
|
37
37
|
FileTooLargeError,
|
|
38
38
|
map_response_error,
|
|
39
39
|
)
|
|
40
|
+
from outo_models_cli.http import TIMEOUT_PUT
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
class _ProgressLike(Protocol):
|
|
@@ -88,7 +89,7 @@ def _put_stream(
|
|
|
88
89
|
progress.update(task_id, advance=len(chunk))
|
|
89
90
|
yield chunk
|
|
90
91
|
|
|
91
|
-
response = client.put(url, content=_chunk_iter(), headers=headers)
|
|
92
|
+
response = client.put(url, content=_chunk_iter(), headers=headers, timeout=TIMEOUT_PUT)
|
|
92
93
|
if response.status_code == 404:
|
|
93
94
|
raise BadResponseError("LFS object endpoint returned 404.")
|
|
94
95
|
if response.status_code == 401:
|
|
@@ -15,6 +15,7 @@ from outo_models_cli.api import (
|
|
|
15
15
|
unwrap,
|
|
16
16
|
)
|
|
17
17
|
from outo_models_cli.errors import BadResponseError, map_response_error
|
|
18
|
+
from outo_models_cli.http import TIMEOUT_COMMIT
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def upload(
|
|
@@ -84,6 +85,7 @@ def upload(
|
|
|
84
85
|
f"/api/repos/{owner}/{name}/upload",
|
|
85
86
|
files=multipart_files,
|
|
86
87
|
data=fields,
|
|
88
|
+
timeout=TIMEOUT_COMMIT,
|
|
87
89
|
)
|
|
88
90
|
if response.status_code >= 400:
|
|
89
91
|
raise map_response_error(response)
|
|
@@ -130,12 +130,20 @@ def map_response_error(response: httpx.Response, *, fallback: str | None = None)
|
|
|
130
130
|
|
|
131
131
|
|
|
132
132
|
def map_transport_error(exc: httpx.HTTPError) -> OmcError:
|
|
133
|
-
"""Wrap an `httpx.HTTPError` as
|
|
133
|
+
"""Wrap an `httpx.HTTPError` as a stable, actionable CLI error.
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
Timeouts get their own message (field failure: a slow-but-working
|
|
136
|
+
server was reported as "cannot reach", sending the operator hunting
|
|
137
|
+
for outages). A timeout often means the server is STILL processing —
|
|
138
|
+
retrying a commit can duplicate work, so tell the user to check
|
|
139
|
+
first.
|
|
138
140
|
"""
|
|
141
|
+
if isinstance(exc, httpx.TimeoutException):
|
|
142
|
+
return ServerUnreachableError(
|
|
143
|
+
"The server did not respond in time. It may still be processing — "
|
|
144
|
+
"verify with `omc ls` before retrying, then use a longer timeout "
|
|
145
|
+
"if it keeps happening.",
|
|
146
|
+
)
|
|
139
147
|
return ServerUnreachableError(
|
|
140
148
|
"Cannot reach the server. Check the URL, network, and that the server is running.",
|
|
141
149
|
)
|
|
@@ -19,6 +19,16 @@ import httpx
|
|
|
19
19
|
# the connection for a few seconds. Anything longer is genuinely hung.
|
|
20
20
|
_TIMEOUT = httpx.Timeout(30.0, connect=5.0)
|
|
21
21
|
|
|
22
|
+
# Long operations need long read timeouts. A multipart commit after a
|
|
23
|
+
# multi-hundred-GiB LFS session does real server-side work (worktree
|
|
24
|
+
# clone, fetch, quota, audit) that can exceed 30 s — the field failure
|
|
25
|
+
# surfaced as "Cannot reach the server" while the server was alive and
|
|
26
|
+
# working. Writes stay unlimited: the transport buffers, so a slow disk
|
|
27
|
+
# on either side must not abort a 3 GiB PUT mid-stream.
|
|
28
|
+
TIMEOUT_BATCH = httpx.Timeout(120.0, connect=5.0)
|
|
29
|
+
TIMEOUT_COMMIT = httpx.Timeout(600.0, connect=5.0)
|
|
30
|
+
TIMEOUT_PUT = httpx.Timeout(None, connect=10.0, read=900.0)
|
|
31
|
+
|
|
22
32
|
|
|
23
33
|
def build_client(
|
|
24
34
|
base_url: str,
|
|
@@ -138,6 +148,9 @@ def build_basic_async_client(
|
|
|
138
148
|
|
|
139
149
|
|
|
140
150
|
__all__ = [
|
|
151
|
+
"TIMEOUT_BATCH",
|
|
152
|
+
"TIMEOUT_COMMIT",
|
|
153
|
+
"TIMEOUT_PUT",
|
|
141
154
|
"basic_auth_header",
|
|
142
155
|
"build_async_client",
|
|
143
156
|
"build_basic_async_client",
|
|
@@ -105,3 +105,26 @@ def test_every_omc_error_has_stable_code() -> None:
|
|
|
105
105
|
assert ValidationFailedError.code == "validation_failed"
|
|
106
106
|
assert ServerUnreachableError.code == "server_unreachable"
|
|
107
107
|
assert BadResponseError.code == "bad_response"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class TestTimeoutMessageSplit:
|
|
111
|
+
"""A working-but-slow server is NOT 'unreachable' (field failure)."""
|
|
112
|
+
|
|
113
|
+
def test_timeout_gets_its_own_message(self):
|
|
114
|
+
import httpx
|
|
115
|
+
|
|
116
|
+
from outo_models_cli.errors import map_transport_error
|
|
117
|
+
|
|
118
|
+
exc = httpx.ReadTimeout("took too long")
|
|
119
|
+
err = map_transport_error(exc)
|
|
120
|
+
assert "did not respond in time" in str(err)
|
|
121
|
+
assert "Cannot reach" not in str(err)
|
|
122
|
+
|
|
123
|
+
def test_connect_error_keeps_unreachable_message(self):
|
|
124
|
+
import httpx
|
|
125
|
+
|
|
126
|
+
from outo_models_cli.errors import map_transport_error
|
|
127
|
+
|
|
128
|
+
exc = httpx.ConnectError("refused")
|
|
129
|
+
err = map_transport_error(exc)
|
|
130
|
+
assert "Cannot reach the server" in str(err)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{outo_models_cli-0.2.2 → outo_models_cli-0.2.3}/src/outo_models_cli/commands/_upload_commit.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|