crypticorn 2.17.0rc1__py3-none-any.whl → 2.17.0rc2__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.
- crypticorn/__init__.py +2 -2
- crypticorn/auth/client/api/admin_api.py +415 -13
- crypticorn/auth/client/api/auth_api.py +2622 -113
- crypticorn/auth/client/api/service_api.py +258 -7
- crypticorn/auth/client/api/user_api.py +2485 -270
- crypticorn/auth/client/api/wallet_api.py +1518 -77
- crypticorn/auth/client/models/create_api_key_request.py +2 -1
- crypticorn/auth/client/models/get_api_keys200_response_inner.py +2 -1
- crypticorn/auth/client/rest.py +23 -4
- crypticorn/auth/main.py +8 -5
- crypticorn/client.py +227 -59
- crypticorn/common/__init__.py +0 -1
- crypticorn/common/metrics.py +4 -6
- crypticorn/common/middleware.py +10 -5
- crypticorn/common/pagination.py +137 -18
- crypticorn/common/router/admin_router.py +1 -1
- crypticorn/common/utils.py +2 -1
- crypticorn/common/warnings.py +1 -0
- crypticorn/hive/client/api/admin_api.py +1234 -51
- crypticorn/hive/client/api/data_api.py +517 -13
- crypticorn/hive/client/api/models_api.py +1657 -83
- crypticorn/hive/client/api/status_api.py +415 -13
- crypticorn/hive/client/models/api_error_identifier.py +1 -1
- crypticorn/hive/client/rest.py +23 -4
- crypticorn/hive/main.py +99 -25
- crypticorn/klines/client/api/admin_api.py +1234 -51
- crypticorn/klines/client/api/change_in_timeframe_api.py +278 -7
- crypticorn/klines/client/api/funding_rates_api.py +324 -7
- crypticorn/klines/client/api/ohlcv_data_api.py +399 -7
- crypticorn/klines/client/api/status_api.py +415 -13
- crypticorn/klines/client/api/symbols_api.py +225 -7
- crypticorn/klines/client/api/udf_api.py +1393 -120
- crypticorn/klines/client/models/api_error_identifier.py +3 -1
- crypticorn/klines/client/rest.py +23 -4
- crypticorn/klines/main.py +89 -12
- crypticorn/metrics/client/api/admin_api.py +1234 -51
- crypticorn/metrics/client/api/exchanges_api.py +1405 -140
- crypticorn/metrics/client/api/indicators_api.py +640 -13
- crypticorn/metrics/client/api/logs_api.py +305 -7
- crypticorn/metrics/client/api/marketcap_api.py +1240 -60
- crypticorn/metrics/client/api/markets_api.py +352 -7
- crypticorn/metrics/client/api/quote_currencies_api.py +237 -7
- crypticorn/metrics/client/api/status_api.py +415 -13
- crypticorn/metrics/client/api/tokens_api.py +400 -13
- crypticorn/metrics/client/configuration.py +4 -2
- crypticorn/metrics/client/rest.py +23 -4
- crypticorn/metrics/main.py +113 -19
- crypticorn/pay/client/api/admin_api.py +1720 -126
- crypticorn/pay/client/api/now_payments_api.py +1013 -42
- crypticorn/pay/client/api/payments_api.py +580 -13
- crypticorn/pay/client/api/products_api.py +915 -25
- crypticorn/pay/client/api/status_api.py +415 -13
- crypticorn/pay/client/configuration.py +2 -2
- crypticorn/pay/client/models/api_error_identifier.py +7 -7
- crypticorn/pay/client/models/scope.py +1 -0
- crypticorn/pay/client/rest.py +23 -4
- crypticorn/pay/main.py +10 -6
- crypticorn/trade/client/__init__.py +2 -1
- crypticorn/trade/client/api/__init__.py +0 -1
- crypticorn/trade/client/api/admin_api.py +1718 -123
- crypticorn/trade/client/api/api_keys_api.py +1596 -103
- crypticorn/trade/client/api/bots_api.py +1106 -47
- crypticorn/trade/client/api/exchanges_api.py +592 -19
- crypticorn/trade/client/api/notifications_api.py +1340 -112
- crypticorn/trade/client/api/orders_api.py +240 -7
- crypticorn/trade/client/api/status_api.py +415 -13
- crypticorn/trade/client/api/strategies_api.py +1170 -69
- crypticorn/trade/client/api/trading_actions_api.py +650 -19
- crypticorn/trade/client/models/__init__.py +2 -0
- crypticorn/trade/client/models/exchange.py +6 -1
- crypticorn/trade/client/models/exchange_key_balance.py +111 -0
- crypticorn/trade/client/models/futures_balance.py +27 -25
- crypticorn/trade/client/models/spot_balance.py +110 -0
- crypticorn/trade/client/models/strategy.py +5 -3
- crypticorn/trade/client/models/strategy_create.py +6 -4
- crypticorn/trade/client/models/strategy_exchange_info.py +16 -4
- crypticorn/trade/client/models/strategy_update.py +2 -2
- crypticorn/trade/client/rest.py +23 -4
- crypticorn/trade/main.py +15 -12
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc2.dist-info}/METADATA +64 -20
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc2.dist-info}/RECORD +85 -84
- crypticorn/trade/client/api/futures_trading_panel_api.py +0 -1285
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc2.dist-info}/WHEEL +0 -0
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc2.dist-info}/entry_points.txt +0 -0
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc2.dist-info}/licenses/LICENSE +0 -0
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc2.dist-info}/top_level.txt +0 -0
@@ -50,6 +50,7 @@ class CreateApiKeyRequest(BaseModel):
|
|
50
50
|
if i not in set(
|
51
51
|
[
|
52
52
|
"read:predictions",
|
53
|
+
"read:dexsignals",
|
53
54
|
"read:hive:model",
|
54
55
|
"read:hive:data",
|
55
56
|
"write:hive:model",
|
@@ -84,7 +85,7 @@ class CreateApiKeyRequest(BaseModel):
|
|
84
85
|
]
|
85
86
|
):
|
86
87
|
raise ValueError(
|
87
|
-
"each list item must be one of ('read:predictions', 'read:hive:model', 'read:hive:data', 'write:hive:model', 'read:trade:bots', 'write:trade:bots', 'read:trade:exchangekeys', 'write:trade:exchangekeys', 'read:trade:orders', 'read:trade:actions', 'write:trade:actions', 'read:trade:exchanges', 'read:trade:futures', 'write:trade:futures', 'read:trade:notifications', 'write:trade:notifications', 'read:trade:strategies', 'write:trade:strategies', 'read:pay:payments', 'read:pay:products', 'write:pay:products', 'read:pay:now', 'write:pay:now', 'read:metrics:marketcap', 'read:metrics:indicators', 'read:metrics:exchanges', 'read:metrics:tokens', 'read:metrics:markets', 'read:sentiment', 'read:klines', 'read:admin', 'write:admin')"
|
88
|
+
"each list item must be one of ('read:predictions', 'read:dexsignals', 'read:hive:model', 'read:hive:data', 'write:hive:model', 'read:trade:bots', 'write:trade:bots', 'read:trade:exchangekeys', 'write:trade:exchangekeys', 'read:trade:orders', 'read:trade:actions', 'write:trade:actions', 'read:trade:exchanges', 'read:trade:futures', 'write:trade:futures', 'read:trade:notifications', 'write:trade:notifications', 'read:trade:strategies', 'write:trade:strategies', 'read:pay:payments', 'read:pay:products', 'write:pay:products', 'read:pay:now', 'write:pay:now', 'read:metrics:marketcap', 'read:metrics:indicators', 'read:metrics:exchanges', 'read:metrics:tokens', 'read:metrics:markets', 'read:sentiment', 'read:klines', 'read:admin', 'write:admin')"
|
88
89
|
)
|
89
90
|
return value
|
90
91
|
|
@@ -58,6 +58,7 @@ class GetApiKeys200ResponseInner(BaseModel):
|
|
58
58
|
if i not in set(
|
59
59
|
[
|
60
60
|
"read:predictions",
|
61
|
+
"read:dexsignals",
|
61
62
|
"read:hive:model",
|
62
63
|
"read:hive:data",
|
63
64
|
"write:hive:model",
|
@@ -92,7 +93,7 @@ class GetApiKeys200ResponseInner(BaseModel):
|
|
92
93
|
]
|
93
94
|
):
|
94
95
|
raise ValueError(
|
95
|
-
"each list item must be one of ('read:predictions', 'read:hive:model', 'read:hive:data', 'write:hive:model', 'read:trade:bots', 'write:trade:bots', 'read:trade:exchangekeys', 'write:trade:exchangekeys', 'read:trade:orders', 'read:trade:actions', 'write:trade:actions', 'read:trade:exchanges', 'read:trade:futures', 'write:trade:futures', 'read:trade:notifications', 'write:trade:notifications', 'read:trade:strategies', 'write:trade:strategies', 'read:pay:payments', 'read:pay:products', 'write:pay:products', 'read:pay:now', 'write:pay:now', 'read:metrics:marketcap', 'read:metrics:indicators', 'read:metrics:exchanges', 'read:metrics:tokens', 'read:metrics:markets', 'read:sentiment', 'read:klines', 'read:admin', 'write:admin')"
|
96
|
+
"each list item must be one of ('read:predictions', 'read:dexsignals', 'read:hive:model', 'read:hive:data', 'write:hive:model', 'read:trade:bots', 'write:trade:bots', 'read:trade:exchangekeys', 'write:trade:exchangekeys', 'read:trade:orders', 'read:trade:actions', 'write:trade:actions', 'read:trade:exchanges', 'read:trade:futures', 'write:trade:futures', 'read:trade:notifications', 'write:trade:notifications', 'read:trade:strategies', 'write:trade:strategies', 'read:pay:payments', 'read:pay:products', 'write:pay:products', 'read:pay:now', 'write:pay:now', 'read:metrics:marketcap', 'read:metrics:indicators', 'read:metrics:exchanges', 'read:metrics:tokens', 'read:metrics:markets', 'read:sentiment', 'read:klines', 'read:admin', 'write:admin')"
|
96
97
|
)
|
97
98
|
return value
|
98
99
|
|
crypticorn/auth/client/rest.py
CHANGED
@@ -77,6 +77,7 @@ class RESTClientObject:
|
|
77
77
|
|
78
78
|
self.pool_manager: Optional[aiohttp.ClientSession] = None
|
79
79
|
self.retry_client: Optional[aiohttp_retry.RetryClient] = None
|
80
|
+
self.is_sync: bool = False # Track whether this is sync or async mode
|
80
81
|
|
81
82
|
async def close(self) -> None:
|
82
83
|
if self.pool_manager:
|
@@ -170,7 +171,9 @@ class RESTClientObject:
|
|
170
171
|
|
171
172
|
pool_manager: Union[aiohttp.ClientSession, aiohttp_retry.RetryClient]
|
172
173
|
|
173
|
-
#
|
174
|
+
# For sync operations, always use a fresh session
|
175
|
+
should_close_session = False
|
176
|
+
|
174
177
|
if self.pool_manager is None:
|
175
178
|
self.pool_manager = aiohttp.ClientSession(
|
176
179
|
connector=aiohttp.TCPConnector(
|
@@ -178,6 +181,9 @@ class RESTClientObject:
|
|
178
181
|
),
|
179
182
|
trust_env=True,
|
180
183
|
)
|
184
|
+
# Only close session automatically in sync mode
|
185
|
+
should_close_session = self.is_sync
|
186
|
+
|
181
187
|
pool_manager = self.pool_manager
|
182
188
|
|
183
189
|
if self.retries is not None and method in ALLOW_RETRY_METHODS:
|
@@ -193,6 +199,19 @@ class RESTClientObject:
|
|
193
199
|
)
|
194
200
|
pool_manager = self.retry_client
|
195
201
|
|
196
|
-
|
197
|
-
|
198
|
-
|
202
|
+
try:
|
203
|
+
r = await pool_manager.request(**args)
|
204
|
+
# For sessions we're about to close, read the data immediately
|
205
|
+
if should_close_session:
|
206
|
+
response = RESTResponse(r)
|
207
|
+
await response.read() # Read data before closing session
|
208
|
+
return response
|
209
|
+
else:
|
210
|
+
return RESTResponse(r)
|
211
|
+
finally:
|
212
|
+
if should_close_session:
|
213
|
+
if self.retry_client is not None:
|
214
|
+
await self.retry_client.close()
|
215
|
+
self.retry_client = None
|
216
|
+
await self.pool_manager.close()
|
217
|
+
self.pool_manager = None
|
crypticorn/auth/main.py
CHANGED
@@ -25,14 +25,17 @@ class AuthClient:
|
|
25
25
|
self,
|
26
26
|
config: Configuration,
|
27
27
|
http_client: Optional[ClientSession] = None,
|
28
|
+
is_sync: bool = False,
|
28
29
|
):
|
29
30
|
self.config = config
|
30
31
|
self.base_client = ApiClient(configuration=self.config)
|
31
32
|
if http_client is not None:
|
32
33
|
self.base_client.rest_client.pool_manager = http_client
|
34
|
+
# Pass sync context to REST client for proper session management
|
35
|
+
self.base_client.rest_client.is_sync = is_sync
|
33
36
|
# Instantiate all the endpoint clients
|
34
|
-
self.admin = AdminApi(self.base_client)
|
35
|
-
self.service = ServiceApi(self.base_client)
|
36
|
-
self.user = UserApi(self.base_client)
|
37
|
-
self.wallet = WalletApi(self.base_client)
|
38
|
-
self.login = AuthApi(self.base_client)
|
37
|
+
self.admin = AdminApi(self.base_client, is_sync=is_sync)
|
38
|
+
self.service = ServiceApi(self.base_client, is_sync=is_sync)
|
39
|
+
self.user = UserApi(self.base_client, is_sync=is_sync)
|
40
|
+
self.wallet = WalletApi(self.base_client, is_sync=is_sync)
|
41
|
+
self.login = AuthApi(self.base_client, is_sync=is_sync)
|
crypticorn/client.py
CHANGED
@@ -1,22 +1,31 @@
|
|
1
1
|
from typing import TypeVar, Optional
|
2
|
+
import asyncio
|
3
|
+
import warnings
|
2
4
|
from aiohttp import ClientSession, ClientTimeout, TCPConnector
|
3
5
|
from crypticorn.hive import HiveClient
|
4
6
|
from crypticorn.klines import KlinesClient
|
5
7
|
from crypticorn.pay import PayClient
|
8
|
+
|
6
9
|
from crypticorn.trade import TradeClient
|
7
10
|
from crypticorn.metrics import MetricsClient
|
8
11
|
from crypticorn.auth import AuthClient
|
9
|
-
from crypticorn.common import
|
12
|
+
from crypticorn.common import (
|
13
|
+
BaseUrl,
|
14
|
+
ApiVersion,
|
15
|
+
Service,
|
16
|
+
apikey_header as aph,
|
17
|
+
CrypticornDeprecatedSince217,
|
18
|
+
)
|
10
19
|
from importlib.metadata import version
|
20
|
+
from typing_extensions import deprecated
|
11
21
|
|
12
22
|
ConfigT = TypeVar("ConfigT")
|
13
23
|
SubClient = TypeVar("SubClient")
|
14
24
|
|
15
25
|
|
16
|
-
class
|
26
|
+
class BaseAsyncClient:
|
17
27
|
"""
|
18
|
-
|
19
|
-
It is consisting of multiple microservices covering the whole stack of the Crypticorn project.
|
28
|
+
Base class for Crypticorn API clients containing shared functionality.
|
20
29
|
"""
|
21
30
|
|
22
31
|
def __init__(
|
@@ -24,20 +33,23 @@ class ApiClient:
|
|
24
33
|
api_key: Optional[str] = None,
|
25
34
|
jwt: Optional[str] = None,
|
26
35
|
base_url: BaseUrl = BaseUrl.PROD,
|
27
|
-
|
36
|
+
is_sync: bool = False,
|
28
37
|
http_client: Optional[ClientSession] = None,
|
29
38
|
):
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
"""
|
40
|
+
:param api_key: The API key to use for authentication (recommended).
|
41
|
+
:param jwt: The JWT to use for authentication (not recommended).
|
42
|
+
:param base_url: The base URL the client will use to connect to the API.
|
43
|
+
:param is_sync: Whether this client should operate in synchronous mode.
|
44
|
+
:param http_client: Optional aiohttp ClientSession to use for HTTP requests.
|
45
|
+
"""
|
46
|
+
self._base_url = base_url
|
47
|
+
self._api_key = api_key
|
48
|
+
self._jwt = jwt
|
49
|
+
self._is_sync = is_sync
|
39
50
|
self._http_client = http_client
|
40
51
|
self._owns_http_client = http_client is None # whether we own the http client
|
52
|
+
|
41
53
|
self._service_classes: dict[Service, type[SubClient]] = {
|
42
54
|
Service.HIVE: HiveClient,
|
43
55
|
Service.TRADE: TradeClient,
|
@@ -46,12 +58,69 @@ class ApiClient:
|
|
46
58
|
Service.METRICS: MetricsClient,
|
47
59
|
Service.AUTH: AuthClient,
|
48
60
|
}
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
}
|
61
|
+
|
62
|
+
self._services: dict[Service, SubClient] = self._create_services()
|
63
|
+
|
64
|
+
def _create_services(self) -> dict[Service, SubClient]:
|
65
|
+
"""Create services with the appropriate configuration based on sync/async mode."""
|
66
|
+
services = {}
|
67
|
+
for service, client_class in self._service_classes.items():
|
68
|
+
config = self._get_default_config(service)
|
69
|
+
# For sync clients, don't pass the persistent http_client
|
70
|
+
# Let each operation manage its own session
|
71
|
+
if self._is_sync:
|
72
|
+
services[service] = client_class(
|
73
|
+
config, http_client=None, is_sync=self._is_sync
|
74
|
+
)
|
75
|
+
else:
|
76
|
+
services[service] = client_class(
|
77
|
+
config, http_client=self._http_client, is_sync=self._is_sync
|
78
|
+
)
|
79
|
+
return services
|
80
|
+
|
81
|
+
@property
|
82
|
+
def base_url(self) -> BaseUrl:
|
83
|
+
"""
|
84
|
+
The base URL the client will use to connect to the API.
|
85
|
+
"""
|
86
|
+
return self._base_url
|
87
|
+
|
88
|
+
@property
|
89
|
+
def api_key(self) -> Optional[str]:
|
90
|
+
"""
|
91
|
+
The API key the client will use to connect to the API.
|
92
|
+
This is the preferred way to authenticate.
|
93
|
+
"""
|
94
|
+
return self._api_key
|
95
|
+
|
96
|
+
@property
|
97
|
+
def jwt(self) -> Optional[str]:
|
98
|
+
"""
|
99
|
+
The JWT the client will use to connect to the API.
|
100
|
+
This is the not the preferred way to authenticate.
|
101
|
+
"""
|
102
|
+
return self._jwt
|
103
|
+
|
104
|
+
@property
|
105
|
+
def version(self) -> str:
|
106
|
+
"""
|
107
|
+
The version of the client.
|
108
|
+
"""
|
109
|
+
return version("crypticorn")
|
110
|
+
|
111
|
+
@property
|
112
|
+
def is_sync(self) -> bool:
|
113
|
+
"""
|
114
|
+
Whether this client operates in synchronous mode.
|
115
|
+
"""
|
116
|
+
return self._is_sync
|
117
|
+
|
118
|
+
@property
|
119
|
+
def http_client(self) -> Optional[ClientSession]:
|
120
|
+
"""
|
121
|
+
The HTTP client session being used, if any.
|
122
|
+
"""
|
123
|
+
return self._http_client
|
55
124
|
|
56
125
|
@property
|
57
126
|
def hive(self) -> HiveClient:
|
@@ -95,17 +164,67 @@ class ApiClient:
|
|
95
164
|
"""
|
96
165
|
return self._services[Service.AUTH]
|
97
166
|
|
167
|
+
def configure(self, config: ConfigT, service: Service) -> None:
|
168
|
+
"""
|
169
|
+
Update a sub-client's configuration by overriding with the values set in the new config.
|
170
|
+
Useful for testing a specific service against a local server instead of the default proxy.
|
171
|
+
|
172
|
+
:param config: The new configuration to use for the sub-client.
|
173
|
+
:param service: The service to configure.
|
174
|
+
|
175
|
+
Example:
|
176
|
+
>>> # For async client
|
177
|
+
>>> async with AsyncClient() as client:
|
178
|
+
... client.configure(config=HiveConfig(host="http://localhost:8000"), service=Service.HIVE)
|
179
|
+
>>>
|
180
|
+
>>> # For sync client
|
181
|
+
>>> with SyncClient() as client:
|
182
|
+
... client.configure(config=HiveConfig(host="http://localhost:8000"), service=Service.HIVE)
|
183
|
+
"""
|
184
|
+
assert Service.validate(service), f"Invalid service: {service}"
|
185
|
+
client = self._services[service]
|
186
|
+
new_config = client.config
|
187
|
+
for attr in vars(config):
|
188
|
+
new_value = getattr(config, attr)
|
189
|
+
if new_value:
|
190
|
+
setattr(new_config, attr, new_value)
|
191
|
+
|
192
|
+
# Recreate service with new config and appropriate parameters
|
193
|
+
if self._is_sync:
|
194
|
+
self._services[service] = type(client)(
|
195
|
+
new_config, is_sync=self._is_sync, http_client=self._http_client
|
196
|
+
)
|
197
|
+
else:
|
198
|
+
self._services[service] = type(client)(
|
199
|
+
new_config, http_client=self._http_client
|
200
|
+
)
|
201
|
+
|
202
|
+
def _get_default_config(self, service, version=None):
|
203
|
+
if version is None:
|
204
|
+
version = ApiVersion.V1
|
205
|
+
config_class = self._service_classes[service].config_class
|
206
|
+
return config_class(
|
207
|
+
host=f"{self.base_url}/{version}/{service}",
|
208
|
+
access_token=self.jwt,
|
209
|
+
api_key={aph.scheme_name: self.api_key} if self.api_key else None,
|
210
|
+
)
|
211
|
+
|
98
212
|
async def close(self):
|
99
|
-
|
213
|
+
"""Close the client and clean up resources."""
|
214
|
+
# close each service
|
100
215
|
for service in self._services.values():
|
101
|
-
if
|
216
|
+
if (
|
217
|
+
hasattr(service, "base_client")
|
218
|
+
and hasattr(service.base_client, "close")
|
219
|
+
and self._owns_http_client
|
220
|
+
):
|
102
221
|
await service.base_client.close()
|
103
|
-
# close shared
|
222
|
+
# close shared http client if we own it
|
104
223
|
if self._http_client and self._owns_http_client:
|
105
224
|
await self._http_client.close()
|
106
225
|
self._http_client = None
|
107
226
|
|
108
|
-
|
227
|
+
def _ensure_session(self) -> None:
|
109
228
|
"""
|
110
229
|
Lazily create the shared HTTP client when first needed and pass it to all subclients.
|
111
230
|
"""
|
@@ -115,48 +234,97 @@ class ApiClient:
|
|
115
234
|
connector=TCPConnector(limit=100, limit_per_host=20),
|
116
235
|
headers={"User-Agent": f"crypticorn/python/{self.version}"},
|
117
236
|
)
|
118
|
-
|
119
|
-
|
120
|
-
service.base_client, "rest_client"
|
121
|
-
):
|
122
|
-
service.base_client.rest_client.pool_manager = self._http_client
|
237
|
+
# Update services to use the new session
|
238
|
+
self._services = self._create_services()
|
123
239
|
|
124
|
-
def _get_default_config(self, service, version=None):
|
125
|
-
if version is None:
|
126
|
-
version = ApiVersion.V1
|
127
|
-
config_class = self._service_classes[service].config_class
|
128
|
-
return config_class(
|
129
|
-
host=f"{self.base_url}/{version}/{service}",
|
130
|
-
access_token=self.jwt,
|
131
|
-
api_key={aph.scheme_name: self.api_key} if self.api_key else None,
|
132
|
-
)
|
133
|
-
|
134
|
-
def configure(self, config: ConfigT, service: Service) -> None:
|
135
|
-
"""
|
136
|
-
Update a sub-client's configuration by overriding with the values set in the new config.
|
137
|
-
Useful for testing a specific service against a local server instead of the default proxy.
|
138
240
|
|
139
|
-
|
140
|
-
|
241
|
+
class AsyncClient(BaseAsyncClient):
|
242
|
+
"""
|
243
|
+
The official async Python client for interacting with the Crypticorn API.
|
244
|
+
It is consisting of multiple microservices covering the whole stack of the Crypticorn project.
|
245
|
+
"""
|
141
246
|
|
142
|
-
|
143
|
-
|
144
|
-
|
247
|
+
def __init__(
|
248
|
+
self,
|
249
|
+
api_key: Optional[str] = None,
|
250
|
+
jwt: Optional[str] = None,
|
251
|
+
base_url: BaseUrl = BaseUrl.PROD,
|
252
|
+
*,
|
253
|
+
http_client: Optional[ClientSession] = None,
|
254
|
+
):
|
145
255
|
"""
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
for
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
)
|
256
|
+
:param api_key: The API key to use for authentication (recommended).
|
257
|
+
:param jwt: The JWT to use for authentication (not recommended).
|
258
|
+
:param base_url: The base URL the client will use to connect to the API.
|
259
|
+
:param http_client: The HTTP client to use for the client.
|
260
|
+
"""
|
261
|
+
# Initialize as async client
|
262
|
+
super().__init__(api_key, jwt, base_url, is_sync=False, http_client=http_client)
|
263
|
+
|
264
|
+
async def close(self):
|
265
|
+
await super().close()
|
156
266
|
|
157
267
|
async def __aenter__(self):
|
158
|
-
|
268
|
+
self._ensure_session()
|
159
269
|
return self
|
160
270
|
|
161
271
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
162
272
|
await self.close()
|
273
|
+
|
274
|
+
|
275
|
+
@deprecated("Use AsyncClient instead", category=None)
|
276
|
+
class ApiClient(AsyncClient):
|
277
|
+
def __init__(self, *args, **kwargs):
|
278
|
+
warnings.warn(
|
279
|
+
"ApiClient is deprecated. Use AsyncClient instead.",
|
280
|
+
CrypticornDeprecatedSince217,
|
281
|
+
)
|
282
|
+
super().__init__(*args, **kwargs)
|
283
|
+
|
284
|
+
|
285
|
+
class SyncClient(BaseAsyncClient):
|
286
|
+
"""
|
287
|
+
The official synchronous Python client for interacting with the Crypticorn API.
|
288
|
+
"""
|
289
|
+
|
290
|
+
def __init__(
|
291
|
+
self,
|
292
|
+
api_key: Optional[str] = None,
|
293
|
+
jwt: Optional[str] = None,
|
294
|
+
base_url: BaseUrl = BaseUrl.PROD,
|
295
|
+
*,
|
296
|
+
http_client: Optional[ClientSession] = None,
|
297
|
+
):
|
298
|
+
"""
|
299
|
+
:param http_client: Optional aiohttp ClientSession to use for HTTP requests.
|
300
|
+
Note: For sync client, session management is handled automatically.
|
301
|
+
"""
|
302
|
+
super().__init__(api_key, jwt, base_url, is_sync=True, http_client=http_client)
|
303
|
+
|
304
|
+
def close(self):
|
305
|
+
"""Close the client and clean up resources."""
|
306
|
+
# For sync client, don't maintain persistent sessions
|
307
|
+
# Each operation creates its own session within async_to_sync
|
308
|
+
self._http_client = None
|
309
|
+
|
310
|
+
def _ensure_session(self) -> None:
|
311
|
+
"""
|
312
|
+
For sync client, don't create persistent sessions.
|
313
|
+
Let each async_to_sync call handle its own session.
|
314
|
+
"""
|
315
|
+
# Don't create persistent sessions in sync mode
|
316
|
+
# Each API call will handle session creation/cleanup within async_to_sync
|
317
|
+
pass
|
318
|
+
|
319
|
+
def __enter__(self):
|
320
|
+
return self
|
321
|
+
|
322
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
323
|
+
self.close()
|
324
|
+
|
325
|
+
def __del__(self):
|
326
|
+
"""Automatic cleanup when the object is garbage collected."""
|
327
|
+
try:
|
328
|
+
self.close()
|
329
|
+
except Exception:
|
330
|
+
pass
|
crypticorn/common/__init__.py
CHANGED
crypticorn/common/metrics.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# metrics/registry.py
|
2
|
-
from prometheus_client import
|
3
|
-
Counter, Gauge, Histogram, Summary, CollectorRegistry
|
4
|
-
)
|
2
|
+
from prometheus_client import Counter, Gauge, Histogram, Summary, CollectorRegistry
|
5
3
|
|
6
4
|
registry = CollectorRegistry()
|
7
5
|
|
@@ -9,12 +7,12 @@ http_requests_total = Counter(
|
|
9
7
|
"http_requests_total",
|
10
8
|
"Total HTTP requests",
|
11
9
|
["method", "endpoint", "status_code"],
|
12
|
-
registry=registry
|
10
|
+
registry=registry,
|
13
11
|
)
|
14
12
|
|
15
13
|
http_request_duration_seconds = Histogram(
|
16
14
|
"http_request_duration_seconds",
|
17
15
|
"HTTP request duration in seconds",
|
18
16
|
["endpoint"],
|
19
|
-
registry=registry
|
20
|
-
)
|
17
|
+
registry=registry,
|
18
|
+
)
|
crypticorn/common/middleware.py
CHANGED
@@ -10,6 +10,7 @@ import warnings
|
|
10
10
|
from crypticorn.common.warnings import CrypticornDeprecatedSince217
|
11
11
|
from crypticorn.common.metrics import http_requests_total, http_request_duration_seconds
|
12
12
|
|
13
|
+
|
13
14
|
class PrometheusMiddleware(BaseHTTPMiddleware):
|
14
15
|
async def dispatch(self, request, call_next):
|
15
16
|
start = time.perf_counter()
|
@@ -19,19 +20,22 @@ class PrometheusMiddleware(BaseHTTPMiddleware):
|
|
19
20
|
http_requests_total.labels(
|
20
21
|
method=request.method,
|
21
22
|
endpoint=request.url.path,
|
22
|
-
status_code=response.status_code
|
23
|
+
status_code=response.status_code,
|
23
24
|
).inc()
|
24
25
|
|
25
|
-
http_request_duration_seconds.labels(
|
26
|
-
|
27
|
-
)
|
26
|
+
http_request_duration_seconds.labels(endpoint=request.url.path).observe(
|
27
|
+
duration
|
28
|
+
)
|
28
29
|
|
29
30
|
return response
|
30
31
|
|
31
32
|
|
32
33
|
@deprecated("Use add_middleware instead", category=None)
|
33
34
|
def add_cors_middleware(app: "FastAPI"):
|
34
|
-
warnings.warn(
|
35
|
+
warnings.warn(
|
36
|
+
"add_cors_middleware is deprecated. Use add_middleware instead.",
|
37
|
+
CrypticornDeprecatedSince217,
|
38
|
+
)
|
35
39
|
app.add_middleware(
|
36
40
|
CORSMiddleware,
|
37
41
|
allow_origins=[
|
@@ -44,6 +48,7 @@ def add_cors_middleware(app: "FastAPI"):
|
|
44
48
|
allow_headers=["*"],
|
45
49
|
)
|
46
50
|
|
51
|
+
|
47
52
|
def add_middleware(app: "FastAPI"):
|
48
53
|
app.add_middleware(
|
49
54
|
CORSMiddleware,
|