Skip to content

Commit 4e3d849

Browse files
committed
update
1 parent 874717c commit 4e3d849

File tree

10 files changed

+145
-41
lines changed

10 files changed

+145
-41
lines changed

.github/workflows/python-package.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ name: Python package
55

66
on:
77
push:
8-
branches: [master,dev]
8+
branches: [master, dev]
99
pull_request:
1010
branches: [master]
1111

@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
python-version: [3.8, 3.9]
17+
python-version: [3.8, 3.9, 3.11]
1818

1919
steps:
2020
- uses: actions/checkout@v2

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,4 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130+
data

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 0,0,2
2+
3+
+ 异步代理依赖由aetcd3改为aetcd
4+
+ 接口变更,现在可以通过url或者连接参数初始化实例
5+
+ 由于依赖由aetcd3改为aetcd,现在推荐key和value都统一使用bytes类型
6+
17
# 0.0.1
28

39
项目创建

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
提供etcd的代理对象功能.本项目代理的对象是
44

5-
+ aio模式代理aetcd3
6-
+ 普通模式代理etcd3
5+
+ aio模式代理[aetcd](https://aetcd.readthedocs.io/en/latest/index.html)
6+
+ 普通模式代理[etcd3](https://python-etcd3.readthedocs.io/en/latest/)
77

8-
需要注意这两个模块本身冲突,也就是说如果`import etcd3`就不能再`import aetcd3`.因此同一个程序中不能混用两种模式.如果并不确定使用的是什么模式可以在代理的`aio`字段进行确认
8+
需要注意这两个模块本身冲突,也就是说如果`import etcd3`就不能再`import aetcd`.因此同一个程序中不能混用两种模式.如果并不确定使用的是什么模式可以在代理的`aio`字段进行确认
99

1010
## 安装
1111

docker-compose.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
version: "2.4"
2+
x-log: &default-log
3+
options:
4+
max-size: "10m"
5+
max-file: "3"
6+
services:
7+
etcd1:
8+
image: docker.io/bitnami/etcd:3.5.1
9+
logging:
10+
options:
11+
max-file: 3
12+
max-size: 10m
13+
environment:
14+
- ALLOW_NONE_AUTHENTICATION=yes
15+
- ETCDCTL_API=3
16+
ports:
17+
- 12379:2379
18+
- 12380:2380
19+
volumes:
20+
- ./data:/bitnami/etcd

etcdproxy/__init__.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,54 @@
11

2-
from typing import Optional, Any
32
from pyproxypattern import Proxy
4-
from .url_parser import etcdurl_parser
3+
from typing import Optional, Any
4+
from .url_parser import etcdurl_parser, EtcdConnParams
55

66

77
class EtcdProxy(Proxy):
88
"""etcd的代理类."""
99
__slots__ = ('instance', "_callbacks", "_instance_check", "aio")
1010

11-
def __init__(self, *, url: Optional[str] = None) -> None:
11+
def __init__(self, *, conn_params: Optional[EtcdConnParams] = None, aio: bool = False, url: Optional[str] = None) -> None:
1212
"""初始化一个etcd代理.
1313
1414
Args:
1515
url (Optional[str], optional): etcd的url路径,注意schema为`etcd`或`etcd+async`. Defaults to None.
1616
"""
1717

1818
if url:
19-
instance = self.new_instance(url)
19+
instance = self.new_instance_from_url(url)
20+
super().__init__(instance)
21+
elif conn_params:
22+
instance = self.new_instance(conn_params, aio=aio)
2023
super().__init__(instance)
2124
else:
2225
super().__init__()
2326

24-
def new_instance(self, url: str) -> Any:
25-
aio, configs = etcdurl_parser(url)
27+
def new_instance(self, conn_params: EtcdConnParams, *, aio: bool = False) -> Any:
2628
if aio:
2729
self.aio = True
28-
import aetcd3
29-
return aetcd3.client(**configs)
30+
import aetcd
31+
return aetcd.Client(**conn_params)
3032
else:
3133
self.aio = False
3234
import etcd3
33-
return etcd3.client(**configs)
35+
return etcd3.client(**conn_params)
36+
37+
def initialize_from_params(self, conn_params: EtcdConnParams, *, aio: bool = False) -> None:
38+
"""使用参数初始化etcd连接实例
39+
40+
Args:
41+
conn_params (EtcdConnParams): 连接参数
42+
aio (bool, optional): 是否使用异步接口. Defaults to False.
43+
"""
44+
instance = self.new_instance(conn_params, aio=aio)
45+
self.initialize(instance)
46+
47+
def new_instance_from_url(self, url: str) -> Any:
48+
urlinfo = etcdurl_parser(url)
49+
return self.new_instance(urlinfo["conn_params"], aio=urlinfo["aio"])
3450

3551
def initialize_from_url(self, url: str) -> None:
3652
"""从url初始化."""
37-
instance = self.new_instance(url)
53+
instance = self.new_instance_from_url(url)
3854
self.initialize(instance)

etcdproxy/url_parser.py

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,36 @@
11
from urllib.parse import urlparse, parse_qsl
2-
from typing import Tuple, Dict, Union, ItemsView
2+
from typing import Dict, Union, ItemsView, TypedDict, Optional, Sequence, Literal
33

44

5-
def etcdurl_parser(url: str) -> Tuple[bool, Dict[str, Union[str, int, ItemsView]]]:
6-
"""解析etcd路径
5+
class EtcdConnParams(TypedDict, total=False):
6+
host: str
7+
port: int
8+
user: str
9+
password: str
10+
timeout: int
11+
ca_cert: str
12+
cert_key: str
13+
cert_cert: str
14+
grpc_options: ItemsView[str, Union[str, int]]
15+
options: Dict[str, Union[str, int]]
16+
17+
18+
class ParserResult(TypedDict, total=False):
19+
aio: bool
20+
conn_params: EtcdConnParams
21+
key: str
22+
is_prefix: bool
23+
24+
25+
def etcdurl_parser(url: str) -> ParserResult:
26+
"""解析etcd路径.
27+
28+
schema为`etcd`返回的`aio`字段为False,为`etcd+async`返回的`aio`字段为True,其他都为非法.
29+
url中host,port,user,password部分对应etcd中对应内容
30+
url中的path会被作为返回的`key`字段,如果其首字符为`/`则会被去掉,
31+
当返回中有key时如果在url的参数部分设置is_prefix=true则返回的`is_prefix`会被置为True,否则置为False,
32+
`ca_cert`, `cert_key`, `cert_cert`和`timeout`也在url的参数部分设置,其他的参数则会作为grpc参数传入
33+
其他的连接参数可以在url的参数部分设置
734
835
Args:
936
url (str): etcd的地址,注意请以`etcd://`或者`etcd+async://`开头
@@ -12,12 +39,15 @@ def etcdurl_parser(url: str) -> Tuple[bool, Dict[str, Union[str, int, ItemsView]
1239
AttributeError: schema 必须为etcd
1340
1441
Returns:
15-
Dict[str, Union[str, int, Dict[str, str]]]: 初始化etcd客户端的参数数据
42+
Dict[bool,str,bool, Union[str, int, Dict[str, str]]]: 初始化etcd客户端的参数数据,分别为是否为
1643
"""
17-
keys = ("timeout", "ca_cert", "cert_key", "cert_cert")
18-
aio = False
19-
intkeys = ("timeout",)
20-
result: Dict[str, Union[str, int, ItemsView]] = {
44+
conn_str_params = ("ca_cert", "cert_key", "cert_cert")
45+
conn_int_params = ("timeout",)
46+
aio: bool = False
47+
key: Optional[str] = None
48+
is_prefix: Optional[bool] = None
49+
50+
conn_params: EtcdConnParams = {
2151
"host": '127.0.0.1',
2252
"port": 2379,
2353
}
@@ -28,28 +58,57 @@ def etcdurl_parser(url: str) -> Tuple[bool, Dict[str, Union[str, int, ItemsView]
2858
if schema == "etcd+async":
2959
aio = True
3060
if parse_result.username:
31-
result.update({"user": parse_result.username})
61+
conn_params.update({"user": parse_result.username})
3262
if parse_result.password:
33-
result.update({"password": parse_result.password})
63+
conn_params.update({"password": parse_result.password})
3464
if parse_result.port:
35-
result.update({"port": parse_result.port})
65+
conn_params.update({"port": parse_result.port})
3666
if parse_result.hostname:
37-
result.update({"host": parse_result.hostname})
67+
conn_params.update({"host": parse_result.hostname})
68+
if parse_result.path:
69+
if parse_result.path not in ("", "/"):
70+
if parse_result.path.startswith("/"):
71+
key = parse_result.path[1:]
72+
else:
73+
key = parse_result.path
3874
if parse_result.query:
3975
sql_result = dict(parse_qsl(parse_result.query))
4076
_grpc_options: Dict[str, Union[str, int]] = {}
77+
if key is not None:
78+
is_prefix = False
4179
for k, v in sql_result.items():
42-
if k in keys:
43-
if k in intkeys:
44-
result.update({k: int(v)})
80+
if k == "is_prefix" and key is not None:
81+
print("444")
82+
print(v)
83+
if v.lower() == "false" or v == "0":
84+
is_prefix = False
4585
else:
46-
result.update({k: v})
86+
is_prefix = True
87+
elif k in conn_str_params:
88+
conn_params[k] = v # type: ignore
89+
elif k in conn_int_params:
90+
conn_params.update({k: int(v)}) # type: ignore
4791
else:
4892
if v.isdigit():
4993
_grpc_options.update({k: int(v)})
5094
else:
5195
_grpc_options.update({k: v})
5296
if _grpc_options:
53-
grpc_options = _grpc_options.items()
54-
result.update({"grpc_options": grpc_options})
55-
return aio, result
97+
if aio:
98+
conn_params.update({"options": _grpc_options})
99+
else:
100+
grpc_options = _grpc_options.items()
101+
conn_params.update({"grpc_options": grpc_options})
102+
result: ParserResult = {
103+
"aio": aio,
104+
"conn_params": conn_params
105+
}
106+
if key is not None:
107+
result.update({
108+
"key": key
109+
})
110+
if is_prefix is not None:
111+
result.update({
112+
"is_prefix": is_prefix
113+
})
114+
return result

setup.cfg

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = etcdproxy
3-
version = 0.0.1
3+
version = 0.0.2
44
author = hsz
55
author_email = hsz1273327@gmail.com
66
description = etcd的代理对象
@@ -12,14 +12,16 @@ classifiers =
1212
License :: OSI Approved :: MIT License
1313
Programming Language :: Python :: 3.8
1414
Programming Language :: Python :: 3.9
15+
Programming Language :: Python :: 3.10
16+
Programming Language :: Python :: 3.11
1517

1618
[options]
1719
zip_safe = False
1820
include_package_data = True
1921
packages = find:
2022
install_requires =
2123
etcd3 >= 0.12.0
22-
aetcd3 >= 0.1.0a7
24+
aetcd >= 1.0.0a2
2325
pyproxypattern >= 0.0.1
2426
tests_require =
2527
coverage >= 5.5

test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
etcd.initialize_from_url("etcd://localhost:12379")
66

7-
etcd.put('/key', 'dooot')
8-
print(etcd.get('/key'))
7+
etcd.put(b'/key', b'dooot')
8+
print(etcd.get(b'/key'))
99

10-
etcd.close()
10+
etcd.close()

testaio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ async def main() -> None:
66
etcd = EtcdProxy()
77
etcd.initialize_from_url("etcd+async://localhost:12379")
88
print(etcd.aio)
9-
await etcd.put('/key', 'dooot')
10-
print(await etcd.get('/key'))
9+
await etcd.put(b'/key', b'doootaio')
10+
print(await etcd.get(b'/key'))
1111
await etcd.close()
1212

1313
asyncio.run(main())

0 commit comments

Comments
 (0)