Skip to content

Commit

Permalink
first release
Browse files Browse the repository at this point in the history
  • Loading branch information
revol.cai committed Mar 18, 2018
1 parent 71f0a31 commit 6c61292
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 13 deletions.
54 changes: 42 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,65 @@ Notice: The authentication header through gRPC-JSON-Gateway only supported in [e
Features
========

* [x] support python2.7 and python3.5+
* [x] sync client based on requests
* [x] async client based on aiohttp
* [x] support etcd3 gRPC-JSON-Gateway including stream
* [x] response modelizing based on etcd3's swagger spec
* [x] generate code from swagger spec
* [x] auto unit testing
* [ ] support APIs
* [x] Support python2.7 and python3.5+
* [x] Sync client based on requests
* [x] Async client based on aiohttp
* [x] Support etcd3 gRPC-JSON-Gateway including stream
* [x] Response modelizing based on etcd3's swagger spec
* [x] Generate code from swagger spec
* [ ] TLS Connection
* [x] support APIs
* [x] Auth
* [x] KV
* [ ] Watch
* [x] Watch
* [x] Cluster
* [x] Lease
* [x] Maintenance
* [x] Extra APIs
* [ ] stateful utilities
* [ ] Watch
* [ ] Lease
* [ ] Transaction
* [ ] Lock

Quick Start
===========

**Async Client**
**Install**
```bash
$ pip install etcd3-py
```

**Sync Client**
```python
>>> from etcd3 import Client
>>> client = Client('127.0.0.1', 2379)
>>> client.version()
EtcdVersion(etcdserver='3.3.0-rc.4', etcdcluster='3.3.0')
>>> client.put('foo', 'bar')
etcdserverpbPutResponse(header=etcdserverpbResponseHeader(cluster_id=11588568905070377092, member_id=128088275939295631, revision=15433, raft_term=4))
>>> client.range('foo').kvs
[mvccpbKeyValue(key=b'foo', create_revision=15429, mod_revision=15433, version=5, value=b'bar')]
```

**Async Client (Python3.5+)**
```python
>>> import asyncio
>>> from etcd3 import AioClient
>>> client = AioClient('127.0.0.1', 2379)
>>> async def getFoo():
... await client.put('foo', 'bar')
... r = await client.range('foo')
... print('key:', r.kvs[0].key, 'value:', r.kvs[0].value)
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(getFoo())
key: b'foo' value: b'bar'
```


TODO
====

- [ ] performance test
- [ ] extract request response models to improve performance
- [ ] benchmark
- [ ] python-etcd(etcd v2) compatible client
- [ ] etcd browser
2 changes: 1 addition & 1 deletion etcd3/swagger_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def __getattribute__(self, key):
return super(SwaggerSpec, self).__getattribute__(key)

def __dir__(self):
return [k for k in type(self).__dict__.keys() if not k.startswith('__')] + self.spec.keys()
return [k for k in type(self).__dict__.keys() if not k.startswith('__')] + list(self.spec.keys())

def __repr__(self):
return "<SwaggerSpec '%s'>" % self.spec.get('info', {}).get('title')
Expand Down
59 changes: 59 additions & 0 deletions scripts/basemodel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# no use for now
import enum

import six


class BaseModel(object):
pass


class ObjectModel(BaseModel):
def __init__(self, data, node):
if not isinstance(data, dict):
raise TypeError("A dict expected, got a '%s' instead" % type(data))
self._node = node
self._data = data
self._name = node._path[-1]
for k in node.properties._keys():
if k not in data:
continue
v = data[k]
m = node.properties._get(k)
if m._is_schema:
m = m.getModel()
v = m(v)
setattr(self, k, v)

def __iter__(self):
return iter(self._data)

def __contains__(self, item):
return item in self._data

def __repr__(self):
return '%s(%s)' % (
self._name,
', '.join(['%s=%s' % (k, repr(v)) for k, v in six.iteritems(self.__dict__) if k in self._data])
)


class ArrayModel(BaseModel, list):
def __init__(self, data, node):
if not isinstance(data, (list, tuple)):
raise TypeError("A list or tuple expected, got a '%s' instead" % type(data))
self._data = data
self._node = node
m = node.items.getModel()
for i in data:
self.append(m(i))


class ImmutableModel(BaseModel):
def __init__(self, data):
self._data = data

def __get__(self, instance, owner):
if isinstance(self._data, enum.Enum):
return self._data.value
return self._data

0 comments on commit 6c61292

Please sign in to comment.