py-near 1.1.14__py3-none-any.whl → 1.1.16__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/account.py CHANGED
@@ -368,17 +368,22 @@ class Account(object):
368
368
  )
369
369
 
370
370
  async def view_function(
371
- self, contract_id: str, method_name: str, args: dict
371
+ self,
372
+ contract_id: str,
373
+ method_name: str,
374
+ args: dict,
375
+ block_id: Optional[int] = None,
372
376
  ) -> ViewFunctionResult:
373
377
  """
374
378
  Call view function on smart contract. View function is read only function, it can't change state
375
379
  :param contract_id: smart contract account id
376
380
  :param method_name: method name to call
377
381
  :param args: json args to call method
382
+ :param block_id: execution view transaction in block with given id
378
383
  :return: result of view function call
379
384
  """
380
385
  result = await self._provider.view_call(
381
- contract_id, method_name, json.dumps(args).encode("utf8")
386
+ contract_id, method_name, json.dumps(args).encode("utf8"), block_id=block_id
382
387
  )
383
388
  if "error" in result:
384
389
  raise ViewFunctionError(result["error"])
@@ -119,7 +119,7 @@ class InvalidTransactionError(TransactionError):
119
119
 
120
120
 
121
121
  class TxExecutionError(InvalidTransactionError):
122
- def __init__(self, data, error_json=None, **kwargs):
122
+ def __init__(self, data={}, error_json=None, **kwargs):
123
123
  super().__init__(error_json=error_json)
124
124
  if isinstance(data, str):
125
125
  data = json.loads(data)
py_near/providers.py CHANGED
@@ -6,7 +6,7 @@ from typing import Optional
6
6
  import aiohttp
7
7
  from aiohttp import ClientResponseError, ClientConnectorError, ServerDisconnectedError
8
8
  from loguru import logger
9
-
9
+ import datetime
10
10
  from py_near import constants
11
11
  from py_near.constants import TIMEOUT_WAIT_RPC
12
12
  from py_near.exceptions.exceptions import RpcNotAvailableError
@@ -51,21 +51,67 @@ class JsonProvider(object):
51
51
  self._rpc_addresses = rpc_addr
52
52
  else:
53
53
  self._rpc_addresses = [rpc_addr]
54
+ self._available_rpcs = self._rpc_addresses.copy()
55
+ self._last_rpc_addr_check = 0
56
+
57
+ async def check_available_rpcs(self):
58
+ available_rpcs = []
59
+ for rpc_addr in self._rpc_addresses:
60
+ try:
61
+ async with aiohttp.ClientSession() as session:
62
+ timestamp_start = datetime.datetime.utcnow().timestamp()
63
+ r = await session.get(
64
+ "%s/status" % rpc_addr, timeout=TIMEOUT_WAIT_RPC
65
+ )
66
+ if r.status == 200:
67
+ data = json.loads(await r.text())
68
+ if not data["sync_info"]["syncing"]:
69
+ available_rpcs.append(
70
+ (
71
+ rpc_addr,
72
+ datetime.datetime.utcnow().timestamp()
73
+ - timestamp_start,
74
+ )
75
+ )
76
+ continue
77
+ if rpc_addr in self._available_rpcs:
78
+ if r.status == 200:
79
+ logger.error(f"Remove async RPC : {rpc_addr}")
80
+ else:
81
+ logger.error(
82
+ f"Remove rpc because of error {r.status}: {rpc_addr}"
83
+ )
84
+ except Exception as e:
85
+ if rpc_addr in self._available_rpcs:
86
+ logger.error(f"Remove rpc: {e}")
87
+ self._available_rpcs = [
88
+ r[0] for r in sorted(available_rpcs, key=lambda x: x[1])
89
+ ]
54
90
 
55
91
  async def call_rpc_request(self, method, params, timeout=TIMEOUT_WAIT_RPC):
92
+ if (
93
+ self._last_rpc_addr_check < datetime.datetime.now().timestamp() - 30
94
+ or not self._available_rpcs
95
+ ):
96
+ self._last_rpc_addr_check = datetime.datetime.now().timestamp()
97
+ if self._available_rpcs:
98
+ asyncio.create_task(self.check_available_rpcs())
99
+ else:
100
+ logger.warning("No RPC available, rechecking")
101
+ await self.check_available_rpcs()
102
+
103
+ if not self._available_rpcs:
104
+ raise RpcNotAvailableError("No RPC available")
105
+
56
106
  j = {"method": method, "params": params, "id": "dontcare", "jsonrpc": "2.0"}
57
107
 
58
108
  content = None
59
- for rpc_addr in self._rpc_addresses:
109
+ for rpc_addr in self._available_rpcs:
60
110
  try:
61
111
  async with aiohttp.ClientSession() as session:
62
112
  r = await session.post(rpc_addr, json=j, timeout=timeout)
63
113
  r.raise_for_status()
64
114
  content = json.loads(await r.text())
65
- if self._rpc_addresses[0] != rpc_addr:
66
- logger.info(f"Rpc update: {rpc_addr}")
67
- self._rpc_addresses.remove(rpc_addr)
68
- self._rpc_addresses.insert(0, rpc_addr)
69
115
  break
