clarity-api-sdk-python 0.1.2__py3-none-any.whl → 0.2.10__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.
- clarity_api_sdk_python-0.2.10.dist-info/METADATA +79 -0
- clarity_api_sdk_python-0.2.10.dist-info/RECORD +11 -0
- clarity_api_sdk_python-0.2.10.dist-info/top_level.txt +1 -0
- cti/__init__.py +0 -0
- cti/api/async_client.py +127 -0
- {api → cti/api}/client.py +1 -1
- {logger → cti/logger}/logger.py +35 -17
- cti/main.py +33 -0
- clarity_api_sdk_python-0.1.2.dist-info/METADATA +0 -26
- clarity_api_sdk_python-0.1.2.dist-info/RECORD +0 -8
- clarity_api_sdk_python-0.1.2.dist-info/top_level.txt +0 -2
- {clarity_api_sdk_python-0.1.2.dist-info → clarity_api_sdk_python-0.2.10.dist-info}/WHEEL +0 -0
- {api → cti/api}/__init__.py +0 -0
- {logger → cti/logger}/__init__.py +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clarity-api-sdk-python
|
|
3
|
+
Version: 0.2.10
|
|
4
|
+
Summary: A Python SDK to connect to the CTI Clarity API server.
|
|
5
|
+
Author-email: "Chesapeake Technology Inc." <support@chesapeaketech.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/chesapeake-tech/clarity-api-sdk-python
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: httpx>=0.28.1
|
|
13
|
+
Requires-Dist: brotli
|
|
14
|
+
Requires-Dist: h2
|
|
15
|
+
Requires-Dist: httpx_auth>=0.23.1
|
|
16
|
+
Requires-Dist: httpx-retries>=0.4.5
|
|
17
|
+
Requires-Dist: structlog
|
|
18
|
+
Provides-Extra: brotli
|
|
19
|
+
Requires-Dist: httpx[brotli]>=0.28.1; extra == "brotli"
|
|
20
|
+
Provides-Extra: http2
|
|
21
|
+
Requires-Dist: httpx[http2]>=0.28.1; extra == "http2"
|
|
22
|
+
|
|
23
|
+
# Clarity API SDK for Python
|
|
24
|
+
|
|
25
|
+
[](https://pypi.org/project/clarity-api-sdk-python/)
|
|
26
|
+
[](ttps://test.pypi.org/project/clarity-api-sdk-python/)
|
|
27
|
+

|
|
28
|
+
|
|
29
|
+
A Python SDK for connecting to the CTI API server, with structured logging included.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install clarity-api-sdk-python
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Logging
|
|
38
|
+
|
|
39
|
+
Logging support is built with [structlog](https://pypi.org/project/structlog/).
|
|
40
|
+
|
|
41
|
+
Set the root logger by setting the environment variable `LOG_LEVEL`. Otherwise, the default root logging is set to `INFO`.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
"""Example"""
|
|
45
|
+
|
|
46
|
+
import logging
|
|
47
|
+
|
|
48
|
+
from cti.logger import initialize_logger, get_logger, ExternalLoggerConfig
|
|
49
|
+
|
|
50
|
+
initialize_logger(
|
|
51
|
+
external_logger_configurations=[
|
|
52
|
+
ExternalLoggerConfig(name="urllib3"),
|
|
53
|
+
ExternalLoggerConfig(name="httpcore"),
|
|
54
|
+
ExternalLoggerConfig(name="httpx"),
|
|
55
|
+
ExternalLoggerConfig(name="httpx_auth"),
|
|
56
|
+
ExternalLoggerConfig(name="httpx_retries"),
|
|
57
|
+
],
|
|
58
|
+
handlers=[logging.FileHandler("app.log")]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
logger_a = get_logger("logger_a")
|
|
62
|
+
logger_b = get_logger("logger_b", "WARNING")
|
|
63
|
+
|
|
64
|
+
# root_logger = logging.getLogger()
|
|
65
|
+
# root_logger.setLevel("DEBUG")
|
|
66
|
+
|
|
67
|
+
logger_a.info("This is info message from logger_a")
|
|
68
|
+
logger_a.critical("This is critical message from logger_a")
|
|
69
|
+
|
|
70
|
+
# Dynamically change the log level of logger_a to WARNING
|
|
71
|
+
print("\nChanging logger_a level to WARNING...\n")
|
|
72
|
+
logging.getLogger("logger_a").setLevel(logging.WARNING)
|
|
73
|
+
|
|
74
|
+
logger_a.info("This info message from logger_a should NOT be visible.")
|
|
75
|
+
logger_a.warning("This is a new warning message from logger_a.")
|
|
76
|
+
|
|
77
|
+
logger_b.info("This info message from logger_b should NOT be visible.")
|
|
78
|
+
logger_b.warning("This is warning message from logger_b")
|
|
79
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
cti/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
cti/main.py,sha256=EBTB9p_PONeC4rRqm8tknn58A5synrO4-SrFMIrATFQ,1034
|
|
3
|
+
cti/api/__init__.py,sha256=HClrIT0WmCaPPRsejmXBOSS7R-OclE4g7qDgxQS_5hI,55
|
|
4
|
+
cti/api/async_client.py,sha256=T-Rfcr70uYPzFCNanZloUQoJwuNCGFtDPf7HaTyycw8,4694
|
|
5
|
+
cti/api/client.py,sha256=hhfxCpH8ymbArJFAhShPLhnvxf8clY-H8sMbfzIneos,4451
|
|
6
|
+
cti/logger/__init__.py,sha256=o44tO0O_lUvpIN6w0B9-t61L5MKHmD14elaiIm6B6DA,102
|
|
7
|
+
cti/logger/logger.py,sha256=9If0ckorKvJD807b41lR7fZJzT1JpGbu_hmsV1vC1YY,10777
|
|
8
|
+
clarity_api_sdk_python-0.2.10.dist-info/METADATA,sha256=I253M2NSv7bPZ9wVHEfMSs1KtoluEZTLmk4FTmpanVk,2682
|
|
9
|
+
clarity_api_sdk_python-0.2.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
clarity_api_sdk_python-0.2.10.dist-info/top_level.txt,sha256=q0eCD9KnKXWf_VImQ7xYkyYD7cvcG-4X-vm36HhdT_M,4
|
|
11
|
+
clarity_api_sdk_python-0.2.10.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cti
|
cti/__init__.py
ADDED
|
File without changes
|
cti/api/async_client.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Async client for clarity API"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
from httpx import AsyncClient, HTTPStatusError, RequestError, Response, URL
|
|
7
|
+
from httpx_auth import OAuth2ClientCredentials, OAuth2, TokenMemoryCache
|
|
8
|
+
from httpx_retries import Retry, RetryTransport
|
|
9
|
+
|
|
10
|
+
from cti.logger import get_logger
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
OAuth2.token_cache = TokenMemoryCache()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ClarityApiAsyncClient(AsyncClient):
|
|
17
|
+
"""Async client for Clarity API configured with OAuth2 authentication, retry mechanism
|
|
18
|
+
and other defaults to connect to the clarity server.
|
|
19
|
+
|
|
20
|
+
Reuse this client instance for multiple requests for faster performance.
|
|
21
|
+
|
|
22
|
+
The authorization token is cached and shared between each client instance to minimize
|
|
23
|
+
calls to the https://auth.sonarwiz.io/ keycloak server.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self):
|
|
27
|
+
|
|
28
|
+
# credentials for Clarity API
|
|
29
|
+
cti_credentials = OAuth2ClientCredentials(
|
|
30
|
+
token_url=(
|
|
31
|
+
f'{os.environ.get("KEYCLOAK_SERVER_URL", "missing KEYCLOAK_SERVER_URL")}/realms/'
|
|
32
|
+
f'{os.environ.get("KEYCLOAK_REALM", "missing KEYCLOAK_REALM")}'
|
|
33
|
+
"/protocol/openid-connect/token"
|
|
34
|
+
),
|
|
35
|
+
client_id=os.environ.get(
|
|
36
|
+
"KEYCLOAK_CLIENT_ID", "missing KEYCLOAK_CLIENT_ID"
|
|
37
|
+
),
|
|
38
|
+
client_secret=os.environ.get(
|
|
39
|
+
"KEYCLOAK_CLIENT_SECRET", "missing KEYCLOAK_CLIENT_SECRET"
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# retry mechanism for API requests
|
|
44
|
+
retry = Retry(total=12, backoff_factor=0.5)
|
|
45
|
+
transport = RetryTransport(retry=retry)
|
|
46
|
+
|
|
47
|
+
super().__init__(
|
|
48
|
+
base_url=os.environ.get("CLARITY_API_URL", "missing Clarity_API_URL"),
|
|
49
|
+
auth=cti_credentials,
|
|
50
|
+
timeout=60,
|
|
51
|
+
transport=transport,
|
|
52
|
+
http2=True,
|
|
53
|
+
headers={"Accept": "application/json"},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
async def request(self, method: str, url: URL | str, **kwargs) -> Response:
|
|
57
|
+
"""Make an async request to the Clarity API and handle exceptions. Using
|
|
58
|
+
this allows requests to be made concurrently and can provide significantly
|
|
59
|
+
improved performance. Use this to make multiple concurrent requests.
|
|
60
|
+
|
|
61
|
+
The exceptions are caught and logged, and then re-raised.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
method: HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
65
|
+
url: relative URL for the request, eg: "/api/v1/projects/12345"
|
|
66
|
+
kwargs: additional keyword arguments to be passed to the request
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
httpx.Response: Response from the API
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
RequestError: If there was an issue with the request
|
|
73
|
+
HTTPStatusError: If the response status code is not in the 2xx range
|
|
74
|
+
Exception: For any other uncaught exception
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
request_id = str(uuid.uuid4())
|
|
78
|
+
if "headers" not in kwargs or kwargs["headers"] is None:
|
|
79
|
+
kwargs["headers"] = {}
|
|
80
|
+
# append x-request-id header to the kwargs "headers"
|
|
81
|
+
kwargs["headers"].update({"x-request-id": request_id})
|
|
82
|
+
logger.info(
|
|
83
|
+
"request",
|
|
84
|
+
extra={"url": url, "request_id": request_id},
|
|
85
|
+
)
|
|
86
|
+
# make the actual request and return the response
|
|
87
|
+
response = await super().request(method, url, **kwargs)
|
|
88
|
+
logger.info(
|
|
89
|
+
"response",
|
|
90
|
+
extra={
|
|
91
|
+
"request_id": request_id,
|
|
92
|
+
"response": {"status_code": response.status_code},
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
return response
|
|
96
|
+
except HTTPStatusError as e:
|
|
97
|
+
logger.error(
|
|
98
|
+
"http",
|
|
99
|
+
extra={
|
|
100
|
+
"request": {
|
|
101
|
+
"method": e.request.method,
|
|
102
|
+
"url": str(e.request.url),
|
|
103
|
+
"headers": dict(e.request.headers),
|
|
104
|
+
},
|
|
105
|
+
"error": {
|
|
106
|
+
"message": str(e.response.content),
|
|
107
|
+
"status_code": e.response.status_code,
|
|
108
|
+
"headers": dict(e.response.headers),
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
raise e
|
|
113
|
+
except RequestError as e:
|
|
114
|
+
logger.error(
|
|
115
|
+
"request",
|
|
116
|
+
extra={
|
|
117
|
+
"request": {
|
|
118
|
+
"method": e.request.method,
|
|
119
|
+
"url": str(e.request.url),
|
|
120
|
+
"headers": dict(e.request.headers),
|
|
121
|
+
},
|
|
122
|
+
"error": {
|
|
123
|
+
"message": str(e),
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
raise e
|
{api → cti/api}/client.py
RENAMED
|
@@ -7,7 +7,7 @@ from httpx import Client, HTTPStatusError, RequestError, Response, URL
|
|
|
7
7
|
from httpx_auth import OAuth2ClientCredentials, OAuth2, TokenMemoryCache
|
|
8
8
|
from httpx_retries import Retry, RetryTransport
|
|
9
9
|
|
|
10
|
-
from logger import get_logger
|
|
10
|
+
from cti.logger import get_logger
|
|
11
11
|
|
|
12
12
|
logger = get_logger(__name__)
|
|
13
13
|
OAuth2.token_cache = TokenMemoryCache()
|
{logger → cti/logger}/logger.py
RENAMED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from collections import OrderedDict
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
import logging
|
|
6
|
+
import os
|
|
6
7
|
import socket
|
|
7
8
|
import sys
|
|
8
9
|
import urllib.error
|
|
@@ -32,16 +33,25 @@ class ExternalLoggerConfig:
|
|
|
32
33
|
self.propagate = propagate
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
def get_logger(
|
|
36
|
+
def get_logger(
|
|
37
|
+
name: str, level: int | str | None = None
|
|
38
|
+
) -> structlog.stdlib.BoundLogger:
|
|
36
39
|
"""Creates a structlog logger with the specified name.
|
|
37
40
|
|
|
38
41
|
Args:
|
|
39
42
|
name (str): The logger name.
|
|
43
|
+
level (int | str | None, optional): The logging level for this logger.
|
|
44
|
+
If None, the root logger's level is used. Defaults to None.
|
|
40
45
|
|
|
41
46
|
Returns:
|
|
42
|
-
|
|
47
|
+
structlog.stdlib.BoundLogger: The structlog logger.
|
|
43
48
|
"""
|
|
44
|
-
|
|
49
|
+
logger = structlog.get_logger(name)
|
|
50
|
+
if level:
|
|
51
|
+
# To set the level, we need to get the actual standard library logger instance.
|
|
52
|
+
stdlib_logger = logging.getLogger(name)
|
|
53
|
+
stdlib_logger.setLevel(level)
|
|
54
|
+
return logger
|
|
45
55
|
|
|
46
56
|
|
|
47
57
|
def _flatten_extra_processor(_, __, event_dict):
|
|
@@ -166,8 +176,9 @@ def _secret_redaction_processor(_, __, event_dict):
|
|
|
166
176
|
|
|
167
177
|
|
|
168
178
|
def initialize_logger(
|
|
169
|
-
initial_context: dict | None,
|
|
170
|
-
external_logger_configurations: list[ExternalLoggerConfig] | None,
|
|
179
|
+
initial_context: dict | None = None,
|
|
180
|
+
external_logger_configurations: list[ExternalLoggerConfig] | None = None,
|
|
181
|
+
handlers: list[logging.Handler] | None = None,
|
|
171
182
|
) -> None:
|
|
172
183
|
"""Configures logging for the application using structlog.
|
|
173
184
|
|
|
@@ -194,15 +205,24 @@ def initialize_logger(
|
|
|
194
205
|
bind to the context at the start of the application. These values
|
|
195
206
|
will be included in every log message. Defaults to None.
|
|
196
207
|
external_logger_configurations (list[ExternalLoggerConfig], optional): A list of configuration
|
|
208
|
+
handlers (list[logging.Handler], optional): A list of handlers to send log records to.
|
|
197
209
|
"""
|
|
198
210
|
# Configure standard logging to be the sink for structlog.
|
|
199
211
|
# The format="%(message)s" is important because structlog will format the log record
|
|
200
212
|
# into a JSON string and pass it as the 'message'.
|
|
201
|
-
logging.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
)
|
|
213
|
+
formatter = logging.Formatter("%(message)s")
|
|
214
|
+
|
|
215
|
+
# Create a handler for stdout
|
|
216
|
+
stdout_handler = logging.StreamHandler(sys.stdout)
|
|
217
|
+
stdout_handler.setFormatter(formatter)
|
|
218
|
+
|
|
219
|
+
# Get the root logger and add handlers
|
|
220
|
+
root_logger = logging.getLogger()
|
|
221
|
+
root_logger.addHandler(stdout_handler)
|
|
222
|
+
for handler in handlers or []:
|
|
223
|
+
handler.setFormatter(formatter)
|
|
224
|
+
root_logger.addHandler(handler)
|
|
225
|
+
root_logger.setLevel(_level())
|
|
206
226
|
|
|
207
227
|
# configure external loggers
|
|
208
228
|
if external_logger_configurations:
|
|
@@ -260,15 +280,13 @@ def initialize_logger(
|
|
|
260
280
|
structlog.contextvars.bind_contextvars(**static_context)
|
|
261
281
|
|
|
262
282
|
|
|
263
|
-
def _level():
|
|
283
|
+
def _level() -> str:
|
|
264
284
|
"""Get the log level for the logger.
|
|
265
285
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
- MANUAL only logs ERROR
|
|
269
|
-
- PRODUCTION - turns off debug
|
|
286
|
+
The log level is determined by the `LOG_LEVEL` environment variable.
|
|
287
|
+
If the environment variable is not set, it defaults to "ERROR".
|
|
270
288
|
|
|
271
289
|
Returns:
|
|
272
|
-
str: log level
|
|
290
|
+
str: The log level as a string (e.g., "DEBUG", "INFO", "ERROR").
|
|
273
291
|
"""
|
|
274
|
-
return "
|
|
292
|
+
return os.getenv("LOG_LEVEL", "INFO").upper()
|
cti/main.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Example"""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from cti.logger import initialize_logger, get_logger, ExternalLoggerConfig
|
|
6
|
+
|
|
7
|
+
initialize_logger(
|
|
8
|
+
external_logger_configurations=[
|
|
9
|
+
ExternalLoggerConfig(name="urllib3"),
|
|
10
|
+
ExternalLoggerConfig(name="httpcore"),
|
|
11
|
+
ExternalLoggerConfig(name="httpx"),
|
|
12
|
+
ExternalLoggerConfig(name="httpx_auth"),
|
|
13
|
+
ExternalLoggerConfig(name="httpx_retries"),
|
|
14
|
+
]
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger_a = get_logger("logger_a")
|
|
18
|
+
logger_b = get_logger("logger_b", "WARNING")
|
|
19
|
+
|
|
20
|
+
# root_logger = logging.getLogger()
|
|
21
|
+
# root_logger.setLevel("DEBUG")
|
|
22
|
+
|
|
23
|
+
logger_a.info("This is info message from logger_a")
|
|
24
|
+
logger_a.critical("This is critical message from logger_a")
|
|
25
|
+
|
|
26
|
+
# Dynamically change the log level of logger_a to WARNING
|
|
27
|
+
print("\nChanging logger_a level to WARNING...\n")
|
|
28
|
+
logging.getLogger("logger_a").setLevel(logging.WARNING)
|
|
29
|
+
|
|
30
|
+
logger_a.info("This info message from logger_a should NOT be visible.")
|
|
31
|
+
logger_a.warning("This is a new warning message from logger_a.")
|
|
32
|
+
|
|
33
|
+
logger_b.warning("This is warning message from logger_b")
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: clarity-api-sdk-python
|
|
3
|
-
Version: 0.1.2
|
|
4
|
-
Summary: A Python SDK to connect to the CTI Clarity API server.
|
|
5
|
-
Author-email: "Chesapeake Technology Inc." <support@chesapeaketech.com>
|
|
6
|
-
Project-URL: Homepage, https://github.com/chesapeake-tech/clarity-api-sdk-python
|
|
7
|
-
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python: >=3.12
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
Requires-Dist: httpx_auth>=0.23.1
|
|
13
|
-
Requires-Dist: httpx-retries>=0.4.5
|
|
14
|
-
Requires-Dist: httpx[brotli]>=0.28.1
|
|
15
|
-
Requires-Dist: httpx[http2]>=0.28.1
|
|
16
|
-
Requires-Dist: structlog
|
|
17
|
-
|
|
18
|
-
# Clarity API SDK for Python
|
|
19
|
-
|
|
20
|
-
A Python SDK for connecting to the CTI API server, with structured logging included.
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
pip install clarity-api-sdk-python
|
|
26
|
-
```
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
api/__init__.py,sha256=HClrIT0WmCaPPRsejmXBOSS7R-OclE4g7qDgxQS_5hI,55
|
|
2
|
-
api/client.py,sha256=ACXwN8MYWcj-1LNk2TJXD015ePNX0OQWC7qbJnc35Dc,4447
|
|
3
|
-
logger/__init__.py,sha256=o44tO0O_lUvpIN6w0B9-t61L5MKHmD14elaiIm6B6DA,102
|
|
4
|
-
logger/logger.py,sha256=p6VVXp8tcnaIM2hVMOWACS-Bs1kPCbS4dD9pBSQJ0a0,9720
|
|
5
|
-
clarity_api_sdk_python-0.1.2.dist-info/METADATA,sha256=gH9wBa9rkKmW8KQiBVTlsRZjldPbYEFwKe82WygTtIw,842
|
|
6
|
-
clarity_api_sdk_python-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
-
clarity_api_sdk_python-0.1.2.dist-info/top_level.txt,sha256=pfn0Jb5h-xZeUmY1lAn7svgMuIf_2siz75uRuyChn5s,11
|
|
8
|
-
clarity_api_sdk_python-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
{api → cti/api}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|