Package: etcetra
etcetraPure python asyncio Etcd client.InstallationpipinstalletcetraAPI DocumentationReferhere.Basic usageAll etcd operations managed by etcetra can be executed usingEtcdClient.EtcdClientinstance is a wrapper which holds connection information to Etcd channel.
This instance is reusable, since actual connection to gRPC channel will be established
when you initiate connection calls (see below).frometcetraimportEtcdClient,HostPortPairetcd=EtcdClient(HostPortPair('127.0.0.1',2379))Like I mentioned above, actual connection establishment with Etcd's gRPC channel will be done
when you callEtcdClient.connect(). This call returns async context manager, which managesEtcdCommunicatorinstance.asyncwithetcd.connect()ascommunicator:awaitcommunicator.put('testkey','testvalue')value=awaitcommunicator.get('testkey')print(value)# testvalueEtcdCommunicator.get_prefix(prefix)will return a dictionary containing all key-values with given key prefix.asyncwithetcd.connect()ascommunicator:awaitcommunicator.put('/testdir','root')awaitcommunicator.put('/testdir/1','1')awaitcommunicator.put('/testdir/2','2')awaitcommunicator.put('/testdir/2/3','3')test_dir=awaitcommunicator.get_prefix('/testdir')print(test_dir)# {'/testdir': 'root', '/testdir/1': '1', '/testdir/2': '2', '/testdir/2/3': '3'}Operating with Etcd lockJust likeEtcdClient.connect(), you can easilly use etcd lock by callingEtcdClient.with_lock(lock_name, timeout=None).asyncdeffirst():asyncwithetcd.with_lock('foolock')ascommunicator:value=awaitcommunicator.get('testkey')print('first:',value,end=' | ')asyncdefsecond():awaitasyncio.sleep(0.1)asyncwithetcd.with_lock('foolock')ascommunicator:value=awaitcommunicator.get('testkey')print('second:',value)asyncwithetcd.connect()ascommunicator:awaitcommunicator.put('testkey','testvalue')awaitasyncio.gather(first(),second())# first: testvalue | second: testvalueAddingtimeoutparameter toEtcdClient.with_lock()call will add a timeout to lock acquiring process.asyncdeffirst():asyncwithetcd.with_lock('foolock')ascommunicator:value=awaitcommunicator.get('testkey')print('first:',value)awaitasyncio.sleep(10)asyncdefsecond():awaitasyncio.sleep(0.1)asyncwithetcd.with_lock('foolock',timeout=5)ascommunicator:value=awaitcommunicator.get('testkey')print('second:',value)asyncwithetcd.connect()ascommunicator:awaitcommunicator.put('testkey','testvalue')awaitasyncio.gather(first(),second())# asyncio.TimeoutError followed by first: testvalue outputAddingttlparameter toEtcdClient.with_lock()call will force lock to be released after given seconds.asyncdeffirst():asyncwithetcd.with_lock('foolock',ttl=5)ascommunicator:awaitasyncio.sleep(10)awaitfirst()# on other fileimporttimeasyncdefsecond():start=time.time()asyncwithetcd.with_lock('foolock',ttl=5)ascommunicator:print(f'acquired lock after{time.time()-start}seconds')awaitsecond()# acquired lock after 4.756163120269775 secondsWatchYou can watch changes on key withEtcdCommunicator.watch(key).asyncdefwatch():asyncwithetcd.connect()ascommunicator:asyncforeventincommunicator.watch('testkey'):print(event.event,event.value)asyncdefupdate():awaitasyncio.sleep(0.1)asyncwithetcd.connect()ascommunicator:awaitcommunicator.put('testkey','1')awaitcommunicator.put('testkey','2')awaitcommunicator.put('testkey','3')awaitcommunicator.put('testkey','4')awaitcommunicator.put('testkey','5')awaitasyncio.gather(watch(),update())# WatchEventType.PUT 1# WatchEventType.PUT 2# WatchEventType.PUT 3# WatchEventType.PUT 4# WatchEventType.PUT 5Watching changes on keys with specific prefix can be also done byEtcdCommunicator.watch_prefix(key_prefix).asyncdefwatch():asyncwithetcd.connect()ascommunicator:asyncforeventincommunicator.watch_prefix('/testdir'):print(event.event,event.key,event.value)asyncdefupdate():awaitasyncio.sleep(0.1)asyncwithetcd.connect()ascommunicator:awaitcommunicator.put('/testdir','1')awaitcommunicator.put('/testdir/foo','2')awaitcommunicator.put('/testdir/bar','3')awaitcommunicator.put('/testdir/foo/baz','4')awaitasyncio.gather(watch(),update())# WatchEventType.PUT /testdir 1# WatchEventType.PUT /testdir/foo 2# WatchEventType.PUT /testdir/bar 3# WatchEventType.PUT /testdir/foo/baz 4TransactionYou can run etcd transaction by callingEtcdCommunicator.txn_compare(compares, txn_builder).Constructing comparesConstructing compare operations can be done by comparingCompareKeyinstance with value with Python's built-in comparison operators (==,!=,>,<).frometcetraimportCompareKeycompares=[CompareKey('cmpkey1').value=='foo',CompareKey('cmpkey2').value>'bar',]Executing transaction callsasyncwithetcd.connect()withcommunicator:awaitcommunicator.put('cmpkey1','foo')awaitcommunicator.put('cmpkey2','baz')awaitcommunicator.put('successkey','asdf')def_txn(success,failure):success.get('successkey')values=awaitcommunicator.txn_compare(compares,_txn)print(values)# ['asdf']compares=[CompareKey('cmpkey1').value=='foo',CompareKey('cmpkey2').value<'bar',]asyncwithetcd.connect()withcommunicator:awaitcommunicator.put('failurekey','asdf')def_txn(success,failure):failure.get('failurekey')values=awaitcommunicator.txn_compare(compares,_txn)print(values)# ['asdf']If you don't need compare conditions for transaction, you can useEtcdCommunicator.txn(txn_builder),
which is a shorthand forEtcdCommunicator.txn_compare([], lambda success, failure: txn_builder(success)).asyncwithetcd.connect()withcommunicator:def_txn(action):action.get('cmpkey1')action.get('cmpkey2')values=awaitcommunicator.txn(_txn)print(values)# ['foo', 'baz']ContributingCompiling Protobuf$scripts/compile_protobuf.py<targetEtcdversion>Generating documentation$cddocs
$makemarkdown
$mv_build/markdown/index.mfreferences.md