crypticorn 2.17.0rc1__py3-none-any.whl → 2.17.0rc3__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.
Files changed (87) hide show
  1. crypticorn/__init__.py +2 -2
  2. crypticorn/auth/client/api/admin_api.py +415 -13
  3. crypticorn/auth/client/api/auth_api.py +2622 -113
  4. crypticorn/auth/client/api/service_api.py +258 -7
  5. crypticorn/auth/client/api/user_api.py +2485 -270
  6. crypticorn/auth/client/api/wallet_api.py +1518 -77
  7. crypticorn/auth/client/models/create_api_key_request.py +2 -1
  8. crypticorn/auth/client/models/get_api_keys200_response_inner.py +2 -1
  9. crypticorn/auth/client/rest.py +23 -4
  10. crypticorn/auth/main.py +8 -5
  11. crypticorn/client.py +227 -59
  12. crypticorn/common/__init__.py +0 -1
  13. crypticorn/common/auth.py +2 -1
  14. crypticorn/common/metrics.py +4 -6
  15. crypticorn/common/middleware.py +10 -5
  16. crypticorn/common/pagination.py +137 -18
  17. crypticorn/common/router/admin_router.py +1 -1
  18. crypticorn/common/utils.py +2 -1
  19. crypticorn/common/warnings.py +1 -0
  20. crypticorn/hive/client/api/admin_api.py +1234 -51
  21. crypticorn/hive/client/api/data_api.py +517 -13
  22. crypticorn/hive/client/api/models_api.py +1657 -83
  23. crypticorn/hive/client/api/status_api.py +415 -13
  24. crypticorn/hive/client/models/api_error_identifier.py +1 -1
  25. crypticorn/hive/client/rest.py +23 -4
  26. crypticorn/hive/main.py +99 -25
  27. crypticorn/klines/client/api/admin_api.py +1234 -51
  28. crypticorn/klines/client/api/change_in_timeframe_api.py +278 -7
  29. crypticorn/klines/client/api/funding_rates_api.py +324 -7
  30. crypticorn/klines/client/api/ohlcv_data_api.py +399 -7
  31. crypticorn/klines/client/api/status_api.py +415 -13
  32. crypticorn/klines/client/api/symbols_api.py +225 -7
  33. crypticorn/klines/client/api/udf_api.py +1393 -120
  34. crypticorn/klines/client/models/api_error_identifier.py +3 -1
  35. crypticorn/klines/client/rest.py +23 -4
  36. crypticorn/klines/main.py +89 -12
  37. crypticorn/metrics/client/api/admin_api.py +1234 -51
  38. crypticorn/metrics/client/api/exchanges_api.py +1405 -140
  39. crypticorn/metrics/client/api/indicators_api.py +640 -13
  40. crypticorn/metrics/client/api/logs_api.py +305 -7
  41. crypticorn/metrics/client/api/marketcap_api.py +1240 -60
  42. crypticorn/metrics/client/api/markets_api.py +352 -7
  43. crypticorn/metrics/client/api/quote_currencies_api.py +237 -7
  44. crypticorn/metrics/client/api/status_api.py +415 -13
  45. crypticorn/metrics/client/api/tokens_api.py +400 -13
  46. crypticorn/metrics/client/configuration.py +4 -2
  47. crypticorn/metrics/client/rest.py +23 -4
  48. crypticorn/metrics/main.py +113 -19
  49. crypticorn/pay/client/api/admin_api.py +1720 -126
  50. crypticorn/pay/client/api/now_payments_api.py +1013 -42
  51. crypticorn/pay/client/api/payments_api.py +580 -13
  52. crypticorn/pay/client/api/products_api.py +915 -25
  53. crypticorn/pay/client/api/status_api.py +415 -13
  54. crypticorn/pay/client/configuration.py +2 -2
  55. crypticorn/pay/client/models/api_error_identifier.py +7 -7
  56. crypticorn/pay/client/models/scope.py +1 -0
  57. crypticorn/pay/client/rest.py +23 -4
  58. crypticorn/pay/main.py +10 -6
  59. crypticorn/trade/client/__init__.py +2 -1
  60. crypticorn/trade/client/api/__init__.py +0 -1
  61. crypticorn/trade/client/api/admin_api.py +1718 -123
  62. crypticorn/trade/client/api/api_keys_api.py +1596 -103
  63. crypticorn/trade/client/api/bots_api.py +1106 -47
  64. crypticorn/trade/client/api/exchanges_api.py +592 -19
  65. crypticorn/trade/client/api/notifications_api.py +1340 -112
  66. crypticorn/trade/client/api/orders_api.py +240 -7
  67. crypticorn/trade/client/api/status_api.py +415 -13
  68. crypticorn/trade/client/api/strategies_api.py +1170 -69
  69. crypticorn/trade/client/api/trading_actions_api.py +650 -19
  70. crypticorn/trade/client/models/__init__.py +2 -0
  71. crypticorn/trade/client/models/exchange.py +6 -1
  72. crypticorn/trade/client/models/exchange_key_balance.py +111 -0
  73. crypticorn/trade/client/models/futures_balance.py +27 -25
  74. crypticorn/trade/client/models/spot_balance.py +110 -0
  75. crypticorn/trade/client/models/strategy.py +5 -3
  76. crypticorn/trade/client/models/strategy_create.py +6 -4
  77. crypticorn/trade/client/models/strategy_exchange_info.py +16 -4
  78. crypticorn/trade/client/models/strategy_update.py +2 -2
  79. crypticorn/trade/client/rest.py +23 -4
  80. crypticorn/trade/main.py +15 -12
  81. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/METADATA +64 -20
  82. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/RECORD +86 -85
  83. crypticorn/trade/client/api/futures_trading_panel_api.py +0 -1285
  84. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/WHEEL +0 -0
  85. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/entry_points.txt +0 -0
  86. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/licenses/LICENSE +0 -0
  87. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.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
 
