Skip to content
Merged
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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ CVAT_USERNAME=username
CVAT_PASSWORD=password
```

By default the client connects to `https://app.cvat.ai`. To use a self-hosted
server, set `CVAT_HOST` (a bare host is fine — `https://` is assumed):

```bash
CVAT_HOST=https://cvat.nextml.com
```

You can also pass it directly: `next_cvat.Client(host="https://cvat.nextml.com", ...)`.

If you don't want to use your username and password, you can create a token:

```bash
Expand Down
27 changes: 24 additions & 3 deletions next_cvat/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from cvat_sdk import Client as CVATClient
from cvat_sdk import make_client
from pydantic import BaseModel
from pydantic import BaseModel, field_validator

from next_cvat.access_token import AccessToken
from next_cvat.settings import settings
Expand All @@ -16,11 +16,32 @@
from .project import Project
from .task import Task

#: Default CVAT server. Override via the ``CVAT_HOST`` environment variable
#: (e.g. ``CVAT_HOST=https://cvat.nextml.com``) or the ``host=`` argument.
DEFAULT_CVAT_HOST = "https://app.cvat.ai"


class Client(BaseModel):
username: str | None = None
password: str | None = None
token: str | None = None
host: str = DEFAULT_CVAT_HOST

@field_validator("host", mode="before")
@classmethod
def _normalize_host(cls, value: str | None) -> str:
"""Default empty hosts and ensure an explicit scheme is present.

Passing a scheme (``https://`` / ``http://``) lets the CVAT SDK skip its
flaky HTTPS/HTTP auto-detection, which otherwise times out and raises
``InvalidHostException`` when the server is slow to answer.
"""
if value is None or value == "":
return DEFAULT_CVAT_HOST
value = value.rstrip("/")
if not value.startswith(("http://", "https://")):
value = f"https://{value}"
return value

@classmethod
def from_env(cls, env_prefix: str | None = None) -> Client:
Expand Down Expand Up @@ -52,14 +73,14 @@ def cvat_client(self) -> Generator[CVATClient, Any, Any]:
@contextmanager
def basic_cvat_client(self) -> Generator[CVATClient, None, None]:
with make_client(
host="app.cvat.ai", credentials=(self.username, self.password)
host=self.host, credentials=(self.username, self.password)
) as client:
client.login((self.username, self.password))
yield client

@contextmanager
def token_cvat_client(self) -> Generator[CVATClient, None, None]:
with make_client(host="app.cvat.ai") as client:
with make_client(host=self.host) as client:
token = AccessToken.deserialize(self.token)

# Only set Authorization header if we have a real API key (not session-based)
Expand Down
1 change: 1 addition & 0 deletions next_cvat/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ class Settings(
username: str | None = None
password: str | None = None
token: str | None = None
host: str | None = None

return Settings()
Loading