py-near 1.1.43__py3-none-any.whl → 1.1.47__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.
py_near/providers.py CHANGED
@@ -5,8 +5,7 @@ import json
5
5
  from collections import Counter
6
6
  from typing import Optional
7
7
 
8
- import aiohttp
9
- from aiohttp import ClientResponseError, ClientConnectorError, ServerDisconnectedError
8
+ import httpx
10
9
  from loguru import logger
11
10
 
12
11
  from py_near.constants import TIMEOUT_WAIT_RPC
@@ -61,10 +60,10 @@ class JsonProvider(object):
61
60
  self._last_rpc_addr_check = 0
62
61
  self.allow_broadcast = allow_broadcast
63
62
  self._timeout = timeout
64
- self.session = aiohttp.ClientSession()
63
+ self._client: httpx.AsyncClient = httpx.AsyncClient()
65
64
 
66
65
  async def shutdown(self):
67
- await self.session.close()
66
+ pass
68
67
 
69
68
  async def check_available_rpcs(self):
70
69
  if (
@@ -81,7 +80,8 @@ class JsonProvider(object):
81
80
  await asyncio.sleep(3)
82
81
 
83
82
  if not self._available_rpcs:
84
- raise RpcNotAvailableError("All RPCs are unavailable")
83
+ self._available_rpcs = self._rpc_addresses.copy()
84
+ logger.error("All RPCs are async, reset to default list")
85
85
 
86
86
  async def _check_available_rpcs(self):
87
87
  available_rpcs = []
@@ -93,32 +93,46 @@ class JsonProvider(object):
93
93
  "params": {"finality": "final"},
94
94
  "id": 1,
95
95
  }
96
- async with aiohttp.ClientSession() as session:
97
- async with session.post(rpc_addr, json=data) as r:
98
- if r.status == 200:
99
- data = json.loads(await r.text())["result"]
100
- if data["sync_info"]["syncing"]:
101
- last_block_ts = datetime.datetime.fromisoformat(
102
- data["sync_info"]["latest_block_time"]
103
- )
104
- diff = (
105
- datetime.datetime.utcnow().timestamp()
106
- - last_block_ts.timestamp()
107
- )
108
- is_syncing = diff > 60
109
- else:
110
- is_syncing = False
111
- if is_syncing:
112
- logger.error(f"Remove async RPC : {rpc_addr} ({diff})")
113
- continue
114
- available_rpcs.append(rpc_addr)
115
- else:
116
- logger.error(
117
- f"Remove rpc because of error {r.status}: {rpc_addr}"
118
- )
96
+ auth_key = "py-near"
97
+ rpc_addr_url = rpc_addr
98
+ if "@" in rpc_addr:
99
+ auth_key = rpc_addr_url.split("//")[1].split("@")[0]
100
+ rpc_addr_url = rpc_addr_url.replace(auth_key + "@", "")
101
+
102
+ r = await self._client.post(
103
+ rpc_addr_url,
104
+ json=data,
105
+ headers={
106
+ "Referer": "https://tgapp.herewallet.app",
107
+ "Authorization": f"Bearer {auth_key}",
108
+ },
109
+ )
110
+ if r.status_code == 200:
111
+ data = json.loads(r.text)["result"]
112
+ diff = 0
113
+ if data["sync_info"]["syncing"]:
114
+ last_block_ts = datetime.datetime.fromisoformat(
115
+ data["sync_info"]["latest_block_time"]
116
+ )
117
+ diff = (
118
+ datetime.datetime.utcnow().timestamp()
119
+ - last_block_ts.timestamp()
120
+ )
121
+ is_syncing = diff > 60
122
+ else:
123
+ is_syncing = False
124
+ if is_syncing:
125
+ logger.error(f"Remove async RPC : {rpc_addr} ({diff})")
126
+ continue
127
+ available_rpcs.append(rpc_addr)
128
+ else:
129
+ logger.error(
130
+ f"Remove rpc because of error {r.status_code}: {rpc_addr}"
131
+ )
119
132
  except Exception as e:
120
133
  if rpc_addr in self._available_rpcs:
121
134
  logger.error(f"Remove rpc: {e}")