@@ -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
- # https pool manager
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
- r = await pool_manager.request(**args)
197
-
198
- return RESTResponse(r)
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 BaseUrl, ApiVersion, Service, apikey_header as aph
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 ApiClient:
26
+ class BaseAsyncClient:
17
27
  """
18
- The official Python client for interacting with the Crypticorn API.
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
- self.base_url = base_url
31
- """The base URL the client will use to connect to the API."""
32
- self.api_key = api_key
33
- """The API key to use for authentication (recommended)."""
34
- self.jwt = jwt
35
- """The JWT to use for authentication (not recommended)."""
36
- self.version = version("crypticorn")
37
- """The version of the client."""
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
- self._services: dict[Service, SubClient] = {
50
- service: client_class(
51
- self._get_default_config(service), http_client=self._http_client
52
- )
53
- for service, client_class in self._service_classes.items()
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
- # close each in sync
213
+ """Close the client and clean up resources."""
214
+ # close each service
100
215
  for service in self._services.values():
101
- if hasattr(service.base_client, "close") and self._owns_http_client:
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 in async
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
- async def _ensure_session(self) -> None:
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
- for service in self._services.values():
119
- if hasattr(service, "base_client") and hasattr(
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
- :param config: The new configuration to use for the sub-client.
140
- :param service: The service to configure.
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
- Example:
143
- >>> async with ApiClient() as client:
144
- ... client.configure(config=HiveConfig(host="http://localhost:8000"), service=Service.HIVE)
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
- assert Service.validate(service), f"Invalid service: {service}"
147
- client = self._services[service]
148
- new_config = client.config
149
- for attr in vars(config):
150
- new_value = getattr(config, attr)
151
- if new_value:
152
- setattr(new_config, attr, new_value)
153
- self._services[service] = type(client)(
154
- new_config, http_client=self._http_client
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
- await self._ensure_session()
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
@@ -16,4 +16,3 @@ from crypticorn.common.openapi import *
16
16
  from crypticorn.common.metrics import *
17
17
  from crypticorn.common.router.status_router import router as status_router
18
18
  from crypticorn.common.router.admin_router import router as admin_router
19
-
crypticorn/common/auth.py CHANGED
@@ -250,7 +250,8 @@ class AuthHandler:
250
250
  This function is used for WebSocket connections.
251
251
  """
252
252
  try:
253
- return await self.bearer_auth(bearer=bearer, sec=sec)
253
+ credentials = HTTPAuthorizationCredentials(scheme="Bearer", credentials=bearer)
254
+ return await self.bearer_auth(bearer=credentials, sec=sec)
254
255
  except HTTPException as e:
255
256
  raise WebSocketException.from_http_exception(e)
256
257
 
@@ -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
+ )
@@ -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
- endpoint=request.url.path
27
- ).observe(duration)
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("add_cors_middleware is deprecated. Use add_middleware instead.", CrypticornDeprecatedSince217)
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,