algokit-subscriber 1.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.
@@ -0,0 +1,234 @@
1
+ Metadata-Version: 2.3
2
+ Name: algokit-subscriber
3
+ Version: 1.0.1
4
+ Summary:
5
+ Author: Algorand Foundation
6
+ Requires-Python: >=3.12,<4.0
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Requires-Dist: py-algorand-sdk (>=2.9.1,<3.0.0)
11
+ Description-Content-Type: text/markdown
12
+
13
+ <div align="center">
14
+ <a href="https://github.com/algorandfoundation/algokit-subscriber-py"><img src="https://bafybeidbb3a7cgn3unoz4oouk2jme4eavqgqtnskfr4bqhbjku3s52de4a.ipfs.w3s.link/algokit-subscriber-logo.png" width=60%></a>
15
+ </div>
16
+
17
+ <p align="center">
18
+ <a target="_blank" href="https://algorandfoundation.github.io/algokit-subscriber-py/"><img src="https://img.shields.io/badge/docs-repository-74dfdc?logo=github&style=flat.svg" /></a>
19
+ <a target="_blank" href="https://algorand.co/algokit"><img src="https://img.shields.io/badge/learn-AlgoKit-74dfdc?logo=algorand&mac=flat.svg" /></a>
20
+ <a target="_blank" href="https://github.com/algorandfoundation/algokit-subscriber-py"><img src="https://img.shields.io/github/stars/algorandfoundation/algokit-subscriber-py?color=74dfdc&logo=star&style=flat" /></a>
21
+ <a target="_blank" href="https://algorand.co/algokit"><img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fgithub.com%2Falgorandfoundation%2Falgokit-subscriber-py&countColor=%2374dfdc&style=flat" /></a>
22
+ </p>
23
+
24
+ ---
25
+
26
+ This library a simple, but flexible / configurable Algorand transaction subscription / indexing mechanism. It allows you to quickly create Python services that follow or subscribe to the Algorand Blockchain.
27
+
28
+ > pip install algokit_subscriber
29
+
30
+ [Documentation](./docs/index.md)
31
+
32
+ ## Quick start
33
+
34
+ ```python
35
+ # Create subscriber
36
+ subscriber = AlgorandSubscriber(
37
+ {
38
+ "filters": [
39
+ {
40
+ "name": "filter1",
41
+ "filter": {
42
+ "type": "pay",
43
+ "sender": "ABC...",
44
+ },
45
+ },
46
+ ],
47
+ # ... other options (use intellisense to explore)
48
+ },
49
+ algod,
50
+ optional_indexer
51
+ );
52
+
53
+ # Set up subscription(s)
54
+ def on_filter1(transaction, event_name):
55
+ ...
56
+
57
+ subscriber.on("filter1", on_filter1)
58
+
59
+ # Either: Start the subscriber (if in long-running process)
60
+ subscriber.start();
61
+
62
+ # OR: Poll the subscriber (if in cron job / periodic lambda)
63
+ subscriber.pollOnce();
64
+ ```
65
+
66
+ ## Key features
67
+
68
+ - **Notification _and_ indexing** - You have fine-grained control over the syncing behaviour and can control the number of rounds to sync at a time, the pattern of syncing i.e. start from the beginning of the chain, or start from the tip; drop stale records if your service can't keep up or keep syncing from where you are up to; etc.
69
+ - **Low latency processing** - When your service has caught up to the tip of the chain it can optionally wait for new rounds so you have a low latency reaction to a new round occurring
70
+ - **Watermarking and resilience** - You can create reliable syncing / indexing services through a simple round watermarking capability that allows you to create resilient syncing services that can recover from an outage
71
+ - **Extensive subscription filtering** - You can filter by transaction type, sender, receiver, note prefix, apps (ID, creation, on complete, ARC-4 method signature, call arguments, ARC-28 events), assets (ID, creation, amount transferred range), transfers (amount transferred range) and balance changes (algo and assets)
72
+ - **ARC-28 event subscription support** - You can subscribe to ARC-28 events for a smart contract
73
+ - **Balance change support** - Subscribed transactions will have all algo and asset balance changes calculated for you and you can also subscribe to balance changes that meet certain criteria
74
+ - **First-class inner transaction support** - Your filter will find arbitrarily nested inner transactions and return that transaction (indexer can't do this!)
75
+ - **State-proof support** - You can subscribe to state proof transactions
76
+ - **Simple programming model** - It's really easy to use and consume through easy to use, type-safe methods and objects and subscribed transactions have a comprehensive and familiar model type with all relevant/useful information about that transaction (including things like transaction id, round number, created asset/app id, app logs, etc.) modelled on the indexer data model (which is used regardless of whether the transactions come from indexer or algod so it's a consistent experience)
77
+ - **Easy to deploy** - You have full control over how you want to deploy and use the subscriber; it will work with whatever persistence (e.g. sql, no-sql, etc.), queuing/messaging (e.g. queues, topics, buses, web hooks, web sockets) and compute (e.g. serverless periodic lambdas, continually running containers, virtual machines, etc.) services you want to use
78
+ - **Fast initial index** - There is an indexer catch up mode that allows you to use indexer to catch up to the tip of the chain in seconds or minutes rather than days; alternatively, if you prefer to just use algod and not indexer that option is available too!
79
+
80
+ ## Balance change notes
81
+
82
+ The balance change semantics work mostly as expected, however the sematics around asset creation and destruction warrants further clarification.
83
+
84
+ When an asset is created, the full asset supply is attributed to the asset creators account.
85
+
86
+ The balance change for an asset create transaction will be as below:
87
+
88
+ ```py
89
+ {
90
+ "address": "VIDHG4SYANCP2GUQXXSFSNBPJWS4TAQSI3GH4GYO54FSYPDIBYPMSF7HBY", # The asset creator
91
+ "asset_id": 2391, # The created asset id
92
+ "amount": 100000, # Full asset supply of the created asset
93
+ "roles": [BalanceChangeroles.AssetCreator]
94
+ }
95
+ ```
96
+
97
+ When an asset is destroyed, the full asset supply must be in the asset creators account and the asset manager must send the destroy transaction.
98
+ Unfortunately we cannot determine the asset creator or full asset supply from the transaction data. As a result the balance change will always be attributed to the asset manager and will have a 0 amount.
99
+ If you need to account for the asset supply being destroyed from the creators account, you'll need to handle this separately.
100
+
101
+ The balance change for an asset destroy transaction will be as below:
102
+
103
+ ```python
104
+ {
105
+ "address": "PIDHG4SYANCP2GUQXXSFSNBPJWS4TAQSI3GH4GYO54FSYPDIBYPMSF7HBY", # The asset destroyer, which will always be the asset manager
106
+ "assetId": 2391, # The destroyed asset id
107
+ "amount": 0, # This value will always be 0
108
+ "roles": [BalanceChangeroles.AssetDestroyer]
109
+ }
110
+ ```
111
+
112
+ ## Examples
113
+
114
+ ### Data History Museum index
115
+
116
+ The following code, when algod is pointed to TestNet, will find all transactions emitted by the [Data History Museum](https://datahistory.org) since the beginning of time in _seconds_ and then find them in real-time as they emerge on the chain.
117
+
118
+ The watermark is stored in-memory so this particular example is not resilient to restarts. To change that you can implement proper persistence of the watermark. There is [an example that uses the file system](./examples/data-history-museum/) to demonstrate this.
119
+
120
+ ```python
121
+ algorand = AlgorandClient.testnet()
122
+
123
+ # The watermark is used to track how far the subscriber has processed transactions
124
+ watermark = 0
125
+
126
+ def get_watermark() -> int:
127
+ return watermark
128
+
129
+ def set_watermark(new_watermark: int) -> None:
130
+ global watermark
131
+ watermark = new_watermark
132
+
133
+ subscriber = AlgorandSubscriber(
134
+ # algod is used to get the latest transactions once the subscriber has caught up to the network
135
+ algod_client=algorand.client.algod,
136
+ config={
137
+ "filters": [
138
+ {
139
+ "name": "dhm-asset",
140
+ "filter": {
141
+ # Match asset configuration transactions
142
+ "type": "acfg",
143
+ # Data History Museum creator account on TestNet
144
+ "sender": "ER7AMZRPD5KDVFWTUUVOADSOWM4RQKEEV2EDYRVSA757UHXOIEKGMBQIVU",
145
+ },
146
+ }
147
+ ],
148
+ "frequency_in_seconds": 5,
149
+ "max_rounds_to_sync": 100,
150
+ "sync_behaviour": "catchup-with-indexer",
151
+ "watermark_persistence": {"get": get_watermark, "set": set_watermark},
152
+ },
153
+ # indexer is used to get historical transactions
154
+ indexer_client=algorand.client.indexer,
155
+ )
156
+
157
+ def process_dhm_assets(transactions: list[SubscribedTransaction], filter_name: str) -> None:
158
+ print(f"Received {len(transactions)} asset changes")
159
+ # ... do stuff with the transactions
160
+
161
+ # Attach our callback to the 'dhm-asset' filter
162
+ subscriber.on_batch("dhm-asset", process_dhm_assets)
163
+
164
+ def handle_error(error: Exception) -> None:
165
+ print(f"An error occurred: {error}")
166
+
167
+ # Attach the error handler
168
+ subscriber.on_error(handle_error)
169
+
170
+ # Start the subscriber
171
+ subscriber.start()
172
+ ```
173
+
174
+ ### USDC real-time monitoring
175
+
176
+ The following code, when algod is pointed to MainNet, will find all transfers of [USDC](https://www.circle.com/en/usdc-multichain/algorand) that are greater than $1 and it will poll every 1s for new transfers.
177
+
178
+ ```python
179
+ from algokit_subscriber import AlgorandSubscriber
180
+ from algokit_subscriber.types import SubscribedTransaction
181
+ from algokit_utils import AlgorandClient
182
+
183
+ algorand = AlgorandClient.mainnet()
184
+
185
+ # The watermark is used to track how far the subscriber has processed transactions
186
+ watermark = 0
187
+
188
+ def get_watermark() -> int:
189
+ return watermark
190
+
191
+ def set_watermark(new_watermark: int) -> None:
192
+ global watermark
193
+ watermark = new_watermark
194
+
195
+ subscriber = AlgorandSubscriber(
196
+ algod_client=algorand.client.algod,
197
+ config={
198
+ "filters": [
199
+ {
200
+ "name": "usdc",
201
+ "filter": {
202
+ "type": "axfer",
203
+ "asset_id": 31566704, # MainNet: USDC
204
+ "min_amount": 1_000_000, # $1
205
+ },
206
+ }
207
+ ],
208
+ "wait_for_block_when_at_tip": True,
209
+ "sync_behaviour": "skip-sync-newest",
210
+ "watermark_persistence": {"get": get_watermark, "set": set_watermark},
211
+ },
212
+ )
213
+
214
+ def process_usdc_transfer(transfer: SubscribedTransaction, filter_name: str) -> None:
215
+ asset_transfer = transfer.get("asset-transfer-transaction", {})
216
+ amount = asset_transfer.get("amount", 0) / 1_000_000
217
+ print(
218
+ f"{transfer['sender']} sent {asset_transfer.get('receiver')} "
219
+ f"USDC${amount:.2f} in transaction {transfer['id']}"
220
+ )
221
+
222
+ # Attach our callback to the 'usdc' filter
223
+ subscriber.on("usdc", process_usdc_transfer)
224
+
225
+ def handle_error(error: Exception) -> None:
226
+ print(f"An error occurred: {error}")
227
+
228
+ # Attach the error handler
229
+ subscriber.on_error(handle_error)
230
+
231
+ # Start the subscriber
232
+ subscriber.start()
233
+ ```
234
+
@@ -0,0 +1,221 @@
1
+ <div align="center">
2
+ <a href="https://github.com/algorandfoundation/algokit-subscriber-py"><img src="https://bafybeidbb3a7cgn3unoz4oouk2jme4eavqgqtnskfr4bqhbjku3s52de4a.ipfs.w3s.link/algokit-subscriber-logo.png" width=60%></a>
3
+ </div>
4
+
5
+ <p align="center">
6
+ <a target="_blank" href="https://algorandfoundation.github.io/algokit-subscriber-py/"><img src="https://img.shields.io/badge/docs-repository-74dfdc?logo=github&style=flat.svg" /></a>
7
+ <a target="_blank" href="https://algorand.co/algokit"><img src="https://img.shields.io/badge/learn-AlgoKit-74dfdc?logo=algorand&mac=flat.svg" /></a>
8
+ <a target="_blank" href="https://github.com/algorandfoundation/algokit-subscriber-py"><img src="https://img.shields.io/github/stars/algorandfoundation/algokit-subscriber-py?color=74dfdc&logo=star&style=flat" /></a>
9
+ <a target="_blank" href="https://algorand.co/algokit"><img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fgithub.com%2Falgorandfoundation%2Falgokit-subscriber-py&countColor=%2374dfdc&style=flat" /></a>
10
+ </p>
11
+
12
+ ---
13
+
14
+ This library a simple, but flexible / configurable Algorand transaction subscription / indexing mechanism. It allows you to quickly create Python services that follow or subscribe to the Algorand Blockchain.
15
+
16
+ > pip install algokit_subscriber
17
+
18
+ [Documentation](./docs/index.md)
19
+
20
+ ## Quick start
21
+
22
+ ```python
23
+ # Create subscriber
24
+ subscriber = AlgorandSubscriber(
25
+ {
26
+ "filters": [
27
+ {
28
+ "name": "filter1",
29
+ "filter": {
30
+ "type": "pay",
31
+ "sender": "ABC...",
32
+ },
33
+ },
34
+ ],
35
+ # ... other options (use intellisense to explore)
36
+ },
37
+ algod,
38
+ optional_indexer
39
+ );
40
+
41
+ # Set up subscription(s)
42
+ def on_filter1(transaction, event_name):
43
+ ...
44
+
45
+ subscriber.on("filter1", on_filter1)
46
+
47
+ # Either: Start the subscriber (if in long-running process)
48
+ subscriber.start();
49
+
50
+ # OR: Poll the subscriber (if in cron job / periodic lambda)
51
+ subscriber.pollOnce();
52
+ ```
53
+
54
+ ## Key features
55
+
56
+ - **Notification _and_ indexing** - You have fine-grained control over the syncing behaviour and can control the number of rounds to sync at a time, the pattern of syncing i.e. start from the beginning of the chain, or start from the tip; drop stale records if your service can't keep up or keep syncing from where you are up to; etc.
57
+ - **Low latency processing** - When your service has caught up to the tip of the chain it can optionally wait for new rounds so you have a low latency reaction to a new round occurring
58
+ - **Watermarking and resilience** - You can create reliable syncing / indexing services through a simple round watermarking capability that allows you to create resilient syncing services that can recover from an outage
59
+ - **Extensive subscription filtering** - You can filter by transaction type, sender, receiver, note prefix, apps (ID, creation, on complete, ARC-4 method signature, call arguments, ARC-28 events), assets (ID, creation, amount transferred range), transfers (amount transferred range) and balance changes (algo and assets)
60
+ - **ARC-28 event subscription support** - You can subscribe to ARC-28 events for a smart contract
61
+ - **Balance change support** - Subscribed transactions will have all algo and asset balance changes calculated for you and you can also subscribe to balance changes that meet certain criteria
62
+ - **First-class inner transaction support** - Your filter will find arbitrarily nested inner transactions and return that transaction (indexer can't do this!)
63
+ - **State-proof support** - You can subscribe to state proof transactions
64
+ - **Simple programming model** - It's really easy to use and consume through easy to use, type-safe methods and objects and subscribed transactions have a comprehensive and familiar model type with all relevant/useful information about that transaction (including things like transaction id, round number, created asset/app id, app logs, etc.) modelled on the indexer data model (which is used regardless of whether the transactions come from indexer or algod so it's a consistent experience)
65
+ - **Easy to deploy** - You have full control over how you want to deploy and use the subscriber; it will work with whatever persistence (e.g. sql, no-sql, etc.), queuing/messaging (e.g. queues, topics, buses, web hooks, web sockets) and compute (e.g. serverless periodic lambdas, continually running containers, virtual machines, etc.) services you want to use
66
+ - **Fast initial index** - There is an indexer catch up mode that allows you to use indexer to catch up to the tip of the chain in seconds or minutes rather than days; alternatively, if you prefer to just use algod and not indexer that option is available too!
67
+
68
+ ## Balance change notes
69
+
70
+ The balance change semantics work mostly as expected, however the sematics around asset creation and destruction warrants further clarification.
71
+
72
+ When an asset is created, the full asset supply is attributed to the asset creators account.
73
+
74
+ The balance change for an asset create transaction will be as below:
75
+
76
+ ```py
77
+ {
78
+ "address": "VIDHG4SYANCP2GUQXXSFSNBPJWS4TAQSI3GH4GYO54FSYPDIBYPMSF7HBY", # The asset creator
79
+ "asset_id": 2391, # The created asset id
80
+ "amount": 100000, # Full asset supply of the created asset
81
+ "roles": [BalanceChangeroles.AssetCreator]
82
+ }
83
+ ```
84
+
85
+ When an asset is destroyed, the full asset supply must be in the asset creators account and the asset manager must send the destroy transaction.
86
+ Unfortunately we cannot determine the asset creator or full asset supply from the transaction data. As a result the balance change will always be attributed to the asset manager and will have a 0 amount.
87
+ If you need to account for the asset supply being destroyed from the creators account, you'll need to handle this separately.
88
+
89
+ The balance change for an asset destroy transaction will be as below:
90
+
91
+ ```python
92
+ {
93
+ "address": "PIDHG4SYANCP2GUQXXSFSNBPJWS4TAQSI3GH4GYO54FSYPDIBYPMSF7HBY", # The asset destroyer, which will always be the asset manager
94
+ "assetId": 2391, # The destroyed asset id
95
+ "amount": 0, # This value will always be 0
96
+ "roles": [BalanceChangeroles.AssetDestroyer]
97
+ }
98
+ ```
99
+
100
+ ## Examples
101
+
102
+ ### Data History Museum index
103
+
104
+ The following code, when algod is pointed to TestNet, will find all transactions emitted by the [Data History Museum](https://datahistory.org) since the beginning of time in _seconds_ and then find them in real-time as they emerge on the chain.
105
+
106
+ The watermark is stored in-memory so this particular example is not resilient to restarts. To change that you can implement proper persistence of the watermark. There is [an example that uses the file system](./examples/data-history-museum/) to demonstrate this.
107
+
108
+ ```python
109
+ algorand = AlgorandClient.testnet()
110
+
111
+ # The watermark is used to track how far the subscriber has processed transactions
112
+ watermark = 0
113
+
114
+ def get_watermark() -> int:
115
+ return watermark
116
+
117
+ def set_watermark(new_watermark: int) -> None:
118
+ global watermark
119
+ watermark = new_watermark
120
+
121
+ subscriber = AlgorandSubscriber(
122
+ # algod is used to get the latest transactions once the subscriber has caught up to the network
123
+ algod_client=algorand.client.algod,
124
+ config={
125
+ "filters": [
126
+ {
127
+ "name": "dhm-asset",
128
+ "filter": {
129
+ # Match asset configuration transactions
130
+ "type": "acfg",
131
+ # Data History Museum creator account on TestNet
132
+ "sender": "ER7AMZRPD5KDVFWTUUVOADSOWM4RQKEEV2EDYRVSA757UHXOIEKGMBQIVU",
133
+ },
134
+ }
135
+ ],
136
+ "frequency_in_seconds": 5,
137
+ "max_rounds_to_sync": 100,
138
+ "sync_behaviour": "catchup-with-indexer",
139
+ "watermark_persistence": {"get": get_watermark, "set": set_watermark},
140
+ },
141
+ # indexer is used to get historical transactions
142
+ indexer_client=algorand.client.indexer,
143
+ )
144
+
145
+ def process_dhm_assets(transactions: list[SubscribedTransaction], filter_name: str) -> None:
146
+ print(f"Received {len(transactions)} asset changes")
147
+ # ... do stuff with the transactions
148
+
149
+ # Attach our callback to the 'dhm-asset' filter
150
+ subscriber.on_batch("dhm-asset", process_dhm_assets)
151
+
152
+ def handle_error(error: Exception) -> None:
153
+ print(f"An error occurred: {error}")
154
+
155
+ # Attach the error handler
156
+ subscriber.on_error(handle_error)
157
+
158
+ # Start the subscriber
159
+ subscriber.start()
160
+ ```
161
+
162
+ ### USDC real-time monitoring
163
+
164
+ The following code, when algod is pointed to MainNet, will find all transfers of [USDC](https://www.circle.com/en/usdc-multichain/algorand) that are greater than $1 and it will poll every 1s for new transfers.
165
+
166
+ ```python
167
+ from algokit_subscriber import AlgorandSubscriber
168
+ from algokit_subscriber.types import SubscribedTransaction
169
+ from algokit_utils import AlgorandClient
170
+
171
+ algorand = AlgorandClient.mainnet()
172
+
173
+ # The watermark is used to track how far the subscriber has processed transactions
174
+ watermark = 0
175
+
176
+ def get_watermark() -> int:
177
+ return watermark
178
+
179
+ def set_watermark(new_watermark: int) -> None:
180
+ global watermark
181
+ watermark = new_watermark
182
+
183
+ subscriber = AlgorandSubscriber(
184
+ algod_client=algorand.client.algod,
185
+ config={
186
+ "filters": [
187
+ {
188
+ "name": "usdc",
189
+ "filter": {
190
+ "type": "axfer",
191
+ "asset_id": 31566704, # MainNet: USDC
192
+ "min_amount": 1_000_000, # $1
193
+ },
194
+ }
195
+ ],
196
+ "wait_for_block_when_at_tip": True,
197
+ "sync_behaviour": "skip-sync-newest",
198
+ "watermark_persistence": {"get": get_watermark, "set": set_watermark},
199
+ },
200
+ )
201
+
202
+ def process_usdc_transfer(transfer: SubscribedTransaction, filter_name: str) -> None:
203
+ asset_transfer = transfer.get("asset-transfer-transaction", {})
204
+ amount = asset_transfer.get("amount", 0) / 1_000_000
205
+ print(
206
+ f"{transfer['sender']} sent {asset_transfer.get('receiver')} "
207
+ f"USDC${amount:.2f} in transaction {transfer['id']}"
208
+ )
209
+
210
+ # Attach our callback to the 'usdc' filter
211
+ subscriber.on("usdc", process_usdc_transfer)
212
+
213
+ def handle_error(error: Exception) -> None:
214
+ print(f"An error occurred: {error}")
215
+
216
+ # Attach the error handler
217
+ subscriber.on_error(handle_error)
218
+
219
+ # Start the subscriber
220
+ subscriber.start()
221
+ ```
@@ -0,0 +1,176 @@
1
+ [tool.poetry]
2
+ name = "algokit-subscriber"
3
+ version = "1.0.1"
4
+ description = ""
5
+ authors = ["Algorand Foundation"]
6
+ readme = "README.md"
7
+
8
+ [tool.poetry.dependencies]
9
+ python = "^3.12"
10
+ py-algorand-sdk = "^2.9.1"
11
+
12
+ [tool.poetry.group.dev.dependencies]
13
+ algokit-utils = "^4.1.0"
14
+ mypy = "^1.17.1"
15
+ ruff = "^0.12.8"
16
+ pytest = "^8.4.1"
17
+ pre-commit = "^4.2.0"
18
+ black = "^25.1.0"
19
+ pytest-cov = "^6.2.1"
20
+ sphinx = "^8.2.3"
21
+ furo = "^2024.1.29"
22
+ myst-parser = "^4.0.0"
23
+ sphinx-autodoc2 = "^0.5.0"
24
+ sphinx-copybutton = "^0.5.2"
25
+ sphinx-autobuild = "^2024.4.16"
26
+ sphinx-mermaid = "^0.0.8"
27
+ poethepoet = "^0.36.0"
28
+ pytest-sugar = "^1.0.0"
29
+ pip-audit = "^2.9.0"
30
+
31
+ [build-system]
32
+ requires = ["poetry-core"]
33
+ build-backend = "poetry.core.masonry.api"
34
+
35
+ [tool.ruff]
36
+ # TODO: eventually bring this down to 120
37
+ line-length = 280
38
+ lint.select = [
39
+ # all possible codes as of this ruff version are listed here,
40
+ # ones we don't want/need are commented out to make it clear
41
+ # which have been omitted on purpose vs which ones get added
42
+ # in new ruff releases and should be considered for enabling
43
+ "F", # pyflakes
44
+ "E",
45
+ "W", # pycodestyle
46
+ "C90", # mccabe
47
+ "I", # isort
48
+ "N", # PEP8 naming
49
+ "UP", # pyupgrade
50
+ "YTT", # flake8-2020
51
+ "ANN", # flake8-annotations
52
+ # "S", # flake8-bandit
53
+ # "BLE", # flake8-blind-except
54
+ "FBT", # flake8-boolean-trap
55
+ "B", # flake8-bugbear
56
+ "A", # flake8-builtins
57
+ # "COM", # flake8-commas
58
+ "C4", # flake8-comprehensions
59
+ "DTZ", # flake8-datetimez
60
+ "T10", # flake8-debugger
61
+ # "DJ", # flake8-django
62
+ # "EM", # flake8-errmsg
63
+ # "EXE", # flake8-executable
64
+ "ISC", # flake8-implicit-str-concat
65
+ "ICN", # flake8-import-conventions
66
+ # "G", # flake8-logging-format
67
+ # "INP", # flake8-no-pep420
68
+ "PIE", # flake8-pie
69
+ "T20", # flake8-print
70
+ "PYI", # flake8-pyi
71
+ "PT", # flake8-pytest-style
72
+ "Q", # flake8-quotes
73
+ "RSE", # flake8-raise
74
+ "RET", # flake8-return
75
+ "SLF", # flake8-self
76
+ "SIM", # flake8-simplify
77
+ "TID", # flake8-tidy-imports
78
+ "TCH", # flake8-type-checking
79
+ "ARG", # flake8-unused-arguments
80
+ "PTH", # flake8-use-pathlib
81
+ "ERA", # eradicate
82
+ # "PD", # pandas-vet
83
+ "PGH", # pygrep-hooks
84
+ "PL", # pylint
85
+ # "TRY", # tryceratops
86
+ # "NPY", # NumPy-specific rules
87
+ "RUF", # Ruff-specific rules
88
+ ]
89
+ lint.ignore = [
90
+ "RET505", # allow else after return
91
+ "SIM108", # allow if-else in place of ternary
92
+ "E111", # indentation is not a multiple of four
93
+ "E117", # over-indented
94
+ "ISC001", # single line implicit string concatenation
95
+ "ISC002", # multi line implicit string concatenation
96
+ "Q000", # bad quotes inline string
97
+ "Q001", # bad quotes multiline string
98
+ "Q002", # bad quotes docstring
99
+ "Q003", # avoidable escaped quotes
100
+ "W191", # indentation contains tabs
101
+ "ERA001", # commented out code
102
+ ]
103
+ # Exclude a variety of commonly ignored directories.
104
+ extend-exclude = ["docs", ".git", ".mypy_cache", ".ruff_cache"]
105
+ # Assume Python 3.12.
106
+ target-version = "py312"
107
+
108
+ [tool.ruff.lint.flake8-annotations]
109
+ allow-star-arg-any = true
110
+ suppress-none-returning = true
111
+
112
+ [tool.mypy]
113
+ files = ["src", "examples"]
114
+ exclude = ["dist", "tests"]
115
+ python_version = "3.12"
116
+ warn_unused_ignores = true
117
+ warn_redundant_casts = true
118
+ warn_unused_configs = true
119
+ warn_unreachable = true
120
+ warn_return_any = true
121
+ strict = true
122
+ disallow_untyped_decorators = true
123
+ disallow_any_generics = false
124
+ implicit_reexport = false
125
+ show_error_codes = true
126
+
127
+ [tool.pytest.ini_options]
128
+ pythonpath = ["src", "tests"]
129
+
130
+ [tool.ruff.lint.per-file-ignores]
131
+ "tests/*" = ["E501", "T201", "PLR2004", "F811"]
132
+ "examples/*" = ["T201"]
133
+
134
+ [tool.poe.tasks]
135
+ docs-test = { shell = "sphinx-build -b doctest docs docs/_build -W --keep-going -n -E" }
136
+ docs-clear = { shell = "rm -rf docs/_build" }
137
+ docs-build = { shell = "poetry run poe docs-clear && sphinx-build docs docs/_build -W --keep-going -n -E" }
138
+ docs-dev = { shell = "poetry run poe docs-build && sphinx-autobuild docs docs/_build" }
139
+
140
+ [tool.semantic_release]
141
+ version_toml = ["pyproject.toml:tool.poetry.version"]
142
+ build_command = "pip install build && python -m build"
143
+ commit_message = "{version}\n\n[skip ci] Automatically generated by python-semantic-release"
144
+ tag_format = "v{version}"
145
+ allow_zero_version = false
146
+
147
+ [tool.semantic_release.branches.main]
148
+ match = "main"
149
+ prerelease_token = "beta"
150
+ prerelease = false
151
+
152
+ [tool.semantic_release.commit_parser_options]
153
+ allowed_tags = [
154
+ "build",
155
+ "chore",
156
+ "ci",
157
+ "docs",
158
+ "feat",
159
+ "fix",
160
+ "perf",
161
+ "style",
162
+ "refactor",
163
+ "test",
164
+ ]
165
+ minor_tags = ["feat"]
166
+ patch_tags = ["fix", "perf", "docs"]
167
+
168
+ [tool.semantic_release.publish]
169
+ dist_glob_patterns = [
170
+ "dist/*",
171
+ "stubs/dist/*",
172
+ ] # order here is important to ensure compiler wheel is published first
173
+ upload_to_vcs_release = true
174
+
175
+ [tool.semantic_release.remote.token]
176
+ env = "GITHUB_TOKEN"
@@ -0,0 +1,28 @@
1
+ from .subscriber import AlgorandSubscriber
2
+ from .subscription import get_subscribed_transactions
3
+ from .types.arc28 import Arc28EventGroup
4
+ from .types.event_emitter import EventListener
5
+ from .types.subscription import (
6
+ AlgorandSubscriberConfig,
7
+ BalanceChange,
8
+ BalanceChangeRole,
9
+ NamedTransactionFilter,
10
+ SubscribedTransaction,
11
+ TransactionSubscriptionParams,
12
+ TransactionSubscriptionResult,
13
+ )
14
+
15
+ __all__ = [
16
+ "AlgorandSubscriber",
17
+ "AlgorandSubscriberConfig",
18
+ "Arc28EventGroup",
19
+ "BalanceChange",
20
+ "BalanceChangeRole",
21
+ "EventListener",
22
+ "NamedTransactionFilter",
23
+ "SubscribedTransaction",
24
+ "TransactionFilter",
25
+ "TransactionSubscriptionParams",
26
+ "TransactionSubscriptionResult",
27
+ "get_subscribed_transactions",
28
+ ]