135
+ logger.error(f"Rpc check error: {e}")
122
136
  self._available_rpcs = available_rpcs
123
137
 
124
138
  @staticmethod
@@ -138,24 +152,26 @@ class JsonProvider(object):
138
152
  if "@" in rpc_call_addr:
139
153
  auth_key = rpc_call_addr.split("//")[1].split("@")[0]
140
154
  rpc_call_addr = rpc_call_addr.replace(auth_key + "@", "")
141
- async with aiohttp.ClientSession() as session:
142
- r = await session.post(
143
- rpc_call_addr,
144
- json=j,
145
- timeout=self._timeout,
146
- headers={
147
- "Referer": "https://tgapp.herewallet.app",
148
- "Authorization": f"Bearer {auth_key}",
149
- }, # NEAR RPC requires Referer header
150
- )
151
- if r.status == 200:
152
- return json.loads(await r.text())
153
- return {
154
- "error": {
155
- "cause": {"name": "RPC_ERROR", "message": f"Status: {r.status}"},
156
- "data": await r.text(),
157
- }
155
+ r = await self._client.post(
156
+ rpc_call_addr,
157
+ json=j,
158
+ timeout=self._timeout,
159
+ headers={
160
+ "Referer": "https://tgapp.herewallet.app",
161
+ "Authorization": f"Bearer {auth_key}",
162
+ },
163
+ )
164
+ if r.status_code == 200:
165
+ return json.loads(r.text)
166
+ return {
167
+ "error": {
168
+ "cause": {
169
+ "name": "RPC_ERROR",
170
+ "message": f"Status: {r.status_code}",
171
+ },
172
+ "data": r.text,
158
173
  }
174
+ }
159
175
 
160
176
  if broadcast or threshold:
161
177
  pending = [
@@ -163,6 +179,9 @@ class JsonProvider(object):
163
179
  ]
164
180
 
165
181
  responses = []
182
+ correct_responses = []
183
+ result = None
184
+
166
185
  while pending and len(pending):
167
186
  done, pending = await asyncio.wait(
168
187
  pending, return_when=asyncio.FIRST_COMPLETED
@@ -175,17 +194,25 @@ class JsonProvider(object):
175
194
  responses.append(result)
176
195
  except Exception as e:
177
196
  logger.warning(e)
178
- if responses:
197
+ if responses and threshold:
179
198
  array = [hash(json.dumps(x)) for x in responses]
180
199
  most_frequent_element = self.most_frequent_by_hash(array)
181
200
  correct_responses = [
182
- x for x in responses if hash(json.dumps(x)) == most_frequent_element
201
+ x
202
+ for x in responses
203
+ if hash(json.dumps(x)) == most_frequent_element
183
204
  ]
184
205
  if len(correct_responses) >= threshold:
185
206
  for task in pending:
186
207
  task.cancel()
187
- return most_frequent_element
208
+ return correct_responses[0]
209
+ if threshold and threshold > 0:
210
+ raise RpcEmptyResponse(
211
+ f"Threshold not reached: {len(correct_responses)}/{threshold}"
212
+ )
213
+ return result
188
214
  else:
215
+ res = None
189
216
  for rpc_addr in self._available_rpcs:
190
217
  try:
191
218
  res = await f(rpc_addr)
@@ -194,7 +221,7 @@ class JsonProvider(object):
194
221
  except Exception as e:
195
222
  logger.error(f"Rpc error: {e}")
196
223
  continue
197
- raise RpcEmptyResponse("RPC returned empty response")
224
+ return res
198
225
 
199
226
  @staticmethod
200
227
  def get_error_from_response(content: dict):
@@ -299,7 +326,7 @@ class JsonProvider(object):
299
326
 
300
327
  async def get_status(self):
301
328
  await self.check_available_rpcs()
302
- for rpc_addr in self._available_rpcs:
329
+ for rpc_addr in self._available_rpcs.copy():
303
330
  try:
304
331
  data = {
305
332
  "jsonrpc": "2.0",
@@ -307,16 +334,19 @@ class JsonProvider(object):
307
334
  "params": {"finality": "final"},
308
335
  "id": 1,
309
336
  }
310
- async with aiohttp.ClientSession() as session:
311
- async with session.post(rpc_addr, json=data) as r:
312
- if r.status == 200:
313
- return json.loads(await r.text())["result"]
314
- except (
315
- ClientResponseError,
316
- ClientConnectorError,
317
- ServerDisconnectedError,
318
- ConnectionError,
319
- ) as e:
337
+ headers = {
338
+ "Referer": "https://tgapp.herewallet.app",
339
+ }
340
+ if "@" in rpc_addr:
341
+ auth_key = rpc_addr.split("//")[1].split("@")[0]
342
+ rpc_addr = rpc_addr.replace(auth_key + "@", "")
343
+ headers = {
344
+ "Authorization": f"Bearer {auth_key}"
345
+ }
346
+ r = await self._client.post(rpc_addr, json=data, headers=headers)
347
+ if r.status_code == 200:
348
+ return json.loads(r.text)["result"]
349
+ except ConnectionError as e:
320
350
  logger.error(f"Rpc get status error: {e}")
321
351
  except Exception as e:
322
352
  logger.error(e)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: py-near
3
- Version: 1.1.43
3
+ Version: 1.1.47
4
4
  Summary: Pretty simple and fully asynchronous framework for working with NEAR blockchain
5
5
  Author: pvolnov
6
6
  Author-email: petr@herewallet.app
@@ -12,9 +12,9 @@ Classifier: Programming Language :: Python :: 3.9
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
- Requires-Dist: aiohttp (>=3.7.4,<4.0.0)
16
- Requires-Dist: ed25519 (>=1.5,<2.0)
17
- Requires-Dist: py-near-primitives (>=0.2.3,<0.3.0)
15
+ Requires-Dist: ed25519 (==1.5)
16
+ Requires-Dist: httpx (==0.27.0)
17
+ Requires-Dist: py-near-primitives (==0.2.3)
18
18
  Description-Content-Type: text/markdown
19
19
 
20
20
  # py-near
@@ -20,10 +20,10 @@ py_near/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
20
20
  py_near/exceptions/exceptions.py,sha256=DEFipaAHm0y7oCuN2QKzHsiQvUTUQVl-Ce36Ag7n7hs,5509
21
21
  py_near/exceptions/provider.py,sha256=K-wexgjPJ8sw42JePwaP7R5dJEIn9DoFJRvVcURsx6s,7718
22
22
  py_near/models.py,sha256=GZQD1TKGWlwqsJsKRXrVNBjCdAIpk7GQypU-QOtAPFs,11533
23
- py_near/providers.py,sha256=gNYPBWoYIO1Tqj2aPvBlJUrFvUZtCIo6o19o9ibOFxo,15986
23
+ py_near/providers.py,sha256=1eg9MFIYIKA09bZyTdSZ2Umrh0kX1SUFEhY_tN8Cqz4,16722
24
24
  py_near/transactions.py,sha256=QAXegv2JpKISk92NaChtIH6-QPHrcWbrwdKH_lH4TsU,3186
25
25
  py_near/utils.py,sha256=FirRH93ydH1cwjn0-sNrZeIn3BRD6QHedrP2VkAdJ6g,126
26
- py_near-1.1.43.dist-info/LICENSE,sha256=I_GOA9xJ35FiL-KnYXZJdATkbO2KcV2dK2enRGVxzKM,1023
27
- py_near-1.1.43.dist-info/METADATA,sha256=3xDsCOdQv1EbiwaUX4pItm0bcgqm9X5xF_3-ifSUWfM,4713
28
- py_near-1.1.43.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
29
- py_near-1.1.43.dist-info/RECORD,,
26
+ py_near-1.1.47.dist-info/LICENSE,sha256=I_GOA9xJ35FiL-KnYXZJdATkbO2KcV2dK2enRGVxzKM,1023
27
+ py_near-1.1.47.dist-info/METADATA,sha256=Nd8CpvwjXiIyN9WK4JdCs46PgRUExpRNnSXTW6javYE,4693
28
+ py_near-1.1.47.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
29
+ py_near-1.1.47.dist-info/RECORD,,