python-etcd3-async 0.1.0__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.
@@ -0,0 +1,328 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-etcd3-async
3
+ Version: 0.1.0
4
+ Summary: etcd3 async sdk
5
+ Requires-Python: <4,>=3.12
6
+ Requires-Dist: grpcio==1.76.0
7
+ Requires-Dist: prometheus-client>=0.24.1
8
+ Requires-Dist: protobuf==6.31.1
9
+ Description-Content-Type: text/markdown
10
+
11
+ # python-etcd3-async
12
+
13
+ [中文文档](./README_CN.md) | English
14
+
15
+ Python async client for the etcd API v3, supporting both synchronous and asynchronous operations.
16
+
17
+ * Free software: Apache Software License 2.0
18
+ * Python 3.12+
19
+
20
+ ## Features
21
+
22
+ - **Full etcd v3 API support**: Key-Value operations, Leases, Locks, Watches, Transactions
23
+ - **Async/Sync dual support**: Same API for both async and sync usage with feature parity
24
+ - **Automatic reconnection**: Auto-rebuild stale connections (e.g., after etcd server restart)
25
+ - **Keepalive error propagation**: Lease keepalive errors are properly propagated for connection loss detection
26
+ - **Endpoint failover**: Built-in support for multiple endpoints with automatic failover
27
+ - **Context manager**: Clean resource management for both sync and async
28
+
29
+ ## Quick Start
30
+
31
+ ### Async Client
32
+
33
+ ```python
34
+ import asyncio
35
+ from etcd3 import async_client
36
+
37
+ async def main():
38
+ # Create client
39
+ client = await async_client(host='localhost', port=2379)
40
+
41
+ # Key-Value operations
42
+ await client.put('/mykey', 'Hello etcd!')
43
+ value, meta = await client.get('/mykey')
44
+ print(value) # b'Hello etcd!'
45
+
46
+ await client.delete('/mykey')
47
+ await client.close()
48
+
49
+ asyncio.run(main())
50
+ ```
51
+
52
+ ### Sync Client
53
+
54
+ ```python
55
+ from etcd3 import client
56
+
57
+ # Create sync client
58
+ etcd = client(host='localhost', port=2379)
59
+
60
+ # Key-Value operations
61
+ etcd.put('/mykey', 'Hello etcd!')
62
+ value, meta = etcd.get('/mykey')
63
+ print(value) # b'Hello etcd!'
64
+
65
+ etcd.delete('/mykey')
66
+ ```
67
+
68
+ ## Key Features
69
+
70
+ ### Key-Value Operations
71
+
72
+ ```python
73
+ # Put with lease
74
+ lease = await client.lease(30)
75
+ await client.put('/app/instance', 'value', lease=lease)
76
+
77
+ # Get with prefix
78
+ async for value, meta in client.get_prefix('/app/'):
79
+ print(f'{meta.key}: {value}')
80
+
81
+ # Range query
82
+ async for value, meta in client.get_range('/start', '/end'):
83
+ print(value)
84
+ ```
85
+
86
+ ### Leases (TTL)
87
+
88
+ ```python
89
+ # Create lease with TTL
90
+ lease = await client.lease(60)
91
+ await client.put('/ephemeral/key', 'value', lease=lease)
92
+
93
+ # Keep lease alive
94
+ async for response in lease.refresh():
95
+ print(f'Lease TTL: {response.TTL}')
96
+
97
+ # Revoke lease (deletes all attached keys)
98
+ await lease.revoke()
99
+ ```
100
+
101
+ ### Distributed Locks
102
+
103
+ ```python
104
+ # Acquire lock (full path like Go SDK)
105
+ lock = await client.lock('/myapp/mylock', ttl=30)
106
+ acquired = await lock.acquire(timeout=10)
107
+
108
+ if acquired:
109
+ try:
110
+ # Critical section
111
+ print('Lock acquired')
112
+ finally:
113
+ await lock.release()
114
+
115
+ # Or use context manager
116
+ lock = client.lock('/myapp/mylock', ttl=30)
117
+ async with lock:
118
+ # Critical section
119
+ pass
120
+ ```
121
+
122
+ ### Watch Changes
123
+
124
+ ```python
125
+ # Watch single key
126
+ events_iter, cancel = await client.watch('/mykey')
127
+ async for event in events_iter:
128
+ print(f'Event: {event.event_type}, value: {event.value}')
129
+
130
+ # Watch prefix
131
+ events_iter, cancel = await client.watch_prefix('/app/')
132
+ async for event in events_iter:
133
+ print(f'Changed: {event.key}')
134
+
135
+ # Cancel watch
136
+ cancel()
137
+
138
+ # Watch with timeout (sync/async)
139
+ events_iter, cancel = client.watch('/key', timeout=10)
140
+ for event in events_iter:
141
+ print(event)
142
+ ```
143
+
144
+ ### Transactions
145
+
146
+ ```python
147
+ # Conditional transaction
148
+ success, response = await client.transaction(
149
+ compare=[
150
+ client.transactions.value('/key') == 'expected',
151
+ ],
152
+ success=[
153
+ client.transactions.put('/key', 'new_value'),
154
+ ],
155
+ failure=[
156
+ client.transactions.get('/key'),
157
+ ]
158
+ )
159
+ ```
160
+
161
+ ### Cluster Management
162
+
163
+ ```python
164
+ # List cluster members
165
+ async for member in client.members():
166
+ print(f'{member.name}: {member.clientURLs}')
167
+
168
+ # Add/remove/update members
169
+ await client.add_member(['http://localhost:2380'])
170
+ await client.remove_member(member_id)
171
+ await client.update_member(member_id, ['http://localhost:2380'])
172
+ ```
173
+
174
+ ### Maintenance Operations
175
+
176
+ ```python
177
+ # Get cluster status
178
+ status = await client.status()
179
+ print(f'Leader: {status.leader}')
180
+
181
+ # Compact history
182
+ await client.compact(revision, physical=True)
183
+
184
+ # Defragment database
185
+ await client.defragment()
186
+
187
+ # Alarm management
188
+ await client.create_alarm() # Activate NOSPACE alarm
189
+ alarms = [alarm async for alarm in client.list_alarms()]
190
+ await client.disarm_alarm()
191
+ ```
192
+
193
+ ## Examples
194
+
195
+ ### Leader Election
196
+
197
+ ```python
198
+ # Async: examples/leader_election.py
199
+ # Sync: examples/leader_election_sync.py
200
+
201
+ from etcd3 import async_client
202
+
203
+ async def main():
204
+ client = await async_client(host='localhost', port=2379)
205
+ election = LeaderElection(client, 'my-service-leader', ttl=30)
206
+
207
+ if await election.try_become_leader():
208
+ print('Became leader')
209
+ # Do leader work...
210
+ else:
211
+ print('Not leader')
212
+
213
+ await election.close()
214
+ await client.close()
215
+ ```
216
+
217
+ ### Service Registration
218
+
219
+ ```python
220
+ # Async: examples/service_registration.py
221
+ # Sync: examples/service_registration_sync.py
222
+
223
+ from etcd3 import async_client
224
+
225
+ async def main():
226
+ client = await async_client(host='localhost', port=2379)
227
+ registry = ServiceRegistration(client)
228
+
229
+ lease = await registry.register(
230
+ service_name='my-service',
231
+ instance_id='instance-001',
232
+ host='192.168.1.100',
233
+ port=8080,
234
+ ttl=30,
235
+ )
236
+ registry.start_keepalive(lease)
237
+ # Service stays registered...
238
+ ```
239
+
240
+ ### Service Discovery
241
+
242
+ ```python
243
+ # Async: examples/service_discovery.py
244
+ # Sync: examples/service_discovery_sync.py
245
+
246
+ from etcd3 import async_client
247
+
248
+ async def main():
249
+ client = await async_client(host='localhost', port=2379)
250
+ discovery = ServiceDiscovery(client)
251
+
252
+ # Discover instances
253
+ instances = await discovery.discover('my-service')
254
+ print(f'Found {len(instances)} instances')
255
+
256
+ # Watch for changes
257
+ def on_change(new_instances):
258
+ print(f'Changed: {len(new_instances)} instances')
259
+
260
+ await discovery.watch('my-service', on_change)
261
+ ```
262
+
263
+ ## Configuration
264
+
265
+ ```python
266
+ # Basic configuration
267
+ client = await async_client(
268
+ host='localhost',
269
+ port=2379,
270
+ timeout=10, # Request timeout in seconds
271
+ )
272
+
273
+ # Multiple endpoints with failover
274
+ client = await async_client(
275
+ endpoints=['localhost:2379', 'localhost:22379'],
276
+ failover=True, # Automatically switch to healthy endpoint
277
+ )
278
+
279
+ # TLS authentication
280
+ client = await async_client(
281
+ host='localhost',
282
+ port=2379,
283
+ ca_cert='/path/to/ca.crt',
284
+ cert_key='/path/to/client.key',
285
+ cert_cert='/path/to/client.crt',
286
+ )
287
+
288
+ # User authentication
289
+ client = await async_client(
290
+ host='localhost',
291
+ port=2379,
292
+ user='username',
293
+ password='password',
294
+ )
295
+ ```
296
+
297
+ ## Environment Variables
298
+
299
+ | Variable | Description |
300
+ |----------|-------------|
301
+ | `ETCD3_LOG_LEVEL` | Set logging level (DEBUG, INFO, WARNING, ERROR) |
302
+
303
+ ## API Reference
304
+
305
+ ### Client Factory
306
+
307
+ | Function | Description |
308
+ |----------|-------------|
309
+ | `async_client()` | Create async client |
310
+ | `client()` | Create sync client |
311
+
312
+ ### Key Methods
313
+
314
+ | Category | Methods |
315
+ |----------|---------|
316
+ | **KV** | `get`, `put`, `delete`, `get_prefix`, `get_range`, `get_all` |
317
+ | **Leases** | `lease`, `revoke_lease`, `refresh_lease`, `get_lease_info` |
318
+ | **Locks** | `lock` |
319
+ | **Watches** | `watch`, `watch_prefix`, `add_watch_callback`, `watch_once`, `cancel_watch` |
320
+ | **Transactions** | `transaction` |
321
+ | **Cluster** | `members`, `add_member`, `remove_member`, `update_member`, `status` |
322
+ | **Maintenance** | `compact`, `defragment`, `hash`, `create_alarm`, `list_alarms`, `disarm_alarm`, `snapshot` |
323
+
324
+ ## References
325
+
326
+ - Initial code 参考:
327
+ 1. [python-etcd3](https://github.com/kragniz/python-etcd3)
328
+ 2. [Go etcd client v3](https://pkg.go.dev/go.etcd.io/etcd/client/v3)
@@ -0,0 +1,42 @@
1
+ etcd3/__init__.py,sha256=_G81-O1yZsDNpMp_VvWDS8YNE9EgHEmgAhXdZ5VokP8,2811
2
+ etcd3/async_client.py,sha256=wnzRgPzmFN2Xe0TPxSL-bHTo_lnL01l_I2L6arMGUaw,54229
3
+ etcd3/async_client_extensions.py,sha256=IgW_2l0_h0AtFUFe6uPHYD-7UlBRMdMrwoWEsfDgI0U,34213
4
+ etcd3/async_endpoint.py,sha256=nDQtXCze1H8SAHIFJ0HYc1dBY2Nc0gw_t3SVT5d1Zmg,4587
5
+ etcd3/async_managers.py,sha256=bUbrqkeq1Rv_qhDGOkIqgSoqA-Lyo10Vbxc60KahtXc,17620
6
+ etcd3/async_monitor.py,sha256=iZPac3Pqn5bI0ONguJJctQsi4b2ft4hWPknug6jj5RY,453
7
+ etcd3/async_watch.py,sha256=m6I2BAoI54J-ZPVVMxao6XJQXvVyQ88Hp2QeauD9z20,15962
8
+ etcd3/client.py,sha256=43OjwOGlxgA3pKTkMZoL4Aem0_Axk7TDrb6-TB6veos,72347
9
+ etcd3/endpoint.py,sha256=aSMBmEitAADW0ABAZhc8L7jPYsQo8amEWKv6LJbsmC4,2589
10
+ etcd3/events.py,sha256=e4gTiUE4SRon8R-if9SKORIM_jr8XtVye-GcV-owKdw,972
11
+ etcd3/exceptions.py,sha256=mCsOaL6Pw1C72JZ8XqvTPiFsklk5CSjyDmMtdqv5Wv4,996
12
+ etcd3/leases.py,sha256=7eVl0rA2IGIOBTHbBn0Te9SQ-TKBr4v20X7iUzWYcSo,2804
13
+ etcd3/locks.py,sha256=KBtMS33FLGS13lcvJkIQPelnRLyTUXhqhJRs4De-uts,22533
14
+ etcd3/log.py,sha256=sMo6av48x9Nf_YDIWdSv8R-VUeVXi1n17wGDAAaFnlM,4282
15
+ etcd3/log_formatters.py,sha256=weWgMQjuI_cZFBEUORGCgkFJQIPvDUaiCLJN64t7s9o,7304
16
+ etcd3/log_sampling.py,sha256=7PhNz-HOZ6sHdBiXS29YJ3baHY_L1VwJaq4NZt2yX78,6692
17
+ etcd3/members.py,sha256=LLg6sAWDaaW0wzl_yBF3ialMQuaRKLFpFDEcmWcbqzY,1414
18
+ etcd3/observability.py,sha256=BLOUMs1H2icF-tEWB_RoZFKoCNoXZt1ZZERGfckesW4,14215
19
+ etcd3/shared.py,sha256=6-mhHNvGD6TPwq79EQa6oGxoZoGKR5oZSZkJeSbZpc8,16195
20
+ etcd3/trace.py,sha256=OtOmAZ5uz7v7dqPoAaEM8d94prK5f5tSEJq7Qi2w_0c,6650
21
+ etcd3/trace_attributes.py,sha256=oE_r3OMvbqm_BWlwKsTWjJucJEQ4osWu0HvwlyI9uc4,12025
22
+ etcd3/transactions.py,sha256=FJe-J5QBm-pV_J9dGIMor35PHAXvb61bjSELkg5Bw4U,7587
23
+ etcd3/utils.py,sha256=M1Rl9LwEvDUlkFBcaK6YPYLAPXjGmgt11d4WWqaNJHc,4811
24
+ etcd3/watch.py,sha256=rPEvOm1nQOPzj00OV6GFObZ2uSQqNl2pEcT7ut7xO44,8838
25
+ etcd3/exporters/__init__.py,sha256=9nMfrCsQ-pN5WUIY2ZHBsY4N3xmdD2Z24mDCi19hJE8,362
26
+ etcd3/exporters/prometheus.py,sha256=goA42MAwuR-FI4QH5K4jMOJt51tfC2V9BTeAf9dL5hg,10211
27
+ etcdrpc/__init__.py,sha256=msPVuBwxhRwvr3kn1r6RkUMalHpbXRfrg6A3bNmVu8s,547
28
+ etcdrpc/auth.proto,sha256=Py2ysZTy4i89vxRHBdXK-KcPCkojcEfWXDNbDajLRWk,1170
29
+ etcdrpc/auth_pb2.py,sha256=H-HBNZ64Mi0aqMIgjqvgdDcm5DBl9GIhqipRBCt1LlA,2120
30
+ etcdrpc/auth_pb2.pyi,sha256=vEV6IzZnKObqDI6fJPtXAqRbnKZC3vXy4UAem_XZVvY,1985
31
+ etcdrpc/auth_pb2_grpc.py,sha256=Xr3nckN8gNGTbWmOjz2OncHsPjwNBb4UaMGzqk4Jicg,892
32
+ etcdrpc/kv.proto,sha256=OI66aiqsN584XUhviA7SacMPEww1y6BrCa0gz9moBzE,1395
33
+ etcdrpc/kv_pb2.py,sha256=zMO9NYM2AUSzzu6GABRg_5ECpnQ7kcm4nBbBBFwrb94,1943
34
+ etcdrpc/kv_pb2.pyi,sha256=_McnDO1AgFNMqjAGbA1TuEs4TQH7tBbuR7tU-zckYmM,1741
35
+ etcdrpc/kv_pb2_grpc.py,sha256=PtuNgs59bODIsJCxPXtwfGwA26fVSYl7pDJwGNrFh4I,890
36
+ etcdrpc/rpc.proto,sha256=UD4vXbdPUNHSLSx7K_UltWqJjWswHBKSs0t9aNMA4qg,31351
37
+ etcdrpc/rpc_pb2.py,sha256=8qhnULNr8jym0qyivDVqJB86H_u1Rb_K8iY8FNT9e0s,30252
38
+ etcdrpc/rpc_pb2.pyi,sha256=TfPtHXIluO_D1rchVMVfoSgZTPQLk-YNhmcJBIt9XIk,35473
39
+ etcdrpc/rpc_pb2_grpc.py,sha256=HOifyKL0X3BmLkS1r0tWTMfyfChBiy76O6Ys12kT5n8,73810
40
+ python_etcd3_async-0.1.0.dist-info/METADATA,sha256=OSgYlmcjLRkK0MJYJrKBb84luLROrXAPYeYe7H-N854,7724
41
+ python_etcd3_async-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
42
+ python_etcd3_async-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any