{"id":13501282,"url":"https://github.com/Revolution1/etcd3-py","last_synced_at":"2025-03-29T08:32:19.012Z","repository":{"id":38359651,"uuid":"119022351","full_name":"Revolution1/etcd3-py","owner":"Revolution1","description":"Pure python client for etcd v3 (Using gRPC-JSON-Gateway)","archived":false,"fork":false,"pushed_at":"2024-02-23T23:17:14.000Z","size":361,"stargazers_count":107,"open_issues_count":33,"forks_count":26,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-24T05:49:44.282Z","etag":null,"topics":["etcd","etcd-client","etcd3","etcd3-client","grpc-json-gateway","python-client"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Revolution1.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.rst","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-26T07:50:44.000Z","updated_at":"2025-02-18T15:05:07.000Z","dependencies_parsed_at":"2024-01-16T10:35:21.926Z","dependency_job_id":"9b1969c3-d820-4f95-9910-9e524f21d602","html_url":"https://github.com/Revolution1/etcd3-py","commit_stats":{"total_commits":150,"total_committers":8,"mean_commits":18.75,"dds":0.4066666666666666,"last_synced_commit":"deea4583c2e61a7d8af58c8790d4fa50cf602544"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Revolution1%2Fetcd3-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Revolution1%2Fetcd3-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Revolution1%2Fetcd3-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Revolution1%2Fetcd3-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Revolution1","download_url":"https://codeload.github.com/Revolution1/etcd3-py/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246162092,"owners_count":20733351,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["etcd","etcd-client","etcd3","etcd3-client","grpc-json-gateway","python-client"],"created_at":"2024-07-31T22:01:31.510Z","updated_at":"2025-03-29T08:32:17.765Z","avatar_url":"https://github.com/Revolution1.png","language":"Python","readme":"# etcd3-py\n\n[![pypi](https://img.shields.io/pypi/v/etcd3-py.svg)](https://pypi.python.org/pypi/etcd3-py)\n[![travis](https://travis-ci.org/Revolution1/etcd3-py.svg?branch=master)](https://travis-ci.org/Revolution1/etcd3-py)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/9448814cd66b4a568365bc050d88270c)](https://www.codacy.com/app/revol/etcd3-py?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=Revolution1/etcd3-py\u0026amp;utm_campaign=Badge_Grade)\n[![codecov](https://codecov.io/gh/Revolution1/etcd3-py/branch/master/graph/badge.svg)](https://codecov.io/gh/Revolution1/etcd3-py)\n[![doc](https://readthedocs.org/projects/etcd3-py/badge/?version=latest)](http://etcd3-py.readthedocs.io/en/latest/?badge=latest)\n[![updates](https://pyup.io/repos/github/Revolution1/etcd3-py/shield.svg)](https://pyup.io/repos/github/Revolution1/etcd3-py/)\n[![python3](https://pyup.io/repos/github/Revolution1/etcd3-py/python-3-shield.svg)](https://pyup.io/repos/github/Revolution1/etcd3-py/)\n\nPython client for etcd v3 (Using gRPC-JSON-Gateway)\n\n* Free software: Apache Software License 2.0\n* Source Code: https://github.com/Revolution1/etcd3-py\n* Documentation: https://etcd3-py.readthedocs.io.\n* etcd version required: v3.2.2+\n\nNotice: The authentication header through gRPC-JSON-Gateway only supported in [etcd v3.3.0+](https://github.com/coreos/etcd/pull/7999)\n\n## Features\n\n* [x] Support python2.7 and python3.5+ (aiohttp requires python3.5.2+)\n* [x] Sync client based on requests\n* [x] Async client based on aiohttp\n* [x] TLS Connection\n* [x] support APIs\n    * [x] Auth\n    * [x] KV\n    * [x] Watch\n    * [x] Cluster\n    * [x] Lease\n    * [x] Lock\n    * [x] Maintenance\n    * [x] Extra APIs\n* [x] stateful utilities\n    * [x] Watch\n    * [x] Lease\n    * [x] Transaction\n    * [x] Lock\n\n## Quick Start\n\n**Install**\n```bash\n$ pip install --upgrade etcd3-py\n```\n\n**Sync Client**\n```python\n\u003e\u003e\u003e from etcd3 import Client\n\u003e\u003e\u003e client = Client('127.0.0.1', 2379, cert=(CERT_PATH, KEY_PATH), verify=CA_PATH)\n\u003e\u003e\u003e client.version()\nEtcdVersion(etcdserver='3.3.0-rc.4', etcdcluster='3.3.0')\n\u003e\u003e\u003e client.put('foo', 'bar')\netcdserverpbPutResponse(header=etcdserverpbResponseHeader(cluster_id=11588568905070377092, member_id=128088275939295631, revision=15433, raft_term=4))\n\u003e\u003e\u003e client.range('foo').kvs\n[mvccpbKeyValue(key=b'foo', create_revision=15429, mod_revision=15433, version=5, value=b'bar')]\n```\n\n**Async Client (Python3.5+)**\n```python\n\u003e\u003e\u003e import asyncio\n\u003e\u003e\u003e from etcd3 import AioClient\n\u003e\u003e\u003e client = AioClient('127.0.0.1', 2379)\n\u003e\u003e\u003e async def getFoo():\n...     await client.put('foo', 'bar')\n...     r = await client.range('foo')\n...     print('key:', r.kvs[0].key, 'value:', r.kvs[0].value)\n\u003e\u003e\u003e loop = asyncio.get_event_loop()\n\u003e\u003e\u003e loop.run_until_complete(getFoo())\nkey: b'foo' value: b'bar'\n```\n\n**Transaction Util**\n```python\n\u003e\u003e\u003e from etcd3 import Client\n\u003e\u003e\u003e txn = Client().Txn()\n\u003e\u003e\u003e txn.compare(txn.key('foo').value == 'bar')\n\u003e\u003e\u003e txn.success(txn.put('foo', 'bra'))\n\u003e\u003e\u003e txn.commit()\netcdserverpbTxnResponse(header=etcdserverpbResponseHeader(cluster_id=11588568905070377092, member_id=128088275939295631, revision=15656, raft_term=4), succeeded=True, responses=[etcdserverpbResponseOp(response_put=etcdserverpbPutResponse(header=etcdserverpbResponseHeader(revision=15656)))])\n```\n\n**Lease Util**\n```python\n\u003e\u003e\u003e from etcd3 import Client\n\u003e\u003e\u003e client = Client()\n\u003e\u003e\u003e with client.Lease(ttl=5) as lease:\n...     client.put('foo', 'bar', lease=lease.ID)\n...     client.put('fizz', 'buzz', lease=lease.ID)\n...     r = lease.time_to_live(keys=True)\n...     assert set(r.keys) == {b'foo', b'fizz'}\n...     assert lease.alive()\n```\n\n**Watch Util**\n```python\n\u003e\u003e\u003e from etcd3 import Client\n\u003e\u003e\u003e client = Client()\n\u003e\u003e\u003e watcher = c.Watcher(all=True, progress_notify=True, prev_kv=True)\n\u003e\u003e\u003e w.onEvent('f.*', lambda e: print(e.key, e.value))\n\u003e\u003e\u003e w.runDaemon()\n\u003e\u003e\u003e # etcdctl put foo bar\n\u003e\u003e\u003e # etcdctl put foz bar\nb'foo' b'bar'\nb'foz' b'bar'\n\u003e\u003e\u003e w.stop()\n```\n\n**Lock Util**\n```python\n\u003e\u003e\u003e import time\n\u003e\u003e\u003e from threading import Thread\n\u003e\u003e\u003e from etcd3 import Client\n\u003e\u003e\u003e client = Client()\n\u003e\u003e\u003e name = 'lock_name'\n\u003e\u003e\u003e def user1():\n...     with client.Lock(name, lock_ttl=5):\n...         print('user1 got the lock')\n...         time.sleep(5)\n...         print('user1 releasing the lock')\n\u003e\u003e\u003e def user2():\n...     with client.Lock(name, lock_ttl=5):\n...         print('user2 got the lock')\n...         time.sleep(5)\n...         print('user2 releasing the lock')\n\u003e\u003e\u003e t1 = Thread(target=user1, daemon=True)\n\u003e\u003e\u003e t2 = Thread(target=user2, daemon=True)\n\u003e\u003e\u003e t1.start()\n\u003e\u003e\u003e t2.start()\n\u003e\u003e\u003e t1.join()\n\u003e\u003e\u003e t2.join()\nuser1 got the lock\nuser1 releasing the lock\nuser2 got the lock\nuser2 releasing the lock\n```\n\n**Start a single-node etcd using docker**\n```bash\nexport NODE1=0.0.0.0\nexport ETCD_VER=v3.3\ndocker run -d \\\n-p 2379:2379 \\\n-p 2380:2380 \\\n--volume=/tmp/etcd3-data:/etcd-data \\\n--name etcd3 quay.io/coreos/etcd:$ETCD_VER \\\n/usr/local/bin/etcd \\\n--data-dir=/etcd-data --name node1 \\\n--initial-advertise-peer-urls http://${NODE1}:2380 --listen-peer-urls http://${NODE1}:2380 \\\n--advertise-client-urls http://${NODE1}:2379 --listen-client-urls http://${NODE1}:2379 \\\n--initial-cluster node1=http://${NODE1}:2380\n```\n\n## FAQ\n\n**Q:** authentication seems not working? Try calling api of a auth-enabled etcd server returned error \"ErrUserEmpty error:'etcdserver: user name is empty'\"\n\n**A:** Take a look at [#41](https://github.com/Revolution1/etcd3-py/issues/41), currently etcd3-py dose not authenticate automatically, you need to call client.auth() by yourself.\n\n## TODO\n\n- [ ] human friendly middle level apis\n- [ ] able to expose json or raw response to user\n- [ ] add election api\n- [ ] benchmark\n- [ ] python-etcd(etcd v2) compatible client\n- [ ] etcd browser\n- [ ] support etcd v3.4.x\n","funding_links":[],"categories":["Python"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRevolution1%2Fetcd3-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRevolution1%2Fetcd3-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRevolution1%2Fetcd3-py/lists"}