From cfc37900f56d6c86ff8e2aaae36ddb73a08294fa Mon Sep 17 00:00:00 2001 From: Jinen Setpal Date: Wed, 8 Jan 2025 13:43:15 -0400 Subject: [PATCH 1/5] made client requests tenacious, fixed incorrect function call --- dagshub/common/api/repo.py | 2 +- dagshub/ls_client.py | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/dagshub/common/api/repo.py b/dagshub/common/api/repo.py index 69b20253..75624a97 100644 --- a/dagshub/common/api/repo.py +++ b/dagshub/common/api/repo.py @@ -81,7 +81,7 @@ def __init__(self, repo: str, host: Optional[str] = None, auth: Optional[Any] = @retry(retry=retry_if_exception_type(LSInitializingError), wait=wait_fixed(3), stop=stop_after_attempt(5)) def _tenacious_ls_request(self, *args, **kwargs): - res = self.http_request(*args, **kwargs) + res = http_request(*args, **kwargs) if res.text.startswith(""): raise LSInitializingError() elif res.status_code // 100 != 2: diff --git a/dagshub/ls_client.py b/dagshub/ls_client.py index f6d5cbf6..085b5aaa 100644 --- a/dagshub/ls_client.py +++ b/dagshub/ls_client.py @@ -1,4 +1,5 @@ from tenacity import retry, wait_fixed, stop_after_attempt, retry_if_exception_type +from dagshub.data_engine.model.errors import LSInitializingError from json import JSONDecodeError from typing import Optional import importlib.util @@ -9,6 +10,20 @@ from dagshub.common import config +class _TenaciousLSCLientWrapper: + def __init__(self, func): + self.func = func + + @retry(retry=retry_if_exception_type(LSInitializingError), wait=wait_fixed(3), stop=stop_after_attempt(5)) + def wrapped_func(self, *args, **kwargs): + res = self.func(*args, **kwargs) + if res.text.startswith(""): + raise LSInitializingError() + elif res.status_code // 100 != 2: + raise RuntimeError(f"Process failed! Server Response: {res.text}") + return res + + def _use_legacy_client(): """ https://github.com/HumanSignal/label-studio/releases/tag/1.13.0, \ @@ -61,4 +76,11 @@ def get_label_studio_client( "api_key": token if token is not None else get_token(host=host), } - return LabelStudio(**kwargs) + ls_client = LabelStudio(**kwargs) + if legacy_client: + ls_client.make_request = _TenaciousLSCLientWrapper(ls_client.make_request).wrapped_func + else: + ls_client._client_wrapper.httpx_client.request = _TenaciousLSCLientWrapper(ls_client._client_wrapper.httpx_client.request).wrapped_func + ls_client._client_wrapper.httpx_client.stream = _TenaciousLSCLientWrapper(ls_client._client_wrapper.httpx_client.stream).wrapped_func + + return ls_client From 86c148037d3f0dde3621cd1dc442200e5ae065f2 Mon Sep 17 00:00:00 2001 From: Jinen Setpal Date: Wed, 8 Jan 2025 13:46:53 -0400 Subject: [PATCH 2/5] linter fixes --- dagshub/ls_client.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dagshub/ls_client.py b/dagshub/ls_client.py index 085b5aaa..ead771ff 100644 --- a/dagshub/ls_client.py +++ b/dagshub/ls_client.py @@ -80,7 +80,11 @@ def get_label_studio_client( if legacy_client: ls_client.make_request = _TenaciousLSCLientWrapper(ls_client.make_request).wrapped_func else: - ls_client._client_wrapper.httpx_client.request = _TenaciousLSCLientWrapper(ls_client._client_wrapper.httpx_client.request).wrapped_func - ls_client._client_wrapper.httpx_client.stream = _TenaciousLSCLientWrapper(ls_client._client_wrapper.httpx_client.stream).wrapped_func + ls_client._client_wrapper.httpx_client.request = _TenaciousLSCLientWrapper( + ls_client._client_wrapper.httpx_client.request + ).wrapped_func + ls_client._client_wrapper.httpx_client.stream = _TenaciousLSCLientWrapper( + ls_client._client_wrapper.httpx_client.stream + ).wrapped_func return ls_client From 4c83253544096ffbc04889c777aef5a0314cc33d Mon Sep 17 00:00:00 2001 From: Jinen Setpal Date: Wed, 8 Jan 2025 19:07:56 -0400 Subject: [PATCH 3/5] reworked wrapper to work with streamed requests Signed-off-by: Jinen Setpal --- dagshub/ls_client.py | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/dagshub/ls_client.py b/dagshub/ls_client.py index ead771ff..63e0ea7b 100644 --- a/dagshub/ls_client.py +++ b/dagshub/ls_client.py @@ -1,26 +1,48 @@ -from tenacity import retry, wait_fixed, stop_after_attempt, retry_if_exception_type +from tenacity import retry, wait_fixed, stop_after_attempt, retry_if_exception_type, Retrying from dagshub.data_engine.model.errors import LSInitializingError +from contextlib import _GeneratorContextManager +from dagshub.common.util import lazy_load from json import JSONDecodeError from typing import Optional +from itertools import tee import importlib.util import semver +import types from dagshub.common.api.repo import RepoAPI from dagshub.auth import get_token from dagshub.common import config +ls_sdk = lazy_load("label_studio_sdk") + + class _TenaciousLSCLientWrapper: def __init__(self, func): self.func = func - @retry(retry=retry_if_exception_type(LSInitializingError), wait=wait_fixed(3), stop=stop_after_attempt(5)) + @retry( + retry=retry_if_exception_type((LSInitializingError, JSONDecodeError, ls_sdk.core.ApiError)), + wait=wait_fixed(3), + stop=stop_after_attempt(5), + ) def wrapped_func(self, *args, **kwargs): res = self.func(*args, **kwargs) - if res.text.startswith(""): - raise LSInitializingError() - elif res.status_code // 100 != 2: - raise RuntimeError(f"Process failed! Server Response: {res.text}") + + if isinstance(res, types.GeneratorType): + proxy, res = tee(res) + if next(proxy).startswith(b""): + raise LSInitializingError() + elif isinstance(res, _GeneratorContextManager): + return res + elif isinstance(res, bytes): + if res.startswith(""): + raise LSInitializingError() + else: + if res.text.startswith(""): + raise LSInitializingError() + elif res.status_code // 100 != 2: + raise RuntimeError(f"Process failed! Server Response: {res.text}") return res @@ -83,8 +105,8 @@ def get_label_studio_client( ls_client._client_wrapper.httpx_client.request = _TenaciousLSCLientWrapper( ls_client._client_wrapper.httpx_client.request ).wrapped_func - ls_client._client_wrapper.httpx_client.stream = _TenaciousLSCLientWrapper( - ls_client._client_wrapper.httpx_client.stream + ls_client.projects.exports.create_export = _TenaciousLSCLientWrapper( + ls_client.projects.exports.create_export ).wrapped_func return ls_client From 26d0faa041a954edfbea466a46cf30434284f192 Mon Sep 17 00:00:00 2001 From: Jinen Setpal Date: Wed, 8 Jan 2025 19:10:15 -0400 Subject: [PATCH 4/5] removed needless import --- dagshub/ls_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dagshub/ls_client.py b/dagshub/ls_client.py index 63e0ea7b..9322645c 100644 --- a/dagshub/ls_client.py +++ b/dagshub/ls_client.py @@ -1,4 +1,4 @@ -from tenacity import retry, wait_fixed, stop_after_attempt, retry_if_exception_type, Retrying +from tenacity import retry, wait_fixed, stop_after_attempt, retry_if_exception_type from dagshub.data_engine.model.errors import LSInitializingError from contextlib import _GeneratorContextManager from dagshub.common.util import lazy_load From 5a1e5afe1b6849f9238052d5253a498969bcd8ee Mon Sep 17 00:00:00 2001 From: Jinen Setpal Date: Thu, 9 Jan 2025 08:39:40 -0400 Subject: [PATCH 5/5] added ls sdk to dev reqs for unit tests --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index 44a2d4d7..e2ea4b50 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,3 +7,4 @@ pytest-mock==3.14.0 fiftyone==0.23.8 datasets==2.19.1 ultralytics==8.3.47 +label-studio-sdk==1.0.8