70
116
  except (
71
117
  RPCTimeoutError,
@@ -154,7 +200,7 @@ class JsonProvider(object):
154
200
  raise
155
201
 
156
202
  async def get_status(self):
157
- for rpc_addr in self._rpc_addresses:
203
+ for rpc_addr in self._available_rpcs:
158
204
  try:
159
205
  async with aiohttp.ClientSession() as session:
160
206
  r = await session.get(
@@ -162,11 +208,7 @@ class JsonProvider(object):
162
208
  )
163
209
  if r.status == 200:
164
210
  data = json.loads(await r.text())
165
- if not data["sync_info"][
166
- "syncing"
167
- ]: # RPC is not in syncing process
168
- self._rpc_addresses.remove(rpc_addr)
169
- self._rpc_addresses.insert(0, rpc_addr)
211
+ if not data["sync_info"]["syncing"]:
170
212
  return data
171
213
  except (
172
214
  ClientResponseError,
@@ -224,17 +266,25 @@ class JsonProvider(object):
224
266
  },
225
267
  )
226
268
 
227
- async def view_call(self, account_id, method_name, args, finality="optimistic"):
228
- return await self.json_rpc(
229
- "query",
230
- {
231
- "request_type": "call_function",
232
- "account_id": account_id,
233
- "method_name": method_name,
234
- "args_base64": base64.b64encode(args).decode("utf8"),
235
- "finality": finality,
236
- },
237
- )
269
+ async def view_call(
270
+ self,
271
+ account_id,
272
+ method_name,
273
+ args,
274
+ finality="optimistic",
275
+ block_id: Optional[int] = None,
276
+ ):
277
+ body = {
278
+ "request_type": "call_function",
279
+ "account_id": account_id,
280
+ "method_name": method_name,
281
+ "args_base64": base64.b64encode(args).decode("utf8"),
282
+ }
283
+ if block_id:
284
+ body["block_id"] = block_id
285
+ else:
286
+ body["finality"] = finality
287
+ return await self.json_rpc("query", body)
238
288
 
239
289
  async def get_block(self, block_id):
240
290
  return await self.json_rpc("block", [block_id])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: py-near
3
- Version: 1.1.14
3
+ Version: 1.1.16
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
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.8
11
11
  Classifier: Programming Language :: Python :: 3.9
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
14
15
  Requires-Dist: aiohttp (>=3.7.4,<4.0.0)
15
16
  Requires-Dist: ed25519 (>=1.5,<2.0)
16
17
  Requires-Dist: py-near-primitives (>=0.2.3,<0.3.0)
@@ -1,5 +1,5 @@
1
1
  py_near/__init__.py,sha256=t5fAxjaU8dN8xpQR2vz0ZGhfTkdVy2RCbkhJhZFglk4,50
2
- py_near/account.py,sha256=k-bgn-VV6UIvqTuJpz-hnfn4uFXaabJRNgX--y_25kY,16325
2
+ py_near/account.py,sha256=tMlQCCgI7H40pr1geArrUbQpaT7bP6wROVnFnfdRmG8,16484
3
3
  py_near/constants.py,sha256=inaWIuwmF1EB5JSB0ynnZY5rKY_QsxhF9KuCOhPsM6k,164
4
4
  py_near/dapps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  py_near/dapps/core.py,sha256=LtN9aW2gw2mvEdhzQcQJIidtjv-XL1xjb0LK8DzqtqE,231
@@ -18,12 +18,12 @@ py_near/dapps/staking/exceptions.py,sha256=UjXLFsDQX0vDGS9CGO7HE9XpLD0vovFNUzCb1
18
18
  py_near/dapps/staking/models.py,sha256=zC5M_pc1oMqHq4GaYif1uwFbW6acD2BsiA9rbyiaUTs,124
19
19
  py_near/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  py_near/exceptions/exceptions.py,sha256=qG-aBYAsyCXcdzmZ84n9wVHCLkuWnd16XZTVprJfUVM,5462
21
- py_near/exceptions/provider.py,sha256=RddgWbmwlXxrPuM7KtOjjJgtMR32ds2d3KxlnpMMC-A,7715
21
+ py_near/exceptions/provider.py,sha256=K-wexgjPJ8sw42JePwaP7R5dJEIn9DoFJRvVcURsx6s,7718
22
22
  py_near/models.py,sha256=_JMpwtI6kP6740jaUPN1JRQIIDGXmL7tXLlR-JvRPXc,9417
23
- py_near/providers.py,sha256=W1ttqR-dfl_Ists8FbUL7O5wARX92JgAqIPQ7uGz-7M,9915
23
+ py_near/providers.py,sha256=a70uIFoQeMBL28Dih5tD3WXmrsIDfenQBL_vVW-S_Ic,11807
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.14.dist-info/LICENSE,sha256=I_GOA9xJ35FiL-KnYXZJdATkbO2KcV2dK2enRGVxzKM,1023
27
- py_near-1.1.14.dist-info/METADATA,sha256=dYh9HIxdTvtkZis5EbI--meZhJVl0NAW6XLC3DbQ1pc,4662
28
- py_near-1.1.14.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
29
- py_near-1.1.14.dist-info/RECORD,,
26
+ py_near-1.1.16.dist-info/LICENSE,sha256=I_GOA9xJ35FiL-KnYXZJdATkbO2KcV2dK2enRGVxzKM,1023
27
+ py_near-1.1.16.dist-info/METADATA,sha256=LQnPUz_D_f3z-BkCJnbYTqHconYGsjI29EgLzy5mUoY,4713
28
+ py_near-1.1.16.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
29
+ py_near-1.1.16.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.6.1
2
+ Generator: poetry-core 1.8.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any