cmsdials 0.0.0__py3-none-any.whl

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.

Potentially problematic release.


This version of cmsdials might be problematic. Click here for more details.

Files changed (52) hide show
  1. LICENSE +674 -0
  2. cmsdials/__init__.py +5 -0
  3. cmsdials/_version.py +1 -0
  4. cmsdials/auth/__init__.py +0 -0
  5. cmsdials/auth/_base.py +136 -0
  6. cmsdials/auth/bearer.py +140 -0
  7. cmsdials/auth/client.py +105 -0
  8. cmsdials/auth/exceptions.py +6 -0
  9. cmsdials/auth/models.py +25 -0
  10. cmsdials/auth/secret_key.py +42 -0
  11. cmsdials/clients/__init__.py +0 -0
  12. cmsdials/clients/dataset_index/__init__.py +0 -0
  13. cmsdials/clients/dataset_index/client.py +13 -0
  14. cmsdials/clients/dataset_index/models.py +30 -0
  15. cmsdials/clients/file_index/__init__.py +0 -0
  16. cmsdials/clients/file_index/client.py +13 -0
  17. cmsdials/clients/file_index/models.py +36 -0
  18. cmsdials/clients/h1d/__init__.py +0 -0
  19. cmsdials/clients/h1d/client.py +13 -0
  20. cmsdials/clients/h1d/models.py +47 -0
  21. cmsdials/clients/h2d/__init__.py +0 -0
  22. cmsdials/clients/h2d/client.py +13 -0
  23. cmsdials/clients/h2d/models.py +50 -0
  24. cmsdials/clients/lumisection/__init__.py +0 -0
  25. cmsdials/clients/lumisection/client.py +13 -0
  26. cmsdials/clients/lumisection/models.py +34 -0
  27. cmsdials/clients/mes/__init__.py +0 -0
  28. cmsdials/clients/mes/client.py +12 -0
  29. cmsdials/clients/mes/models.py +18 -0
  30. cmsdials/clients/ml_bad_lumisection/__init__.py +0 -0
  31. cmsdials/clients/ml_bad_lumisection/client.py +29 -0
  32. cmsdials/clients/ml_bad_lumisection/models.py +38 -0
  33. cmsdials/clients/ml_models_index/__init__.py +0 -0
  34. cmsdials/clients/ml_models_index/client.py +13 -0
  35. cmsdials/clients/ml_models_index/models.py +30 -0
  36. cmsdials/clients/oms_proxy/__init__.py +0 -0
  37. cmsdials/clients/oms_proxy/client.py +49 -0
  38. cmsdials/clients/oms_proxy/models.py +14 -0
  39. cmsdials/clients/run/__init__.py +0 -0
  40. cmsdials/clients/run/client.py +13 -0
  41. cmsdials/clients/run/models.py +29 -0
  42. cmsdials/cmsdials.py +27 -0
  43. cmsdials/filters.py +25 -0
  44. cmsdials/utils/__init__.py +0 -0
  45. cmsdials/utils/_json.py +24 -0
  46. cmsdials/utils/api_client.py +226 -0
  47. cmsdials/utils/base_model.py +33 -0
  48. cmsdials/utils/logger.py +6 -0
  49. cmsdials-0.0.0.dist-info/LICENSE +674 -0
  50. cmsdials-0.0.0.dist-info/METADATA +356 -0
  51. cmsdials-0.0.0.dist-info/RECORD +52 -0
  52. cmsdials-0.0.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,50 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import AnyUrl, BaseModel, Field
