pytonapi 2.0.0__tar.gz → 2.0.1__tar.gz
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.
- {pytonapi-2.0.0/pytonapi.egg-info → pytonapi-2.0.1}/PKG-INFO +20 -24
- pytonapi-2.0.1/README.md +64 -0
- pytonapi-2.0.1/pyproject.toml +155 -0
- pytonapi-2.0.1/pytonapi/__init__.py +1 -0
- pytonapi-2.0.0/pytonapi/__init__.py → pytonapi-2.0.1/pytonapi/__meta__.py +3 -8
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/cli.py +1 -2
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/client.py +45 -21
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/exceptions.py +8 -9
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/client.py +10 -10
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/limiter.py +1 -2
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/mixin.py +19 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/__init__.py +7 -16
- pytonapi-2.0.1/pytonapi/rest/models/accounts.py +465 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/blockchain.py +114 -114
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/dns.py +12 -12
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/emulation.py +1 -1
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/events.py +5 -5
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/gasless.py +5 -5
- pytonapi-2.0.1/pytonapi/rest/models/jettons.py +60 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/multisig.py +9 -9
- pytonapi-2.0.1/pytonapi/rest/models/nft.py +76 -0
- pytonapi-2.0.1/pytonapi/rest/models/purchases.py +26 -0
- pytonapi-2.0.1/pytonapi/rest/models/rates.py +20 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/staking.py +4 -6
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/traces.py +3 -3
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/wallet.py +10 -10
- pytonapi-2.0.1/pytonapi/rest/resources/_base.py +72 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/accounts.py +64 -82
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/blockchain.py +30 -56
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/connect.py +7 -7
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/dns.py +7 -11
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/emulation.py +20 -31
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/events.py +8 -14
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/extra_currency.py +3 -2
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/gasless.py +8 -9
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/jettons.py +11 -21
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/lite_server.py +55 -69
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/multisig.py +4 -4
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/nft.py +19 -32
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/purchases.py +4 -5
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/rates.py +16 -18
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/staking.py +17 -19
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/storage.py +5 -4
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/traces.py +6 -6
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/utilities.py +10 -12
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/wallet.py +16 -27
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/streaming/client.py +13 -8
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/streaming/models.py +2 -3
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/streaming/sse.py +23 -23
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/streaming/ws.py +23 -23
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/types.py +8 -5
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/utils.py +2 -3
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/webhook/client.py +15 -12
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/webhook/dispatcher.py +35 -37
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/webhook/models.py +3 -3
- {pytonapi-2.0.0 → pytonapi-2.0.1/pytonapi.egg-info}/PKG-INFO +20 -24
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi.egg-info/SOURCES.txt +1 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/tests/test_utils.py +1 -1
- pytonapi-2.0.0/README.md +0 -68
- pytonapi-2.0.0/pyproject.toml +0 -69
- pytonapi-2.0.0/pytonapi/rest/models/accounts.py +0 -486
- pytonapi-2.0.0/pytonapi/rest/models/jettons.py +0 -55
- pytonapi-2.0.0/pytonapi/rest/models/nft.py +0 -75
- pytonapi-2.0.0/pytonapi/rest/models/purchases.py +0 -43
- pytonapi-2.0.0/pytonapi/rest/models/rates.py +0 -22
- pytonapi-2.0.0/pytonapi/rest/resources/_base.py +0 -46
- {pytonapi-2.0.0 → pytonapi-2.0.1}/LICENSE +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/py.typed +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/__init__.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/_enums.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/connect.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/extra_currency.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/lite_server.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/storage.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/models/utilities.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/rest/resources/__init__.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/streaming/__init__.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi/webhook/__init__.py +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi.egg-info/dependency_links.txt +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi.egg-info/entry_points.txt +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi.egg-info/requires.txt +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/pytonapi.egg-info/top_level.txt +0 -0
- {pytonapi-2.0.0 → pytonapi-2.0.1}/setup.cfg +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytonapi
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: Python SDK for TONAPI — REST API, streaming, and webhooks for TON blockchain.
|
|
5
5
|
Author: nessshon
|
|
6
6
|
Maintainer: nessshon
|
|
7
7
|
License-Expression: MIT
|
|
8
|
-
Project-URL: Homepage, https://github.com/nessshon/
|
|
9
|
-
Project-URL: Examples, https://github.com/nessshon/
|
|
8
|
+
Project-URL: Homepage, https://github.com/nessshon/tonapi/
|
|
9
|
+
Project-URL: Examples, https://github.com/nessshon/tonapi/tree/main/examples/
|
|
10
10
|
Keywords: AsyncIO,REST API,SDK,TON,TON blockchain,TONAPI,The Open Network,blockchain,crypto,streaming,webhooks
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: Environment :: Console
|
|
@@ -32,10 +32,10 @@ Dynamic: license-file
|
|
|
32
32
|
[](https://ton.org)
|
|
33
33
|

|
|
34
34
|
[](https://pypi.python.org/pypi/pytonapi)
|
|
35
|
-
[](https://github.com/nessshon/tonapi/blob/main/LICENSE)
|
|
36
36
|
[](https://tonviewer.com/UQCZq3_Vd21-4y4m7Wc-ej9NFOhh_qvdfAkAYAOHoQ__Ness)
|
|
37
37
|
|
|
38
|
-

|
|
38
|
+

|
|
39
39
|
|
|
40
40
|

|
|
41
41
|

|
|
@@ -43,8 +43,8 @@ Dynamic: license-file
|
|
|
43
43
|
|
|
44
44
|
### Python SDK for [TONAPI](https://tonapi.io)
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
Access TON blockchain data via REST API, real-time streaming, and webhooks.
|
|
47
|
+
API key required — obtain at [tonconsole.com](https://tonconsole.com/), docs
|
|
48
48
|
at [docs.tonconsole.com](https://docs.tonconsole.com/).
|
|
49
49
|
|
|
50
50
|
> For creating wallets, transferring TON, jettons, etc., use [tonutils](https://github.com/nessshon/tonutils).
|
|
@@ -55,8 +55,8 @@ at [docs.tonconsole.com](https://docs.tonconsole.com/).
|
|
|
55
55
|
- **Streaming** — real-time events via SSE and WebSocket
|
|
56
56
|
- **Webhooks** — push notifications with event dispatcher
|
|
57
57
|
|
|
58
|
-
>
|
|
59
|
-
>
|
|
58
|
+
> Support this project — TON: `donate.ness.ton`
|
|
59
|
+
> `UQCZq3_Vd21-4y4m7Wc-ej9NFOhh_qvdfAkAYAOHoQ__Ness`
|
|
60
60
|
|
|
61
61
|
## Installation
|
|
62
62
|
|
|
@@ -68,30 +68,26 @@ pip install pytonapi
|
|
|
68
68
|
|
|
69
69
|
**REST API**
|
|
70
70
|
|
|
71
|
-
- [Get account info](examples/get_account_info.py)
|
|
72
|
-
- [Get account transactions](examples/get_account_transactions.py)
|
|
73
|
-
- [Get NFTs by owner](examples/get_nft_by_owner.py)
|
|
74
|
-
- [Get NFTs by collection](examples/get_nft_by_collection.py)
|
|
71
|
+
- [Get account info](https://github.com/nessshon/tonapi/blob/main/examples/get_account_info.py)
|
|
72
|
+
- [Get account transactions](https://github.com/nessshon/tonapi/blob/main/examples/get_account_transactions.py)
|
|
73
|
+
- [Get NFTs by owner](https://github.com/nessshon/tonapi/blob/main/examples/get_nft_by_owner.py)
|
|
74
|
+
- [Get NFTs by collection](https://github.com/nessshon/tonapi/blob/main/examples/get_nft_by_collection.py)
|
|
75
75
|
|
|
76
76
|
**Streaming** (SSE & WebSocket)
|
|
77
77
|
|
|
78
|
-
- [SSE subscriptions](examples/streaming_sse.py)
|
|
79
|
-
- [WebSocket subscriptions](examples/streaming_websocket.py)
|
|
78
|
+
- [SSE subscriptions](https://github.com/nessshon/tonapi/blob/main/examples/streaming_sse.py)
|
|
79
|
+
- [WebSocket subscriptions](https://github.com/nessshon/tonapi/blob/main/examples/streaming_websocket.py)
|
|
80
80
|
|
|
81
81
|
**Webhooks**
|
|
82
82
|
|
|
83
|
-
- [FastAPI webhook server](examples/webhook_fastapi.py)
|
|
84
|
-
- [aiohttp webhook server](examples/webhook_aiohttp.py)
|
|
83
|
+
- [FastAPI webhook server](https://github.com/nessshon/tonapi/blob/main/examples/webhook_fastapi.py)
|
|
84
|
+
- [aiohttp webhook server](https://github.com/nessshon/tonapi/blob/main/examples/webhook_aiohttp.py)
|
|
85
85
|
|
|
86
86
|
**Transfers** (requires [tonutils](https://github.com/nessshon/tonutils))
|
|
87
87
|
|
|
88
|
-
- [Transfer TON](examples/transfer_ton.py)
|
|
89
|
-
- [Gasless transfer](examples/transfer_gasless.py)
|
|
90
|
-
|
|
91
|
-
## Support
|
|
92
|
-
|
|
93
|
-
Supported by [TON Society](https://github.com/ton-society/grants-and-bounties) and [TONAPI](https://tonapi.io).
|
|
88
|
+
- [Transfer TON](https://github.com/nessshon/tonapi/blob/main/examples/transfer_ton.py)
|
|
89
|
+
- [Gasless transfer](https://github.com/nessshon/tonapi/blob/main/examples/transfer_gasless.py)
|
|
94
90
|
|
|
95
91
|
## License
|
|
96
92
|
|
|
97
|
-
This repository is distributed under the [MIT License](LICENSE).
|
|
93
|
+
This repository is distributed under the [MIT License](https://github.com/nessshon/tonapi/blob/main/LICENSE).
|
pytonapi-2.0.1/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# 📦 PyTONAPI
|
|
2
|
+
|
|
3
|
+
[](https://ton.org)
|
|
4
|
+

|
|
5
|
+
[](https://pypi.python.org/pypi/pytonapi)
|
|
6
|
+
[](https://github.com/nessshon/tonapi/blob/main/LICENSE)
|
|
7
|
+
[](https://tonviewer.com/UQCZq3_Vd21-4y4m7Wc-ej9NFOhh_qvdfAkAYAOHoQ__Ness)
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+

|
|
12
|
+

|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
### Python SDK for [TONAPI](https://tonapi.io)
|
|
16
|
+
|
|
17
|
+
Access TON blockchain data via REST API, real-time streaming, and webhooks.
|
|
18
|
+
API key required — obtain at [tonconsole.com](https://tonconsole.com/), docs
|
|
19
|
+
at [docs.tonconsole.com](https://docs.tonconsole.com/).
|
|
20
|
+
|
|
21
|
+
> For creating wallets, transferring TON, jettons, etc., use [tonutils](https://github.com/nessshon/tonutils).
|
|
22
|
+
|
|
23
|
+
**Features**
|
|
24
|
+
|
|
25
|
+
- **REST API** — accounts, NFTs, jettons, DNS, and more
|
|
26
|
+
- **Streaming** — real-time events via SSE and WebSocket
|
|
27
|
+
- **Webhooks** — push notifications with event dispatcher
|
|
28
|
+
|
|
29
|
+
> Support this project — TON: `donate.ness.ton`
|
|
30
|
+
> `UQCZq3_Vd21-4y4m7Wc-ej9NFOhh_qvdfAkAYAOHoQ__Ness`
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install pytonapi
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
|
|
40
|
+
**REST API**
|
|
41
|
+
|
|
42
|
+
- [Get account info](https://github.com/nessshon/tonapi/blob/main/examples/get_account_info.py)
|
|
43
|
+
- [Get account transactions](https://github.com/nessshon/tonapi/blob/main/examples/get_account_transactions.py)
|
|
44
|
+
- [Get NFTs by owner](https://github.com/nessshon/tonapi/blob/main/examples/get_nft_by_owner.py)
|
|
45
|
+
- [Get NFTs by collection](https://github.com/nessshon/tonapi/blob/main/examples/get_nft_by_collection.py)
|
|
46
|
+
|
|
47
|
+
**Streaming** (SSE & WebSocket)
|
|
48
|
+
|
|
49
|
+
- [SSE subscriptions](https://github.com/nessshon/tonapi/blob/main/examples/streaming_sse.py)
|
|
50
|
+
- [WebSocket subscriptions](https://github.com/nessshon/tonapi/blob/main/examples/streaming_websocket.py)
|
|
51
|
+
|
|
52
|
+
**Webhooks**
|
|
53
|
+
|
|
54
|
+
- [FastAPI webhook server](https://github.com/nessshon/tonapi/blob/main/examples/webhook_fastapi.py)
|
|
55
|
+
- [aiohttp webhook server](https://github.com/nessshon/tonapi/blob/main/examples/webhook_aiohttp.py)
|
|
56
|
+
|
|
57
|
+
**Transfers** (requires [tonutils](https://github.com/nessshon/tonutils))
|
|
58
|
+
|
|
59
|
+
- [Transfer TON](https://github.com/nessshon/tonapi/blob/main/examples/transfer_ton.py)
|
|
60
|
+
- [Gasless transfer](https://github.com/nessshon/tonapi/blob/main/examples/transfer_gasless.py)
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
This repository is distributed under the [MIT License](https://github.com/nessshon/tonapi/blob/main/LICENSE).
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=80"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pytonapi"
|
|
7
|
+
description = "Python SDK for TONAPI — REST API, streaming, and webhooks for TON blockchain."
|
|
8
|
+
requires-python = ">=3.10,<3.15"
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "nessshon" },
|
|
11
|
+
]
|
|
12
|
+
maintainers = [
|
|
13
|
+
{ name = "nessshon" },
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"aiohttp>=3.9.0",
|
|
17
|
+
"pydantic>=2.4.1,<3.0",
|
|
18
|
+
]
|
|
19
|
+
keywords = [
|
|
20
|
+
"AsyncIO",
|
|
21
|
+
"REST API",
|
|
22
|
+
"SDK",
|
|
23
|
+
"TON",
|
|
24
|
+
"TON blockchain",
|
|
25
|
+
"TONAPI",
|
|
26
|
+
"The Open Network",
|
|
27
|
+
"blockchain",
|
|
28
|
+
"crypto",
|
|
29
|
+
"streaming",
|
|
30
|
+
"webhooks",
|
|
31
|
+
]
|
|
32
|
+
classifiers = [
|
|
33
|
+
"Development Status :: 4 - Beta",
|
|
34
|
+
"Environment :: Console",
|
|
35
|
+
"Framework :: AsyncIO",
|
|
36
|
+
"Intended Audience :: Developers",
|
|
37
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
38
|
+
"Operating System :: OS Independent",
|
|
39
|
+
"Programming Language :: Python :: 3.10",
|
|
40
|
+
"Programming Language :: Python :: 3.11",
|
|
41
|
+
"Programming Language :: Python :: 3.12",
|
|
42
|
+
"Programming Language :: Python :: 3.13",
|
|
43
|
+
"Programming Language :: Python :: 3.14",
|
|
44
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
45
|
+
]
|
|
46
|
+
dynamic = ["version"]
|
|
47
|
+
readme = "README.md"
|
|
48
|
+
license = "MIT"
|
|
49
|
+
|
|
50
|
+
[project.scripts]
|
|
51
|
+
pytonapi = "pytonapi.cli:main"
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Homepage = "https://github.com/nessshon/tonapi/"
|
|
55
|
+
Examples = "https://github.com/nessshon/tonapi/tree/main/examples/"
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.packages.find]
|
|
58
|
+
include = ["pytonapi", "pytonapi.*"]
|
|
59
|
+
|
|
60
|
+
[tool.setuptools.package-data]
|
|
61
|
+
pytonapi = ["py.typed"]
|
|
62
|
+
|
|
63
|
+
[tool.setuptools.dynamic]
|
|
64
|
+
version = { attr = "pytonapi.__meta__.__version__" }
|
|
65
|
+
|
|
66
|
+
[tool.ruff]
|
|
67
|
+
target-version = "py310"
|
|
68
|
+
line-length = 120
|
|
69
|
+
|
|
70
|
+
[tool.ruff.lint]
|
|
71
|
+
select = [
|
|
72
|
+
# pydocstyle
|
|
73
|
+
"D",
|
|
74
|
+
# pycodestyle
|
|
75
|
+
"E",
|
|
76
|
+
"W",
|
|
77
|
+
# pyflakes
|
|
78
|
+
"F",
|
|
79
|
+
# isort
|
|
80
|
+
"I",
|
|
81
|
+
# flake8-bugbear
|
|
82
|
+
"B",
|
|
83
|
+
# flake8-comprehensions
|
|
84
|
+
"C4",
|
|
85
|
+
# pyupgrade
|
|
86
|
+
"UP",
|
|
87
|
+
# flake8-simplify
|
|
88
|
+
"SIM",
|
|
89
|
+
# type-checking imports
|
|
90
|
+
"TCH",
|
|
91
|
+
# perflint
|
|
92
|
+
"PERF",
|
|
93
|
+
# flake8-debugger
|
|
94
|
+
"T10",
|
|
95
|
+
# flake8-print
|
|
96
|
+
"T20",
|
|
97
|
+
# flake8-pie
|
|
98
|
+
"PIE",
|
|
99
|
+
# pylint errors
|
|
100
|
+
"PLE",
|
|
101
|
+
# flake8-return
|
|
102
|
+
"RET",
|
|
103
|
+
# ruff-specific
|
|
104
|
+
"RUF",
|
|
105
|
+
]
|
|
106
|
+
ignore = [
|
|
107
|
+
# line too long
|
|
108
|
+
"E501",
|
|
109
|
+
# docstring in public module
|
|
110
|
+
"D100",
|
|
111
|
+
# docstring in public package
|
|
112
|
+
"D104",
|
|
113
|
+
# docstring in magic method
|
|
114
|
+
"D105",
|
|
115
|
+
# docstring in __init__
|
|
116
|
+
"D107",
|
|
117
|
+
# blank line before class docstring (conflicts D211)
|
|
118
|
+
"D203",
|
|
119
|
+
# multi-line summary second line (conflicts D212)
|
|
120
|
+
"D213",
|
|
121
|
+
# first word capitalization (false positive for proper nouns like dApp)
|
|
122
|
+
"D403",
|
|
123
|
+
# type-checking import (false positive for Pydantic models)
|
|
124
|
+
"TC001",
|
|
125
|
+
"TC002",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
[tool.ruff.lint.per-file-ignores]
|
|
129
|
+
"__init__.py" = ["F401"]
|
|
130
|
+
"pytonapi/rest/models/*" = ["D101"]
|
|
131
|
+
"tests/*" = ["D101", "D102", "T20"]
|
|
132
|
+
"codegen/*" = ["T20"]
|
|
133
|
+
"examples/*" = ["D", "T20", "RUF006", "RUF059"]
|
|
134
|
+
|
|
135
|
+
[tool.ruff.lint.isort]
|
|
136
|
+
known-first-party = ["pytonapi"]
|
|
137
|
+
|
|
138
|
+
[tool.ruff.format]
|
|
139
|
+
quote-style = "double"
|
|
140
|
+
|
|
141
|
+
[tool.mypy]
|
|
142
|
+
plugins = ["pydantic.mypy"]
|
|
143
|
+
python_version = "3.10"
|
|
144
|
+
strict = true
|
|
145
|
+
pretty = true
|
|
146
|
+
show_error_codes = true
|
|
147
|
+
show_error_context = true
|
|
148
|
+
namespace_packages = true
|
|
149
|
+
follow_imports_for_stubs = true
|
|
150
|
+
warn_unreachable = true
|
|
151
|
+
enable_error_code = [
|
|
152
|
+
"ignore-without-code",
|
|
153
|
+
"redundant-cast",
|
|
154
|
+
"truthy-bool",
|
|
155
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
# This source code is licensed under the MIT License found in the
|
|
4
4
|
# LICENSE file in the root directory of this source tree.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"__version__",
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
__version__ = "2.0.0"
|
|
13
|
-
__uri__ = "https://github.com/nessshon/pytonapi"
|
|
6
|
+
__version__ = "2.0.1"
|
|
7
|
+
__author__ = "nessshon"
|
|
8
|
+
__url__ = "https://github.com/nessshon/tonapi"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
|
|
3
|
-
from pytonapi import __version__
|
|
3
|
+
from pytonapi.__meta__ import __version__
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def main() -> None:
|
|
@@ -16,7 +16,6 @@ def main() -> None:
|
|
|
16
16
|
version=f"pytonapi {__version__}",
|
|
17
17
|
)
|
|
18
18
|
parser.parse_args()
|
|
19
|
-
parser.print_help()
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
if __name__ == "__main__":
|
|
@@ -32,10 +32,10 @@ class BaseClient:
|
|
|
32
32
|
base_url: str,
|
|
33
33
|
*,
|
|
34
34
|
timeout: float = 10.0,
|
|
35
|
-
session:
|
|
36
|
-
headers:
|
|
37
|
-
cookies:
|
|
38
|
-
retry_policy:
|
|
35
|
+
session: aiohttp.ClientSession | None = None,
|
|
36
|
+
headers: dict[str, str] | None = None,
|
|
37
|
+
cookies: dict[str, str] | None = None,
|
|
38
|
+
retry_policy: RetryPolicy | None = DEFAULT_RETRY_POLICY,
|
|
39
39
|
) -> None:
|
|
40
40
|
"""Initialize the base HTTP client.
|
|
41
41
|
|
|
@@ -55,7 +55,7 @@ class BaseClient:
|
|
|
55
55
|
self._headers = headers or {}
|
|
56
56
|
self._cookies = cookies or {}
|
|
57
57
|
self._timeout = aiohttp.ClientTimeout(total=timeout)
|
|
58
|
-
self._session:
|
|
58
|
+
self._session: aiohttp.ClientSession | None = session
|
|
59
59
|
|
|
60
60
|
self._is_external_session = session is not None
|
|
61
61
|
self._retry_policy = retry_policy
|
|
@@ -94,14 +94,14 @@ class BaseClient:
|
|
|
94
94
|
|
|
95
95
|
async def __aexit__(
|
|
96
96
|
self,
|
|
97
|
-
exc_type:
|
|
98
|
-
exc_val:
|
|
99
|
-
exc_tb: t.
|
|
97
|
+
exc_type: type[BaseException] | None,
|
|
98
|
+
exc_val: BaseException | None,
|
|
99
|
+
exc_tb: t.Any | None,
|
|
100
100
|
) -> None:
|
|
101
101
|
"""Exit the async context manager."""
|
|
102
102
|
await self.close_session()
|
|
103
103
|
|
|
104
|
-
def _build_headers(self) ->
|
|
104
|
+
def _build_headers(self) -> dict[str, str]:
|
|
105
105
|
"""Build default request headers.
|
|
106
106
|
|
|
107
107
|
:return: Merged headers dict.
|
|
@@ -114,7 +114,7 @@ class BaseClient:
|
|
|
114
114
|
return base
|
|
115
115
|
|
|
116
116
|
@staticmethod
|
|
117
|
-
def _parse_body(text: str) ->
|
|
117
|
+
def _parse_body(text: str) -> tuple[t.Any, str]:
|
|
118
118
|
"""Parse response text and extract error message.
|
|
119
119
|
|
|
120
120
|
:param text: Raw response text.
|
|
@@ -122,7 +122,7 @@ class BaseClient:
|
|
|
122
122
|
"""
|
|
123
123
|
try:
|
|
124
124
|
data = json.loads(text)
|
|
125
|
-
except
|
|
125
|
+
except json.JSONDecodeError:
|
|
126
126
|
return None, text
|
|
127
127
|
|
|
128
128
|
if isinstance(data, dict):
|
|
@@ -133,16 +133,40 @@ class BaseClient:
|
|
|
133
133
|
|
|
134
134
|
return data, str(data)
|
|
135
135
|
|
|
136
|
+
@t.overload
|
|
136
137
|
async def request(
|
|
137
138
|
self,
|
|
138
139
|
method: str,
|
|
139
140
|
path: str,
|
|
140
141
|
*,
|
|
141
|
-
params:
|
|
142
|
-
body: t.
|
|
143
|
-
headers:
|
|
144
|
-
response_model:
|
|
145
|
-
) ->
|
|
142
|
+
params: dict[str, t.Any] | None = None,
|
|
143
|
+
body: t.Any | None = None,
|
|
144
|
+
headers: dict[str, t.Any] | None = None,
|
|
145
|
+
response_model: type[T],
|
|
146
|
+
) -> T: ...
|
|
147
|
+
|
|
148
|
+
@t.overload
|
|
149
|
+
async def request(
|
|
150
|
+
self,
|
|
151
|
+
method: str,
|
|
152
|
+
path: str,
|
|
153
|
+
*,
|
|
154
|
+
params: dict[str, t.Any] | None = None,
|
|
155
|
+
body: t.Any | None = None,
|
|
156
|
+
headers: dict[str, t.Any] | None = None,
|
|
157
|
+
response_model: None = None,
|
|
158
|
+
) -> t.Any: ...
|
|
159
|
+
|
|
160
|
+
async def request(
|
|
161
|
+
self,
|
|
162
|
+
method: str,
|
|
163
|
+
path: str,
|
|
164
|
+
*,
|
|
165
|
+
params: dict[str, t.Any] | None = None,
|
|
166
|
+
body: t.Any | None = None,
|
|
167
|
+
headers: dict[str, t.Any] | None = None,
|
|
168
|
+
response_model: type[T] | None = None,
|
|
169
|
+
) -> T | t.Any:
|
|
146
170
|
"""Execute an HTTP request with retry.
|
|
147
171
|
|
|
148
172
|
:param method: HTTP method (``GET``, ``POST``, etc.).
|
|
@@ -167,8 +191,8 @@ class BaseClient:
|
|
|
167
191
|
raise TONAPISessionNotCreatedError(self.__class__.__name__)
|
|
168
192
|
|
|
169
193
|
session = self._session
|
|
170
|
-
last_error:
|
|
171
|
-
last_status:
|
|
194
|
+
last_error: Exception | None = None
|
|
195
|
+
last_status: int | None = None
|
|
172
196
|
max_attempts = 1
|
|
173
197
|
|
|
174
198
|
if self._retry_policy:
|
|
@@ -207,9 +231,9 @@ class BaseClient:
|
|
|
207
231
|
content_type = response.headers.get("Content-Type", "")
|
|
208
232
|
raise_for_status(response.status, text, content_type)
|
|
209
233
|
|
|
210
|
-
except
|
|
234
|
+
except TONAPIError:
|
|
211
235
|
raise
|
|
212
|
-
except
|
|
236
|
+
except aiohttp.ClientError as exc:
|
|
213
237
|
last_error = exc
|
|
214
238
|
if attempt < max_attempts - 1:
|
|
215
239
|
await asyncio.sleep(1.0)
|
|
@@ -227,7 +251,7 @@ class BaseClient:
|
|
|
227
251
|
async def _handle_success(
|
|
228
252
|
self,
|
|
229
253
|
response: aiohttp.ClientResponse,
|
|
230
|
-
response_model:
|
|
254
|
+
response_model: type[T] | None,
|
|
231
255
|
) -> t.Any:
|
|
232
256
|
"""Process a successful (2xx) response.
|
|
233
257
|
|
|
@@ -34,8 +34,7 @@ class TONAPIError(Exception):
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
class TONAPIConnectionError(TONAPIError):
|
|
37
|
-
"""Network-level error
|
|
38
|
-
refused connection, or other transport issues."""
|
|
37
|
+
"""Network-level error (DNS failure, timeout, refused connection)."""
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
class TONAPIStatusError(TONAPIError):
|
|
@@ -127,15 +126,15 @@ class TONAPIRetryLimitError(TONAPIError):
|
|
|
127
126
|
"""All retry attempts have been exhausted."""
|
|
128
127
|
|
|
129
128
|
attempts: int
|
|
130
|
-
last_status:
|
|
131
|
-
last_error:
|
|
129
|
+
last_status: int | None
|
|
130
|
+
last_error: Exception | None
|
|
132
131
|
|
|
133
132
|
def __init__(
|
|
134
133
|
self,
|
|
135
134
|
*,
|
|
136
135
|
attempts: int,
|
|
137
|
-
last_status:
|
|
138
|
-
last_error:
|
|
136
|
+
last_status: int | None = None,
|
|
137
|
+
last_error: Exception | None = None,
|
|
139
138
|
) -> None:
|
|
140
139
|
self.attempts = attempts
|
|
141
140
|
self.last_status = last_status
|
|
@@ -148,13 +147,13 @@ class TONAPIRetryLimitError(TONAPIError):
|
|
|
148
147
|
super().__init__(", ".join(parts))
|
|
149
148
|
|
|
150
149
|
|
|
151
|
-
STREAMING_RECOVERABLE: t.Final[
|
|
150
|
+
STREAMING_RECOVERABLE: t.Final[tuple[type[TONAPIError], ...]] = (
|
|
152
151
|
TONAPIServerError,
|
|
153
152
|
TONAPITooManyRequestsError,
|
|
154
153
|
TONAPIStreamingError,
|
|
155
154
|
)
|
|
156
155
|
|
|
157
|
-
TONAPI_STATUS_TO_EXCEPTION: t.Final[
|
|
156
|
+
TONAPI_STATUS_TO_EXCEPTION: t.Final[dict[int, type[TONAPIStatusError]]] = {
|
|
158
157
|
400: TONAPIBadRequestError,
|
|
159
158
|
401: TONAPIUnauthorizedError,
|
|
160
159
|
403: TONAPIForbiddenError,
|
|
@@ -180,7 +179,7 @@ def raise_for_status(status: int, body: str, content_type: str = "") -> None:
|
|
|
180
179
|
else:
|
|
181
180
|
try:
|
|
182
181
|
data = json.loads(body)
|
|
183
|
-
except
|
|
182
|
+
except json.JSONDecodeError:
|
|
184
183
|
message = body
|
|
185
184
|
else:
|
|
186
185
|
if isinstance(data, dict):
|
|
@@ -27,14 +27,14 @@ class TonapiRestClient(BaseClient, ResourcesMixin):
|
|
|
27
27
|
api_key: str,
|
|
28
28
|
network: Network,
|
|
29
29
|
*,
|
|
30
|
-
base_url:
|
|
30
|
+
base_url: str | None = None,
|
|
31
31
|
timeout: float = 10.0,
|
|
32
|
-
session:
|
|
33
|
-
headers:
|
|
34
|
-
cookies:
|
|
32
|
+
session: aiohttp.ClientSession | None = None,
|
|
33
|
+
headers: dict[str, str] | None = None,
|
|
34
|
+
cookies: dict[str, str] | None = None,
|
|
35
35
|
rps_limit: int = 0,
|
|
36
36
|
rps_period: float = 1.0,
|
|
37
|
-
retry_policy:
|
|
37
|
+
retry_policy: RetryPolicy | None = DEFAULT_RETRY_POLICY,
|
|
38
38
|
) -> None:
|
|
39
39
|
"""Initialize the TONAPI client.
|
|
40
40
|
|
|
@@ -60,7 +60,7 @@ class TonapiRestClient(BaseClient, ResourcesMixin):
|
|
|
60
60
|
cookies=cookies,
|
|
61
61
|
retry_policy=retry_policy,
|
|
62
62
|
)
|
|
63
|
-
self._rate_limiter:
|
|
63
|
+
self._rate_limiter: RateLimiter | None = (
|
|
64
64
|
RateLimiter(rps=rps_limit, period=rps_period) if rps_limit > 0 else None
|
|
65
65
|
)
|
|
66
66
|
ResourcesMixin.__init__(self, self)
|
|
@@ -70,10 +70,10 @@ class TonapiRestClient(BaseClient, ResourcesMixin):
|
|
|
70
70
|
method: str,
|
|
71
71
|
path: str,
|
|
72
72
|
*,
|
|
73
|
-
params:
|
|
74
|
-
body: t.
|
|
75
|
-
headers:
|
|
76
|
-
response_model:
|
|
73
|
+
params: dict[str, t.Any] | None = None,
|
|
74
|
+
body: t.Any | None = None,
|
|
75
|
+
headers: dict[str, t.Any] | None = None,
|
|
76
|
+
response_model: type[T] | None = None,
|
|
77
77
|
) -> t.Any:
|
|
78
78
|
"""Execute an HTTP request with retry and rate limiting.
|
|
79
79
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import time
|
|
3
|
-
import typing as t
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
class RateLimiter:
|
|
@@ -13,7 +12,7 @@ class RateLimiter:
|
|
|
13
12
|
def __init__(self, rps: int, period: float = 1.0) -> None:
|
|
14
13
|
self._rps = rps
|
|
15
14
|
self._period = period
|
|
16
|
-
self._timestamps:
|
|
15
|
+
self._timestamps: list[float] = []
|
|
17
16
|
self._lock = asyncio.Lock()
|
|
18
17
|
|
|
19
18
|
async def acquire(self) -> None:
|