4
+
5
+ from ...utils.base_model import OBaseModel, PaginatedBaseModel
6
+
7
+
8
+ class LumisectionHistogram2D(BaseModel):
9
+ dataset: str = Field(..., max_length=255)
10
+ me: str = Field(..., max_length=255)
11
+ dataset_id: int
12
+ file_id: int
13
+ run_number: int
14
+ ls_number: int
15
+ me_id: int
16
+ x_min: float
17
+ x_max: float
18
+ x_bin: float
19
+ y_min: float
20
+ y_max: float
21
+ y_bin: float
22
+ entries: int
23
+ data: list[list[float]]
24
+
25
+
26
+ class PaginatedLumisectionHistogram2DList(PaginatedBaseModel):
27
+ next: Optional[AnyUrl]
28
+ previous: Optional[AnyUrl]
29
+ results: list[LumisectionHistogram2D]
30
+
31
+
32
+ class LumisectionHistogram2DFilters(OBaseModel):
33
+ next_token: Optional[str] = None
34
+ page_size: Optional[int] = None
35
+ dataset_id: Optional[int] = None
36
+ file_id: Optional[int] = None
37
+ run_number: Optional[int] = None
38
+ run_number__lte: Optional[int] = None
39
+ run_number__gte: Optional[int] = None
40
+ ls_number: Optional[int] = None
41
+ ls_numbet__lte: Optional[int] = None
42
+ ls_number__gte: Optional[int] = None
43
+ me_id: Optional[int] = None
44
+ entries__gte: Optional[int] = None
45
+ dataset: Optional[str] = None
46
+ dataset__regex: Optional[str] = None
47
+ logical_file_name: Optional[str] = None
48
+ logical_file_name__regex: Optional[str] = None
49
+ me: Optional[str] = None
50
+ me__regex: Optional[str] = None
File without changes
@@ -0,0 +1,13 @@
1
+ from ...utils.api_client import BaseAuthorizedAPIClient
2
+ from .models import Lumisection, LumisectionFilters, PaginatedLumisectionList
3
+
4
+
5
+ class LumisectionClient(BaseAuthorizedAPIClient):
6
+ data_model = Lumisection
7
+ pagination_model = PaginatedLumisectionList
8
+ filter_class = LumisectionFilters
9
+ lookup_url = "lumisection/"
10
+
11
+ def get(self, dataset_id: int, run_number: int, ls_number: int, **kwargs):
12
+ edp = f"{dataset_id}/{run_number}/{ls_number}/"
13
+ return super().get(edp, **kwargs)
@@ -0,0 +1,34 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import AnyUrl, BaseModel, Field
4
+
5
+ from ...utils.base_model import OBaseModel, PaginatedBaseModel
6
+
7
+
8
+ class Lumisection(BaseModel):
9
+ dataset_id: int
10
+ dataset: str = Field(..., max_length=255)
11
+ run_number: int
12
+ ls_number: int
13
+ th1_count: int
14
+ th2_count: int
15
+
16
+
17
+ class PaginatedLumisectionList(PaginatedBaseModel):
18
+ next: Optional[AnyUrl]
19
+ previous: Optional[AnyUrl]
20
+ results: list[Lumisection]
21
+
22
+
23
+ class LumisectionFilters(OBaseModel):
24
+ next_token: Optional[str] = None
25
+ page_size: Optional[int] = None
26
+ dataset_id: Optional[int] = None
27
+ run_number: Optional[int] = None
28
+ run_number__lte: Optional[int] = None
29
+ run_number__gte: Optional[int] = None
30
+ ls_number: Optional[int] = None
31
+ ls_number__lte: Optional[int] = None
32
+ ls_number__gte: Optional[int] = None
33
+ dataset: Optional[str] = None
34
+ dataset__regex: Optional[str] = None
File without changes
@@ -0,0 +1,12 @@
1
+ from ...utils.api_client import BaseAuthorizedAPIClient
2
+ from .models import MEFilters, MonitoringElement
3
+
4
+
5
+ class MonitoringElementClient(BaseAuthorizedAPIClient):
6
+ data_model = MonitoringElement
7
+ filter_class = MEFilters
8
+ lookup_url = "mes/"
9
+
10
+ def get(self, id: int, **kwargs): # noqa: A002
11
+ edp = f"{id}/"
12
+ return super().get(edp, **kwargs)
@@ -0,0 +1,18 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from ...utils.base_model import OBaseModel
6
+
7
+
8
+ class MonitoringElement(BaseModel):
9
+ me_id: int
10
+ me: str = Field(..., max_length=255)
11
+ count: int
12
+ dim: int
13
+
14
+
15
+ class MEFilters(OBaseModel):
16
+ me: Optional[str] = None
17
+ me__regex: Optional[str] = None
18
+ dim: Optional[int] = None
File without changes
@@ -0,0 +1,29 @@
1
+ from ...utils.api_client import BaseAuthorizedAPIClient
2
+ from .models import MLBadLumisection, MLBadLumisectionFilters, PaginatedMLBadLumisectionList
3
+
4
+
5
+ class MLBadLumisectionClient(BaseAuthorizedAPIClient):
6
+ data_model = MLBadLumisection
7
+ pagination_model = PaginatedMLBadLumisectionList
8
+ filter_class = MLBadLumisectionFilters
9
+ lookup_url = "ml-bad-lumisection/"
10
+
11
+ def get(self, model_id: int, dataset_id: int, run_number: int, ls_number: int, me_id: int, **kwargs):
12
+ edp = f"{model_id}/{dataset_id}/{run_number}/{ls_number}/{me_id}/"
13
+ return super().get(edp, **kwargs)
14
+
15
+ def cert_json(self, model_id__in: list[int], dataset_id__in: list[int], run_number__in: list[int], **kwargs):
16
+ edp = "cert-json/"
17
+ midin = ",".join(str(v) for v in model_id__in)
18
+ didin = ",".join(str(v) for v in dataset_id__in)
19
+ ridin = ",".join(str(v) for v in run_number__in)
20
+ params = {"model_id__in": midin, "dataset_id__in": didin, "run_number__in": ridin}
21
+ return super().get(edp, params=params, return_raw_json=True, **kwargs)
22
+
23
+ def golden_json(self, model_id__in: list[int], dataset_id__in: list[int], run_number__in: list[int], **kwargs):
24
+ edp = "golden-json/"
25
+ midin = ",".join(str(v) for v in model_id__in)
26
+ didin = ",".join(str(v) for v in dataset_id__in)
27
+ ridin = ",".join(str(v) for v in run_number__in)
28
+ params = {"model_id__in": midin, "dataset_id__in": didin, "run_number__in": ridin}
29
+ return super().get(edp, params=params, return_raw_json=True, **kwargs)
@@ -0,0 +1,38 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import AnyUrl, BaseModel
4
+
5
+ from ...utils.base_model import OBaseModel, PaginatedBaseModel
6
+
7
+
8
+ class MLBadLumisection(BaseModel):
9
+ model_id: int
10
+ dataset_id: int
11
+ file_id: int
12
+ run_number: int
13
+ ls_number: int
14
+ me_id: int
15
+ mse: float
16
+
17
+
18
+ class PaginatedMLBadLumisectionList(PaginatedBaseModel):
19
+ next: Optional[AnyUrl]
20
+ previous: Optional[AnyUrl]
21
+ results: list[MLBadLumisection]
22
+
23
+
24
+ class MLBadLumisectionFilters(OBaseModel):
25
+ next_token: Optional[str] = None
26
+ page_size: Optional[int] = None
27
+ model_id: Optional[int] = None
28
+ model_id__in: Optional[list[int]] = None
29
+ dataset_id: Optional[int] = None
30
+ dataset_id__in: Optional[list[int]] = None
31
+ dataset: Optional[str] = None
32
+ dataset__regex: Optional[str] = None
33
+ me_id: Optional[int] = None
34
+ me: Optional[str] = None
35
+ me__regex: Optional[str] = None
36
+ run_number: Optional[int] = None
37
+ run_number__in: Optional[list[int]] = None
38
+ ls_number: Optional[int] = None
File without changes
@@ -0,0 +1,13 @@
1
+ from ...utils.api_client import BaseAuthorizedAPIClient
2
+ from .models import MLModelsIndex, MLModelsIndexFilters, PaginatedMLModelsIndexList
3
+
4
+
5
+ class MLModelsIndexClient(BaseAuthorizedAPIClient):
6
+ data_model = MLModelsIndex
7
+ pagination_model = PaginatedMLModelsIndexList
8
+ filter_class = MLModelsIndexFilters
9
+ lookup_url = "ml-models-index/"
10
+
11
+ def get(self, model_id: int, **kwargs):
12
+ edp = f"{model_id}/"
13
+ return super().get(edp, **kwargs)
@@ -0,0 +1,30 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import AnyUrl, BaseModel
4
+
5
+ from ...utils.base_model import OBaseModel, PaginatedBaseModel
6
+
7
+
8
+ class MLModelsIndex(BaseModel):
9
+ model_id: int
10
+ filename: str
11
+ target_me: str
12
+ active: bool
13
+
14
+
15
+ class PaginatedMLModelsIndexList(PaginatedBaseModel):
16
+ next: Optional[AnyUrl]
17
+ previous: Optional[AnyUrl]
18
+ results: list[MLModelsIndex]
19
+
20
+
21
+ class MLModelsIndexFilters(OBaseModel):
22
+ next_token: Optional[str] = None
23
+ page_size: Optional[int] = None
24
+ model_id: Optional[int] = None
25
+ model_id__in: Optional[list[int]] = None
26
+ filename: Optional[str] = None
27
+ filename__regex: Optional[str] = None
28
+ target_me: Optional[str] = None
29
+ target_me__regex: Optional[str] = None
30
+ active: Optional[bool] = None
File without changes
@@ -0,0 +1,49 @@
1
+ from typing import Optional
2
+
3
+ import requests
4
+ from requests.exceptions import HTTPError
5
+
6
+ from ...auth._base import BaseCredentials
7
+ from ...utils.api_client import BaseAPIClient
8
+ from ...utils.logger import logger
9
+ from .models import OMSFilter, OMSPage
10
+
11
+
12
+ class OMSProxyClient(BaseAPIClient):
13
+ default_timeout = 30
14
+ lookup_url = "oms-proxy/"
15
+
16
+ def __init__(
17
+ self,
18
+ creds: BaseCredentials,
19
+ *args: str,
20
+ **kwargs: str,
21
+ ) -> None:
22
+ super().__init__(*args, **kwargs)
23
+ self.creds = creds
24
+
25
+ def _build_headers(self) -> dict:
26
+ base = {
27
+ "accept": "application/json",
28
+ "User-Agent": self._build_user_agent()
29
+ }
30
+ self.creds.before_request(base)
31
+ return base
32
+
33
+ def query(self, endpoint: str, filters: list[OMSFilter], pages: Optional[list[OMSPage]] = None):
34
+ headers = self._build_headers()
35
+ endpoint_url = self.api_url + self.lookup_url
36
+
37
+ # Format filters
38
+ filters = {f"filter[{_filter.attribute_name}][{_filter.operator}]": _filter.value for _filter in filters}
39
+ pages = {f"page[{page.attribute_name}]": page.value for page in pages} if pages and len(pages) > 0 else {}
40
+ params = {"endpoint": endpoint, **filters, **pages}
41
+
42
+ response = requests.get(endpoint_url, headers=headers, params=params, timeout=self.default_timeout)
43
+ try:
44
+ response.raise_for_status()
45
+ except HTTPError as err:
46
+ logger.info(f"Api raw response: {response.text}")
47
+ raise err
48
+
49
+ return response.json()
@@ -0,0 +1,14 @@
1
+ from typing import Literal, Union
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class OMSFilter(BaseModel):
7
+ attribute_name: str
8
+ value: Union[str, int, float]
9
+ operator: Literal["EQ", "NEQ", "LT", "GT", "LE", "GE", "LIKE"]
10
+
11
+
12
+ class OMSPage(BaseModel):
13
+ attribute_name: str
14
+ value: int
File without changes
@@ -0,0 +1,13 @@
1
+ from ...utils.api_client import BaseAuthorizedAPIClient
2
+ from .models import PaginatedRunList, Run, RunFilters
3
+
4
+
5
+ class RunClient(BaseAuthorizedAPIClient):
6
+ data_model = Run
7
+ pagination_model = PaginatedRunList
8
+ filter_class = RunFilters
9
+ lookup_url = "run/"
10
+
11
+ def get(self, dataset_id: int, run_number: int, **kwargs):
12
+ edp = f"{dataset_id}/{run_number}/"
13
+ return super().get(edp, **kwargs)
@@ -0,0 +1,29 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import AnyUrl, BaseModel, Field
4
+
5
+ from ...utils.base_model import OBaseModel, PaginatedBaseModel
6
+
7
+
8
+ class Run(BaseModel):
9
+ dataset_id: int
10
+ dataset: str = Field(..., max_length=255)
11
+ run_number: int
12
+ ls_count: int
13
+
14
+
15
+ class PaginatedRunList(PaginatedBaseModel):
16
+ next: Optional[AnyUrl]
17
+ previous: Optional[AnyUrl]
18
+ results: list[Run]
19
+
20
+
21
+ class RunFilters(OBaseModel):
22
+ next_token: Optional[str] = None
23
+ page_size: Optional[int] = None
24
+ dataset_id: Optional[int] = None
25
+ run_number: Optional[int] = None
26
+ run_number__lte: Optional[int] = None
27
+ run_number__gte: Optional[int] = None
28
+ dataset: Optional[str] = None
29
+ dataset__regex: Optional[str] = None
cmsdials/cmsdials.py ADDED
@@ -0,0 +1,27 @@
1
+ from typing import Optional
2
+
3
+ from .auth._base import BaseCredentials
4
+ from .clients.dataset_index.client import DatasetIndexClient
5
+ from .clients.file_index.client import FileIndexClient
6
+ from .clients.h1d.client import LumisectionHistogram1DClient
7
+ from .clients.h2d.client import LumisectionHistogram2DClient
8
+ from .clients.lumisection.client import LumisectionClient
9
+ from .clients.mes.client import MonitoringElementClient
10
+ from .clients.ml_bad_lumisection.client import MLBadLumisectionClient
11
+ from .clients.ml_models_index.client import MLModelsIndexClient
12
+ from .clients.oms_proxy.client import OMSProxyClient
13
+ from .clients.run.client import RunClient
14
+
15
+
16
+ class Dials:
17
+ def __init__(self, creds: BaseCredentials, workspace: Optional[str] = None, *args, **kwargs) -> None:
18
+ self.dataset_index = DatasetIndexClient(creds, workspace, *args, **kwargs)
19
+ self.file_index = FileIndexClient(creds, workspace, *args, **kwargs)
20
+ self.h1d = LumisectionHistogram1DClient(creds, workspace, *args, **kwargs)
21
+ self.h2d = LumisectionHistogram2DClient(creds, workspace, *args, **kwargs)
22
+ self.lumi = LumisectionClient(creds, workspace, *args, **kwargs)
23
+ self.run = RunClient(creds, workspace, *args, **kwargs)
24
+ self.mes = MonitoringElementClient(creds, workspace, *args, **kwargs)
25
+ self.oms = OMSProxyClient(creds, *args, **kwargs)
26
+ self.ml_bad_lumis = MLBadLumisectionClient(creds, workspace, *args, **kwargs)
27
+ self.ml_models_index = MLModelsIndexClient(creds, workspace, *args, **kwargs)
cmsdials/filters.py ADDED
@@ -0,0 +1,25 @@
1
+ from .clients.dataset_index.models import DatasetIndexFilters
2
+ from .clients.file_index.models import FileIndexFilters
3
+ from .clients.h1d.models import LumisectionHistogram1DFilters
4
+ from .clients.h2d.models import LumisectionHistogram2DFilters
5
+ from .clients.lumisection.models import LumisectionFilters
6
+ from .clients.mes.models import MEFilters
7
+ from .clients.ml_bad_lumisection.models import MLBadLumisectionFilters
8
+ from .clients.ml_models_index.models import MLModelsIndexFilters
9
+ from .clients.oms_proxy.models import OMSFilter, OMSPage
10
+ from .clients.run.models import RunFilters
11
+
12
+
13
+ __all__ = [
14
+ "DatasetIndexFilters",
15
+ "FileIndexFilters",
16
+ "LumisectionHistogram1DFilters",
17
+ "LumisectionHistogram2DFilters",
18
+ "LumisectionFilters",
19
+ "RunFilters",
20
+ "MEFilters",
21
+ "MLBadLumisectionFilters",
22
+ "MLModelsIndexFilters",
23
+ "OMSFilter",
24
+ "OMSPage",
25
+ ]
File without changes
@@ -0,0 +1,24 @@
1
+ import json
2
+ from datetime import datetime
3
+
4
+
5
+ class TokenEncoder(json.JSONEncoder):
6
+ def default(self, o):
7
+ if isinstance(o, datetime):
8
+ return o.isoformat()
9
+
10
+ return json.JSONEncoder.default(self, o)
11
+
12
+
13
+ class TokenDecoder(json.JSONDecoder):
14
+ def __init__(self, *args, **kwargs):
15
+ json.JSONDecoder.__init__(self, *args, **kwargs, object_hook=self.object_hook)
16
+
17
+ def object_hook(self, obj):
18
+ ret = {}
19
+ for key, value in obj.items():
20
+ if key in ("expires_at", "refresh_expires_at"):
21
+ ret[key] = datetime.fromisoformat(value)
22
+ else:
23
+ ret[key] = value
24
+ return ret
@@ -0,0 +1,226 @@
1
+ import sys
2
+ from importlib import util as importlib_util
3
+ from traceback import format_exc
4
+ from typing import Optional
5
+ from warnings import warn
6
+
7
+ import requests
8
+ from pydantic import AnyUrl
9
+ from requests import Response, Session
10
+ from requests.adapters import DEFAULT_RETRIES, HTTPAdapter
11
+ from requests.exceptions import HTTPError
12
+
13
+ from .._version import __version__
14
+ from ..auth._base import BaseCredentials
15
+ from ..utils.logger import logger
16
+
17
+
18
+ if importlib_util.find_spec("tqdm"):
19
+ from tqdm.auto import tqdm
20
+
21
+ TQDM_INSTALLED = True
22
+ else:
23
+ TQDM_INSTALLED = False
24
+
25
+
26
+ class BaseAPIClient:
27
+ PRODUCTION_BASE_URL = "https://cmsdials-api.web.cern.ch/"
28
+ PRODUCTION_API_ROUTE = "api/"
29
+ PRODUCTION_API_VERSION = "v1/"
30
+
31
+ def __init__(
32
+ self, base_url: Optional[str] = None, route: Optional[str] = None, version: Optional[str] = None
33
+ ) -> None:
34
+ self.base_url = self.__endswithslash(base_url or self.PRODUCTION_BASE_URL)
35
+ self.route = self.__endswithslash(route or self.PRODUCTION_API_ROUTE)
36
+ self.version = self.__endswithslash(version or self.PRODUCTION_API_VERSION)
37
+
38
+ @staticmethod
39
+ def __endswithslash(value: str) -> str:
40
+ if value.endswith("/") is False:
41
+ return value + "/"
42
+ return value
43
+
44
+ @classmethod
45
+ def _requests_get_retriable(cls, *args, retries=DEFAULT_RETRIES, **kwargs) -> Response:
46
+ """
47
+ requests.get() with an additional `retries` parameter.
48
+
49
+ Specify retries=<number of attempts - 1> for simple use cases.
50
+ For advanced usage, see https://docs.python-requests.org/en/latest/user/advanced/
51
+ """
52
+ with Session() as s:
53
+ s.mount(cls.PRODUCTION_BASE_URL, HTTPAdapter(max_retries=retries))
54
+ ret = s.get(*args, **kwargs)
55
+ return ret
56
+
57
+ @staticmethod
58
+ def _build_user_agent():
59
+ py_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
60
+ req_version = requests.__version__
61
+ return f"dials-py/{__version__} (python/{py_version}, python-requests/{req_version})"
62
+
63
+ @property
64
+ def api_url(self):
65
+ return self.base_url + self.route + self.version
66
+
67
+
68
+ class BaseAuthorizedAPIClient(BaseAPIClient):
69
+ data_model = None
70
+ pagination_model = None
71
+ filter_class = None
72
+ lookup_url = None
73
+ default_timeout = 30 # seconds
74
+
75
+ def __init__(
76
+ self,
77
+ creds: BaseCredentials,
78
+ workspace: Optional[str] = None,
79
+ *args: str,
80
+ **kwargs: str,
81
+ ) -> None:
82
+ super().__init__(*args, **kwargs)
83
+ self.creds = creds
84
+ self.workspace = workspace
85
+
86
+ def _build_headers(self) -> dict:
87
+ base = {
88
+ "accept": "application/json",
89
+ "User-Agent": self._build_user_agent()
90
+ }
91
+ if self.workspace is not None:
92
+ base["Workspace"] = self.workspace
93
+ self.creds.before_request(base)
94
+ return base
95
+
96
+ def get(self, edp: str, retries=DEFAULT_RETRIES, params: Optional[dict] = None, return_raw_json: bool = False):
97
+ endpoint_url = self.api_url + self.lookup_url + edp
98
+ headers = self._build_headers()
99
+ response = self._requests_get_retriable(
100
+ endpoint_url,
101
+ headers=headers,
102
+ params=params,
103
+ timeout=self.default_timeout,
104
+ retries=retries,
105
+ )
106
+
107
+ try:
108
+ response.raise_for_status()
109
+ except HTTPError as err:
110
+ logger.info(f"Api raw response: {response.text}")
111
+ raise err
112
+
113
+ response = response.json()
114
+ if return_raw_json:
115
+ return response
116
+ return self.data_model(**response)
117
+
118
+ def list(self, filters=None, retries=DEFAULT_RETRIES):
119
+ filters = filters or self.filter_class()
120
+ endpoint_url = self.api_url + self.lookup_url
121
+ headers = self._build_headers()
122
+ response = self._requests_get_retriable(
123
+ endpoint_url,
124
+ headers=headers,
125
+ params=filters.cleandict(),
126
+ timeout=self.default_timeout,
127
+ retries=retries,
128
+ )
129
+
130
+ try:
131
+ response.raise_for_status()
132
+ except HTTPError as err:
133
+ logger.info(f"Api raw response: {response.text}")
134
+ raise err
135
+
136
+ response = response.json()
137
+ if self.pagination_model:
138
+ return self.pagination_model(**response)
139
+ elif isinstance(response, list):
140
+ return [self.data_model(**res) for res in response]
141
+
142
+ raise ValueError("pagination model is None and response is not a list.")
143
+
144
+ def __list_sync(
145
+ self,
146
+ filters,
147
+ max_pages: Optional[int] = None,
148
+ enable_progress: bool = False,
149
+ retries=DEFAULT_RETRIES,
150
+ keep_failed: bool = False,
151
+ resume_from=None,
152
+ ):
153
+ next_string: Optional[AnyUrl] = None
154
+ results = []
155
+ is_last_page = False
156
+
157
+ if resume_from is not None:
158
+ results = resume_from.results
159
+ next_string: AnyUrl = resume_from.next
160
+ if next_string is None and len(results):
161
+ warn(
162
+ "resume_from.next is None while resume_from.result is not empty, doing nothing.",
163
+ RuntimeWarning,
164
+ stacklevel=2,
165
+ )
166
+ is_last_page = True
167
+
168
+ total_pages = 0
169
+ use_tqdm = TQDM_INSTALLED and enable_progress
170
+
171
+ if use_tqdm:
172
+ progress = tqdm(desc="Progress", total=1)
173
+
174
+ while is_last_page is False:
175
+ next_token = dict(next_string.query_params()).get("next_token") if next_string else None
176
+ curr_filters = self.filter_class(**filters.model_dump())
177
+ curr_filters.next_token = next_token
178
+ try:
179
+ response = self.list(curr_filters, retries=retries)
180
+ except Exception as e: # noqa: BLE001
181
+ if use_tqdm:
182
+ progress.close()
183
+
184
+ if not keep_failed:
185
+ raise e
186
+
187
+ exc_formatted = format_exc()
188
+ warn(
189
+ "HTTP request failed, returning partial results. Exception: " + exc_formatted,
190
+ RuntimeWarning,
191
+ stacklevel=2,
192
+ )
193
+ return self.pagination_model(
194
+ next=next_string,
195
+ previous=None,
196
+ results=results,
197
+ exc_type=e.__class__.__name__,
198
+ exc_formatted=exc_formatted,
199
+ )
200
+ results.extend(response.results)
201
+ next_string: AnyUrl = response.next
202
+ is_last_page = next_string is None
203
+ total_pages += 1
204
+ max_pages_reached = max_pages and total_pages >= max_pages
205
+ if use_tqdm:
206
+ if is_last_page or max_pages_reached:
207
+ progress.update()
208
+ else:
209
+ progress.total = total_pages + 1
210
+ progress.update(1)
211
+ if max_pages_reached:
212
+ break
213
+
214
+ if use_tqdm:
215
+ progress.close()
216
+
217
+ return self.pagination_model(
218
+ next=next_string,
219
+ previous=None,
220
+ results=results, # No problem re-using last response count
221
+ )
222
+
223
+ def list_all(self, filters, max_pages: Optional[int] = None, **kwargs):
224
+ if self.pagination_model is None:
225
+ return self.list(filters, retries=kwargs.get("retries", DEFAULT_RETRIES))
226
+ return self.__list_sync(filters, max_pages, **kwargs)