etcd-client-py 0.2.4__tar.gz → 0.3.0__tar.gz
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.
- etcd_client_py-0.3.0/.github/workflows/test.yml +41 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/Cargo.lock +1 -1
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/Cargo.toml +1 -1
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/Makefile +1 -1
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/PKG-INFO +2 -2
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/etcd_client.pyi +28 -36
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/communicator.rs +34 -66
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/compare.rs +14 -6
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/error.rs +4 -4
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/lib.rs +4 -4
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/lock_manager.rs +16 -15
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/txn.rs +9 -4
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/watch.rs +2 -2
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/watch_event.rs +9 -9
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/tests/harness.py +28 -24
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/.github/workflows/ci.yml +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/.gitignore +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/LICENSE +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/README.md +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/py.typed +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/pyproject.toml +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/pytest.ini +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/requirements.txt +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/client.rs +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/condvar.rs +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/txn_response.rs +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/src/watch_event_stream.rs +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/tests/conftest.py +0 -0
- {etcd_client_py-0.2.4 → etcd_client_py-0.3.0}/tests/test.py +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v3
|
|
11
|
+
- name: Install etcd
|
|
12
|
+
run: |
|
|
13
|
+
wget https://github.com/etcd-io/etcd/releases/download/v3.4.10/etcd-v3.4.10-linux-amd64.tar.gz
|
|
14
|
+
tar -xzf etcd-v3.4.10-linux-amd64.tar.gz
|
|
15
|
+
mv etcd-v3.4.10-linux-amd64/etcd* /usr/local/bin/
|
|
16
|
+
|
|
17
|
+
- name: Set up Python as Runtime
|
|
18
|
+
uses: actions/setup-python@v4
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
|
|
22
|
+
- name: Set up Rust toolchain
|
|
23
|
+
uses: actions-rs/toolchain@v1
|
|
24
|
+
with:
|
|
25
|
+
toolchain: stable
|
|
26
|
+
override: true
|
|
27
|
+
|
|
28
|
+
- name: Install protobuf compiler
|
|
29
|
+
run: |
|
|
30
|
+
sudo apt install -y protobuf-compiler
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies and build the package
|
|
33
|
+
run: |
|
|
34
|
+
python -m pip install --upgrade pip
|
|
35
|
+
python -m pip install -r requirements.txt
|
|
36
|
+
python -m maturin build
|
|
37
|
+
python -m pip install .
|
|
38
|
+
|
|
39
|
+
- name: Test
|
|
40
|
+
run: |
|
|
41
|
+
python -m pytest
|
|
@@ -8,7 +8,7 @@ from typing import Any, AsyncIterator, Final, Optional
|
|
|
8
8
|
|
|
9
9
|
@dataclass
|
|
10
10
|
class EtcdLockOption:
|
|
11
|
-
lock_name:
|
|
11
|
+
lock_name: bytes
|
|
12
12
|
timeout: Optional[float]
|
|
13
13
|
ttl: Optional[int]
|
|
14
14
|
|
|
@@ -30,31 +30,31 @@ class CompareOp:
|
|
|
30
30
|
|
|
31
31
|
class Compare:
|
|
32
32
|
@staticmethod
|
|
33
|
-
def version(key:
|
|
33
|
+
def version(key: bytes, cmp: "CompareOp", version: int) -> "Compare": ...
|
|
34
34
|
"""
|
|
35
35
|
Compares the version of the given key.
|
|
36
36
|
"""
|
|
37
37
|
@staticmethod
|
|
38
|
-
def create_revision(key:
|
|
38
|
+
def create_revision(key: bytes, cmp: "CompareOp", revision: int) -> "Compare": ...
|
|
39
39
|
"""
|
|
40
40
|
Compares the creation revision of the given key.
|
|
41
41
|
"""
|
|
42
42
|
@staticmethod
|
|
43
|
-
def mod_revision(key:
|
|
43
|
+
def mod_revision(key: bytes, cmp: "CompareOp", revision: int) -> "Compare": ...
|
|
44
44
|
"""
|
|
45
45
|
Compares the last modified revision of the given key.
|
|
46
46
|
"""
|
|
47
47
|
@staticmethod
|
|
48
|
-
def value(key:
|
|
48
|
+
def value(key: bytes, cmp: "CompareOp", value: bytes) -> "Compare": ...
|
|
49
49
|
"""
|
|
50
50
|
Compares the value of the given key.
|
|
51
51
|
"""
|
|
52
52
|
@staticmethod
|
|
53
|
-
def lease(key:
|
|
53
|
+
def lease(key: bytes, cmp: "CompareOp", lease: int) -> "Compare": ...
|
|
54
54
|
"""
|
|
55
55
|
Compares the lease id of the given key.
|
|
56
56
|
"""
|
|
57
|
-
def with_range(self, end:
|
|
57
|
+
def with_range(self, end: bytes) -> "Compare": ...
|
|
58
58
|
"""
|
|
59
59
|
Sets the comparison to scan the range [key, end).
|
|
60
60
|
"""
|
|
@@ -95,11 +95,11 @@ class TxnOp:
|
|
|
95
95
|
"""
|
|
96
96
|
|
|
97
97
|
@staticmethod
|
|
98
|
-
def get(key:
|
|
98
|
+
def get(key: bytes) -> "TxnOp": ...
|
|
99
99
|
@staticmethod
|
|
100
|
-
def put(key:
|
|
100
|
+
def put(key: bytes, value: bytes) -> "TxnOp": ...
|
|
101
101
|
@staticmethod
|
|
102
|
-
def delete(key:
|
|
102
|
+
def delete(key: bytes) -> "TxnOp": ...
|
|
103
103
|
@staticmethod
|
|
104
104
|
def txn(txn: "Txn") -> "TxnOp": ...
|
|
105
105
|
|
|
@@ -154,21 +154,15 @@ class CondVar:
|
|
|
154
154
|
""" """
|
|
155
155
|
|
|
156
156
|
class Communicator:
|
|
157
|
-
async def get(self, key:
|
|
157
|
+
async def get(self, key: bytes) -> list[int]:
|
|
158
158
|
"""
|
|
159
159
|
Gets the key from the key-value store.
|
|
160
160
|
"""
|
|
161
|
-
async def get_prefix(self, key:
|
|
161
|
+
async def get_prefix(self, key: bytes) -> list[tuple[list[int], list[int]]]:
|
|
162
162
|
"""
|
|
163
163
|
Gets the key from the key-value store.
|
|
164
164
|
"""
|
|
165
|
-
async def put(self, key:
|
|
166
|
-
"""
|
|
167
|
-
Put the given key into the key-value store.
|
|
168
|
-
A put request increments the revision of the key-value store
|
|
169
|
-
and generates one event in the event history.
|
|
170
|
-
"""
|
|
171
|
-
async def put_prefix(self, key: str, value: dict[str, Any]) -> None:
|
|
165
|
+
async def put(self, key: bytes, value: bytes) -> None:
|
|
172
166
|
"""
|
|
173
167
|
Put the given key into the key-value store.
|
|
174
168
|
A put request increments the revision of the key-value store
|
|
@@ -181,19 +175,17 @@ class Communicator:
|
|
|
181
175
|
and generates events with the same revision for every completed operation.
|
|
182
176
|
It is not allowed to modify the same key several times within one txn.
|
|
183
177
|
"""
|
|
184
|
-
async def delete(self, key:
|
|
178
|
+
async def delete(self, key: bytes) -> None:
|
|
185
179
|
"""
|
|
186
180
|
Deletes the given key from the key-value store.
|
|
187
181
|
"""
|
|
188
|
-
async def delete_prefix(self, key:
|
|
182
|
+
async def delete_prefix(self, key: bytes) -> None:
|
|
189
183
|
"""
|
|
190
184
|
Deletes the given key from the key-value store.
|
|
191
185
|
"""
|
|
192
|
-
async def keys_prefix(self, key:
|
|
193
|
-
""" """
|
|
194
|
-
async def replace(self, key: str, initial_value: str, new_value: str) -> bool:
|
|
186
|
+
async def keys_prefix(self, key: bytes) -> list[list[int]]:
|
|
195
187
|
""" """
|
|
196
|
-
async def lock(self, name:
|
|
188
|
+
async def lock(self, name: bytes) -> None:
|
|
197
189
|
"""
|
|
198
190
|
Lock acquires a distributed shared lock on a given named lock.
|
|
199
191
|
On success, it will return a unique key that exists so long as the
|
|
@@ -202,7 +194,7 @@ class Communicator:
|
|
|
202
194
|
lock ownership. The lock is held until Unlock is called on the key or the
|
|
203
195
|
lease associate with the owner expires.
|
|
204
196
|
"""
|
|
205
|
-
async def unlock(self,
|
|
197
|
+
async def unlock(self, name: bytes) -> None:
|
|
206
198
|
"""
|
|
207
199
|
Unlock takes a key returned by Lock and releases the hold on lock. The
|
|
208
200
|
next Lock caller waiting for the lock will then be woken up and given
|
|
@@ -225,7 +217,7 @@ class Communicator:
|
|
|
225
217
|
"""
|
|
226
218
|
def watch(
|
|
227
219
|
self,
|
|
228
|
-
key:
|
|
220
|
+
key: bytes,
|
|
229
221
|
*,
|
|
230
222
|
once: Optional[bool] = False,
|
|
231
223
|
ready_event: Optional["CondVar"] = None,
|
|
@@ -238,7 +230,7 @@ class Communicator:
|
|
|
238
230
|
"""
|
|
239
231
|
def watch_prefix(
|
|
240
232
|
self,
|
|
241
|
-
key:
|
|
233
|
+
key: bytes,
|
|
242
234
|
*,
|
|
243
235
|
once: Optional[bool] = False,
|
|
244
236
|
ready_event: Optional["CondVar"] = None,
|
|
@@ -261,16 +253,16 @@ class Watch:
|
|
|
261
253
|
class WatchEvent:
|
|
262
254
|
""" """
|
|
263
255
|
|
|
264
|
-
key:
|
|
265
|
-
value:
|
|
256
|
+
key: bytes
|
|
257
|
+
value: bytes
|
|
266
258
|
event: "WatchEventType"
|
|
267
|
-
prev_value: Optional[
|
|
259
|
+
prev_value: Optional[bytes]
|
|
268
260
|
|
|
269
261
|
def __init__(
|
|
270
|
-
key:
|
|
271
|
-
value:
|
|
262
|
+
key: bytes,
|
|
263
|
+
value: bytes,
|
|
272
264
|
event: "WatchEventType",
|
|
273
|
-
prev_value: Optional[
|
|
265
|
+
prev_value: Optional[bytes] = None,
|
|
274
266
|
) -> None: ...
|
|
275
267
|
|
|
276
268
|
class WatchEventType:
|
|
@@ -296,7 +288,7 @@ class CondVar:
|
|
|
296
288
|
class ClientError(Exception):
|
|
297
289
|
""" """
|
|
298
290
|
|
|
299
|
-
class
|
|
291
|
+
class GRPCStatusError(ClientError):
|
|
300
292
|
""" """
|
|
301
293
|
|
|
302
294
|
class InvalidArgsError(ClientError):
|
|
@@ -332,7 +324,7 @@ class EndpointError(ClientError):
|
|
|
332
324
|
class LockError(ClientError):
|
|
333
325
|
""" """
|
|
334
326
|
|
|
335
|
-
class
|
|
327
|
+
class GRPCStatusCode(Enum):
|
|
336
328
|
Ok = 0
|
|
337
329
|
"""The operation completed successfully."""
|
|
338
330
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
use etcd_client::
|
|
1
|
+
use etcd_client::Client as EtcdClient;
|
|
2
2
|
use etcd_client::{DeleteOptions, GetOptions, WatchOptions};
|
|
3
|
-
use pyo3::exceptions::PyException;
|
|
4
3
|
use pyo3::prelude::*;
|
|
4
|
+
use pyo3::types::PyBytes;
|
|
5
5
|
use pyo3_asyncio::tokio::future_into_py;
|
|
6
|
-
use std::collections::HashMap;
|
|
7
6
|
use std::sync::Arc;
|
|
8
7
|
use tokio::sync::Mutex;
|
|
9
8
|
|
|
@@ -19,8 +18,9 @@ pub struct PyCommunicator(pub Arc<Mutex<EtcdClient>>);
|
|
|
19
18
|
#[pymethods]
|
|
20
19
|
impl PyCommunicator {
|
|
21
20
|
// TODO: Implement and use the CRUD response types
|
|
22
|
-
fn get<'a>(&'a self, py: Python<'a>, key:
|
|
21
|
+
fn get<'a>(&'a self, py: Python<'a>, key: &PyBytes) -> PyResult<&'a PyAny> {
|
|
23
22
|
let client = self.0.clone();
|
|
23
|
+
let key = key.as_bytes().to_vec();
|
|
24
24
|
future_into_py(py, async move {
|
|
25
25
|
let mut client = client.lock().await;
|
|
26
26
|
let result = client.get(key, None).await;
|
|
@@ -28,7 +28,7 @@ impl PyCommunicator {
|
|
|
28
28
|
.map(|response| {
|
|
29
29
|
let kvs = response.kvs();
|
|
30
30
|
if !kvs.is_empty() {
|
|
31
|
-
Some(
|
|
31
|
+
Some(kvs[0].value().to_owned())
|
|
32
32
|
} else {
|
|
33
33
|
None
|
|
34
34
|
}
|
|
@@ -37,29 +37,32 @@ impl PyCommunicator {
|
|
|
37
37
|
})
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
fn get_prefix<'a>(&'a self, py: Python<'a>, prefix:
|
|
40
|
+
fn get_prefix<'a>(&'a self, py: Python<'a>, prefix: &PyBytes) -> PyResult<&'a PyAny> {
|
|
41
41
|
let client = self.0.clone();
|
|
42
|
+
let prefix = prefix.as_bytes().to_vec();
|
|
43
|
+
|
|
42
44
|
future_into_py(py, async move {
|
|
43
45
|
let mut client = client.lock().await;
|
|
44
46
|
let options = GetOptions::new().with_prefix();
|
|
45
47
|
let result = client.get(prefix, Some(options)).await;
|
|
46
48
|
result
|
|
47
49
|
.map(|response| {
|
|
48
|
-
let mut
|
|
50
|
+
let mut list = vec![];
|
|
49
51
|
let kvs = response.kvs();
|
|
50
52
|
for kv in kvs {
|
|
51
|
-
|
|
52
|
-
let value = String::from_utf8(kv.value().to_owned()).unwrap();
|
|
53
|
-
result.insert(key, value);
|
|
53
|
+
list.push((kv.key().to_owned(), kv.value().to_owned()));
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
list
|
|
56
56
|
})
|
|
57
57
|
.map_err(|e| PyClientError(e).into())
|
|
58
58
|
})
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
fn put<'a>(&'a self, py: Python<'a>, key:
|
|
61
|
+
fn put<'a>(&'a self, py: Python<'a>, key: &PyBytes, value: &PyBytes) -> PyResult<&'a PyAny> {
|
|
62
62
|
let client = self.0.clone();
|
|
63
|
+
let key = key.as_bytes().to_vec();
|
|
64
|
+
let value = value.as_bytes().to_vec();
|
|
65
|
+
|
|
63
66
|
future_into_py(py, async move {
|
|
64
67
|
let mut client = client.lock().await;
|
|
65
68
|
let result = client.put(key, value, None).await;
|
|
@@ -67,23 +70,10 @@ impl PyCommunicator {
|
|
|
67
70
|
})
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
fn
|
|
71
|
-
&'a self,
|
|
72
|
-
py: Python<'a>,
|
|
73
|
-
prefix: String,
|
|
74
|
-
value: String,
|
|
75
|
-
) -> PyResult<&'a PyAny> {
|
|
73
|
+
fn delete<'a>(&'a self, py: Python<'a>, key: &PyBytes) -> PyResult<&'a PyAny> {
|
|
76
74
|
let client = self.0.clone();
|
|
77
|
-
|
|
78
|
-
let mut client = client.lock().await;
|
|
79
|
-
let options = PutOptions::new().with_prev_key();
|
|
80
|
-
let result = client.put(prefix, value, Some(options)).await;
|
|
81
|
-
result.map(|_| ()).map_err(|e| PyClientError(e).into())
|
|
82
|
-
})
|
|
83
|
-
}
|
|
75
|
+
let key = key.as_bytes().to_vec();
|
|
84
76
|
|
|
85
|
-
fn delete<'a>(&'a self, py: Python<'a>, key: String) -> PyResult<&'a PyAny> {
|
|
86
|
-
let client = self.0.clone();
|
|
87
77
|
future_into_py(py, async move {
|
|
88
78
|
let mut client = client.lock().await;
|
|
89
79
|
|
|
@@ -92,8 +82,10 @@ impl PyCommunicator {
|
|
|
92
82
|
})
|
|
93
83
|
}
|
|
94
84
|
|
|
95
|
-
fn delete_prefix<'a>(&'a self, py: Python<'a>, key:
|
|
85
|
+
fn delete_prefix<'a>(&'a self, py: Python<'a>, key: &PyBytes) -> PyResult<&'a PyAny> {
|
|
96
86
|
let client = self.0.clone();
|
|
87
|
+
let key = key.as_bytes().to_vec();
|
|
88
|
+
|
|
97
89
|
future_into_py(py, async move {
|
|
98
90
|
let mut client = client.lock().await;
|
|
99
91
|
let options = DeleteOptions::new().with_prefix();
|
|
@@ -114,39 +106,10 @@ impl PyCommunicator {
|
|
|
114
106
|
})
|
|
115
107
|
}
|
|
116
108
|
|
|
117
|
-
fn
|
|
118
|
-
&'a self,
|
|
119
|
-
py: Python<'a>,
|
|
120
|
-
key: String,
|
|
121
|
-
initial_val: String,
|
|
122
|
-
new_val: String,
|
|
123
|
-
) -> PyResult<&'a PyAny> {
|
|
109
|
+
fn keys_prefix<'a>(&'a self, py: Python<'a>, key: &PyBytes) -> PyResult<&'a PyAny> {
|
|
124
110
|
let client = self.0.clone();
|
|
125
|
-
|
|
126
|
-
let mut client = client.lock().await;
|
|
127
|
-
match client.get(key.clone(), None).await {
|
|
128
|
-
Ok(response) => {
|
|
129
|
-
if let Some(key_value) = response.kvs().get(0) {
|
|
130
|
-
if *key_value.value_str().unwrap() == initial_val {
|
|
131
|
-
match client.put(key, new_val, None).await {
|
|
132
|
-
Ok(_) => Ok(true), // replace successful
|
|
133
|
-
Err(e) => Err(PyClientError(e)),
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
Ok(false) // initial_val not equal to current value
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
Ok(false) // Key does not exist
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
Err(e) => Err(PyClientError(e)),
|
|
143
|
-
}
|
|
144
|
-
.map_err(|e| PyErr::new::<PyException, _>(format!("{}", e.0)))
|
|
145
|
-
})
|
|
146
|
-
}
|
|
111
|
+
let key = key.as_bytes().to_vec();
|
|
147
112
|
|
|
148
|
-
fn keys_prefix<'a>(&'a self, py: Python<'a>, key: String) -> PyResult<&'a PyAny> {
|
|
149
|
-
let client = self.0.clone();
|
|
150
113
|
future_into_py(py, async move {
|
|
151
114
|
let mut client = client.lock().await;
|
|
152
115
|
let options = GetOptions::new().with_prefix();
|
|
@@ -156,8 +119,7 @@ impl PyCommunicator {
|
|
|
156
119
|
let mut result = Vec::new();
|
|
157
120
|
let kvs = response.kvs();
|
|
158
121
|
for kv in kvs {
|
|
159
|
-
|
|
160
|
-
result.push(key);
|
|
122
|
+
result.push(kv.key().to_owned());
|
|
161
123
|
}
|
|
162
124
|
result
|
|
163
125
|
})
|
|
@@ -165,8 +127,10 @@ impl PyCommunicator {
|
|
|
165
127
|
})
|
|
166
128
|
}
|
|
167
129
|
|
|
168
|
-
fn lock<'a>(&'a self, py: Python<'a>, name:
|
|
130
|
+
fn lock<'a>(&'a self, py: Python<'a>, name: &PyBytes) -> PyResult<&'a PyAny> {
|
|
169
131
|
let client = self.0.clone();
|
|
132
|
+
let name = name.as_bytes().to_vec();
|
|
133
|
+
|
|
170
134
|
future_into_py(py, async move {
|
|
171
135
|
let mut client = client.lock().await;
|
|
172
136
|
let result = client.lock(name, None).await;
|
|
@@ -174,11 +138,13 @@ impl PyCommunicator {
|
|
|
174
138
|
})
|
|
175
139
|
}
|
|
176
140
|
|
|
177
|
-
fn unlock<'a>(&'a self, py: Python<'a>,
|
|
141
|
+
fn unlock<'a>(&'a self, py: Python<'a>, name: &PyBytes) -> PyResult<&'a PyAny> {
|
|
178
142
|
let client = self.0.clone();
|
|
143
|
+
let name = name.as_bytes().to_vec();
|
|
144
|
+
|
|
179
145
|
future_into_py(py, async move {
|
|
180
146
|
let mut client = client.lock().await;
|
|
181
|
-
let result = client.unlock(
|
|
147
|
+
let result = client.unlock(name).await;
|
|
182
148
|
result.map(|_| ()).map_err(|e| PyClientError(e).into())
|
|
183
149
|
})
|
|
184
150
|
}
|
|
@@ -222,24 +188,26 @@ impl PyCommunicator {
|
|
|
222
188
|
|
|
223
189
|
fn watch(
|
|
224
190
|
&self,
|
|
225
|
-
key:
|
|
191
|
+
key: &PyBytes,
|
|
226
192
|
once: Option<bool>,
|
|
227
193
|
ready_event: Option<PyCondVar>,
|
|
228
194
|
cleanup_event: Option<PyCondVar>,
|
|
229
195
|
) -> PyWatch {
|
|
230
196
|
let client = self.0.clone();
|
|
197
|
+
let key = key.as_bytes().to_vec();
|
|
231
198
|
let once = once.unwrap_or(false);
|
|
232
199
|
PyWatch::new(client, key, once, None, ready_event, cleanup_event)
|
|
233
200
|
}
|
|
234
201
|
|
|
235
202
|
fn watch_prefix(
|
|
236
203
|
&self,
|
|
237
|
-
key:
|
|
204
|
+
key: &PyBytes,
|
|
238
205
|
once: Option<bool>,
|
|
239
206
|
ready_event: Option<PyCondVar>,
|
|
240
207
|
cleanup_event: Option<PyCondVar>,
|
|
241
208
|
) -> PyWatch {
|
|
242
209
|
let client = self.0.clone();
|
|
210
|
+
let key = key.as_bytes().to_vec();
|
|
243
211
|
let once = once.unwrap_or(false);
|
|
244
212
|
let options = WatchOptions::new().with_prefix();
|
|
245
213
|
PyWatch::new(client, key, once, Some(options), ready_event, cleanup_event)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
use etcd_client::{Compare, CompareOp};
|
|
2
2
|
use pyo3::prelude::*;
|
|
3
3
|
use pyo3::pyclass::CompareOp as PyO3CompareOp;
|
|
4
|
+
use pyo3::types::PyBytes;
|
|
4
5
|
|
|
5
6
|
#[derive(Clone)]
|
|
6
7
|
#[pyclass(name = "CompareOp")]
|
|
@@ -42,31 +43,38 @@ pub struct PyCompare(pub Compare);
|
|
|
42
43
|
#[pymethods]
|
|
43
44
|
impl PyCompare {
|
|
44
45
|
#[staticmethod]
|
|
45
|
-
fn version(key:
|
|
46
|
+
fn version(key: &PyBytes, cmp: PyCompareOp, version: i64) -> PyResult<Self> {
|
|
47
|
+
let key = key.as_bytes().to_vec();
|
|
46
48
|
Ok(PyCompare(Compare::version(key, cmp.0, version)))
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
#[staticmethod]
|
|
50
|
-
fn create_revision(key:
|
|
52
|
+
fn create_revision(key: &PyBytes, cmp: PyCompareOp, revision: i64) -> PyResult<Self> {
|
|
53
|
+
let key = key.as_bytes().to_vec();
|
|
51
54
|
Ok(PyCompare(Compare::create_revision(key, cmp.0, revision)))
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
#[staticmethod]
|
|
55
|
-
fn mod_revision(key:
|
|
58
|
+
fn mod_revision(key: &PyBytes, cmp: PyCompareOp, revision: i64) -> PyResult<Self> {
|
|
59
|
+
let key = key.as_bytes().to_vec();
|
|
56
60
|
Ok(PyCompare(Compare::mod_revision(key, cmp.0, revision)))
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
#[staticmethod]
|
|
60
|
-
fn value(key:
|
|
64
|
+
fn value(key: &PyBytes, cmp: PyCompareOp, value: &PyBytes) -> PyResult<Self> {
|
|
65
|
+
let key = key.as_bytes().to_vec();
|
|
66
|
+
let value = value.as_bytes().to_vec();
|
|
61
67
|
Ok(PyCompare(Compare::value(key, cmp.0, value)))
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
#[staticmethod]
|
|
65
|
-
fn lease(key:
|
|
71
|
+
fn lease(key: &PyBytes, cmp: PyCompareOp, lease: i64) -> PyResult<Self> {
|
|
72
|
+
let key = key.as_bytes().to_vec();
|
|
66
73
|
Ok(PyCompare(Compare::lease(key, cmp.0, lease)))
|
|
67
74
|
}
|
|
68
75
|
|
|
69
|
-
fn with_range(&self, end:
|
|
76
|
+
fn with_range(&self, end: &PyBytes) -> PyResult<Self> {
|
|
77
|
+
let end = end.as_bytes().to_vec();
|
|
70
78
|
Ok(PyCompare(self.0.clone().with_range(end)))
|
|
71
79
|
}
|
|
72
80
|
|
|
@@ -2,7 +2,7 @@ use pyo3::{create_exception, exceptions::PyException, types::PyDict, PyErr, Pyth
|
|
|
2
2
|
use std::fmt::Debug;
|
|
3
3
|
|
|
4
4
|
create_exception!(etcd_client, ClientError, PyException);
|
|
5
|
-
create_exception!(etcd_client,
|
|
5
|
+
create_exception!(etcd_client, GRPCStatusError, ClientError);
|
|
6
6
|
create_exception!(etcd_client, InvalidArgsError, ClientError);
|
|
7
7
|
create_exception!(etcd_client, InvalidUriError, ClientError);
|
|
8
8
|
create_exception!(etcd_client, IoError, ClientError);
|
|
@@ -15,8 +15,8 @@ create_exception!(etcd_client, InvalidHeaderValueError, ClientError);
|
|
|
15
15
|
create_exception!(etcd_client, EndpointError, ClientError);
|
|
16
16
|
create_exception!(etcd_client, LockError, ClientError);
|
|
17
17
|
|
|
18
|
-
#[pyclass(name = "
|
|
19
|
-
pub enum
|
|
18
|
+
#[pyclass(name = "GRPCStatusCode")]
|
|
19
|
+
pub enum PyGRPCStatusCode {
|
|
20
20
|
Ok = 0,
|
|
21
21
|
Cancelled = 1,
|
|
22
22
|
Unknown = 2,
|
|
@@ -54,7 +54,7 @@ impl From<PyClientError> for PyErr {
|
|
|
54
54
|
.unwrap();
|
|
55
55
|
|
|
56
56
|
let kv_args: PyObject = error_details.into_py(py);
|
|
57
|
-
|
|
57
|
+
GRPCStatusError::new_err(kv_args)
|
|
58
58
|
}),
|
|
59
59
|
etcd_client::Error::InvalidArgs(e) => {
|
|
60
60
|
InvalidArgsError::new_err(format!("InvalidArgsError(err={})", e))
|
|
@@ -15,8 +15,8 @@ use communicator::PyCommunicator;
|
|
|
15
15
|
use compare::{PyCompare, PyCompareOp};
|
|
16
16
|
use condvar::PyCondVar;
|
|
17
17
|
use error::{
|
|
18
|
-
ClientError, ElectError, EndpointError,
|
|
19
|
-
InvalidHeaderValueError, InvalidUriError, IoError, LeaseKeepAliveError,
|
|
18
|
+
ClientError, ElectError, EndpointError, GRPCStatusError, InvalidArgsError,
|
|
19
|
+
InvalidHeaderValueError, InvalidUriError, IoError, LeaseKeepAliveError, PyGRPCStatusCode,
|
|
20
20
|
TransportError, Utf8Error, WatchError,
|
|
21
21
|
};
|
|
22
22
|
use lock_manager::PyEtcdLockOption;
|
|
@@ -45,10 +45,10 @@ fn etcd_client(py: Python, module: &PyModule) -> PyResult<()> {
|
|
|
45
45
|
module.add_class::<PyTxnResponse>()?;
|
|
46
46
|
module.add_class::<PyEtcdLockOption>()?;
|
|
47
47
|
|
|
48
|
-
module.add_class::<
|
|
48
|
+
module.add_class::<PyGRPCStatusCode>()?;
|
|
49
49
|
|
|
50
50
|
module.add("ClientError", py.get_type::<ClientError>())?;
|
|
51
|
-
module.add("
|
|
51
|
+
module.add("GRPCStatusError", py.get_type::<GRPCStatusError>())?;
|
|
52
52
|
module.add("InvalidArgsError", py.get_type::<InvalidArgsError>())?;
|
|
53
53
|
module.add("IoError", py.get_type::<IoError>())?;
|
|
54
54
|
module.add("InvalidUriError", py.get_type::<InvalidUriError>())?;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
client::PyClient,
|
|
3
3
|
communicator::PyCommunicator,
|
|
4
|
-
error::{
|
|
4
|
+
error::{GRPCStatusError, LockError, PyClientError},
|
|
5
5
|
};
|
|
6
6
|
use etcd_client::{Client as EtcdClient, LockOptions};
|
|
7
7
|
|
|
8
|
-
use pyo3::prelude
|
|
8
|
+
use pyo3::{prelude::*, types::PyBytes};
|
|
9
9
|
use std::time::Duration;
|
|
10
10
|
use tokio::time::{sleep, timeout};
|
|
11
11
|
|
|
12
12
|
#[derive(Debug, Clone)]
|
|
13
13
|
#[pyclass(get_all, set_all, name = "EtcdLockOption")]
|
|
14
14
|
pub struct PyEtcdLockOption {
|
|
15
|
-
pub lock_name:
|
|
15
|
+
pub lock_name: Vec<u8>,
|
|
16
16
|
pub timeout: Option<f64>,
|
|
17
17
|
pub ttl: Option<i64>,
|
|
18
18
|
}
|
|
@@ -20,7 +20,8 @@ pub struct PyEtcdLockOption {
|
|
|
20
20
|
#[pymethods]
|
|
21
21
|
impl PyEtcdLockOption {
|
|
22
22
|
#[new]
|
|
23
|
-
fn new(lock_name:
|
|
23
|
+
fn new(lock_name: &PyBytes, timeout: Option<f64>, ttl: Option<i64>) -> Self {
|
|
24
|
+
let lock_name = lock_name.as_bytes().to_vec();
|
|
24
25
|
Self {
|
|
25
26
|
lock_name,
|
|
26
27
|
timeout,
|
|
@@ -30,7 +31,7 @@ impl PyEtcdLockOption {
|
|
|
30
31
|
|
|
31
32
|
fn __repr__(&self) -> PyResult<String> {
|
|
32
33
|
Ok(format!(
|
|
33
|
-
"EtcdLockOption(lock_name={}, timeout={:?}, ttl={:?})",
|
|
34
|
+
"EtcdLockOption(lock_name={:?}, timeout={:?}, ttl={:?})",
|
|
34
35
|
self.lock_name, self.timeout, self.ttl
|
|
35
36
|
))
|
|
36
37
|
}
|
|
@@ -38,10 +39,10 @@ impl PyEtcdLockOption {
|
|
|
38
39
|
|
|
39
40
|
pub struct EtcdLockManager {
|
|
40
41
|
pub client: PyClient,
|
|
41
|
-
pub lock_name:
|
|
42
|
+
pub lock_name: Vec<u8>,
|
|
42
43
|
pub ttl: Option<i64>,
|
|
43
44
|
pub timeout_seconds: Option<f64>,
|
|
44
|
-
pub lock_id: Option<
|
|
45
|
+
pub lock_id: Option<Vec<u8>>,
|
|
45
46
|
pub lease_id: Option<i64>,
|
|
46
47
|
pub lease_keepalive_task: Option<tokio::task::JoinHandle<Result<(), PyClientError>>>,
|
|
47
48
|
}
|
|
@@ -98,15 +99,11 @@ impl EtcdLockManager {
|
|
|
98
99
|
.map(|lease_id| LockOptions::new().with_lease(lease_id));
|
|
99
100
|
|
|
100
101
|
let lock_res = client
|
|
101
|
-
.lock(self.lock_name.clone()
|
|
102
|
+
.lock(self.lock_name.clone(), lock_req_options)
|
|
102
103
|
.await
|
|
103
104
|
.map_err(PyClientError)?;
|
|
104
105
|
|
|
105
|
-
self.lock_id = Some(
|
|
106
|
-
std::str::from_utf8(lock_res.key())
|
|
107
|
-
.expect("Invalid utf8 chars includeded in lock request!")
|
|
108
|
-
.to_owned(),
|
|
109
|
-
);
|
|
106
|
+
self.lock_id = Some(lock_res.key().to_vec());
|
|
110
107
|
Ok(())
|
|
111
108
|
},
|
|
112
109
|
)
|
|
@@ -123,7 +120,7 @@ impl EtcdLockManager {
|
|
|
123
120
|
etcd_client::Error::GRpcStatus(status)
|
|
124
121
|
if status.code() != tonic::Code::NotFound =>
|
|
125
122
|
{
|
|
126
|
-
return Err(
|
|
123
|
+
return Err(GRPCStatusError::new_err(status.to_string()))
|
|
127
124
|
}
|
|
128
125
|
_ => return Err(PyClientError(e).into()),
|
|
129
126
|
},
|
|
@@ -160,7 +157,11 @@ impl EtcdLockManager {
|
|
|
160
157
|
client.lease_revoke(lease_id).await.map_err(PyClientError)?;
|
|
161
158
|
} else {
|
|
162
159
|
client
|
|
163
|
-
.unlock(
|
|
160
|
+
.unlock(
|
|
161
|
+
self.lock_id
|
|
162
|
+
.to_owned()
|
|
163
|
+
.expect("Failed to unlock due to lock_id is None"),
|
|
164
|
+
)
|
|
164
165
|
.await
|
|
165
166
|
.map_err(PyClientError)?;
|
|
166
167
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use etcd_client::{DeleteOptions, GetOptions, PutOptions, Txn, TxnOp};
|
|
2
|
-
use pyo3::prelude
|
|
2
|
+
use pyo3::{prelude::*, types::PyBytes};
|
|
3
3
|
|
|
4
4
|
use crate::compare::PyCompare;
|
|
5
5
|
|
|
@@ -10,18 +10,23 @@ pub struct PyTxnOp(pub TxnOp);
|
|
|
10
10
|
#[pymethods]
|
|
11
11
|
impl PyTxnOp {
|
|
12
12
|
#[staticmethod]
|
|
13
|
-
fn get(key:
|
|
13
|
+
fn get(key: &PyBytes) -> PyResult<Self> {
|
|
14
|
+
let key = key.as_bytes().to_vec();
|
|
14
15
|
let options = GetOptions::new();
|
|
15
16
|
Ok(PyTxnOp(TxnOp::get(key, Some(options))))
|
|
16
17
|
}
|
|
18
|
+
|
|
17
19
|
#[staticmethod]
|
|
18
|
-
fn put(key:
|
|
20
|
+
fn put(key: &PyBytes, value: &PyBytes) -> PyResult<Self> {
|
|
21
|
+
let key = key.as_bytes().to_vec();
|
|
22
|
+
let value = value.as_bytes().to_vec();
|
|
19
23
|
let options = PutOptions::new();
|
|
20
24
|
Ok(PyTxnOp(TxnOp::put(key, value, Some(options))))
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
#[staticmethod]
|
|
24
|
-
fn delete(key:
|
|
28
|
+
fn delete(key: &PyBytes) -> PyResult<Self> {
|
|
29
|
+
let key = key.as_bytes().to_vec();
|
|
25
30
|
let options = DeleteOptions::new();
|
|
26
31
|
Ok(PyTxnOp(TxnOp::delete(key, Some(options))))
|
|
27
32
|
}
|
|
@@ -16,7 +16,7 @@ use crate::watch_event_stream::PyWatchEventStream;
|
|
|
16
16
|
#[derive(Clone)]
|
|
17
17
|
pub struct PyWatch {
|
|
18
18
|
client: Arc<Mutex<EtcdClient>>,
|
|
19
|
-
key:
|
|
19
|
+
key: Vec<u8>,
|
|
20
20
|
once: bool,
|
|
21
21
|
options: Option<WatchOptions>,
|
|
22
22
|
watcher: Arc<Mutex<Option<Watcher>>>,
|
|
@@ -30,7 +30,7 @@ pub struct PyWatch {
|
|
|
30
30
|
impl PyWatch {
|
|
31
31
|
pub fn new(
|
|
32
32
|
client: Arc<Mutex<EtcdClient>>,
|
|
33
|
-
key:
|
|
33
|
+
key: Vec<u8>,
|
|
34
34
|
once: bool,
|
|
35
35
|
options: Option<WatchOptions>,
|
|
36
36
|
ready_event: Option<PyCondVar>,
|
|
@@ -7,10 +7,10 @@ use pyo3::pyclass::CompareOp;
|
|
|
7
7
|
#[pyclass(get_all, name = "WatchEvent")]
|
|
8
8
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
9
9
|
pub struct PyWatchEvent {
|
|
10
|
-
key:
|
|
11
|
-
value:
|
|
10
|
+
key: Vec<u8>,
|
|
11
|
+
value: Vec<u8>,
|
|
12
12
|
event: PyWatchEventType,
|
|
13
|
-
prev_value: Option<
|
|
13
|
+
prev_value: Option<Vec<u8>>,
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
#[pymethods]
|
|
@@ -18,10 +18,10 @@ impl PyWatchEvent {
|
|
|
18
18
|
#[new]
|
|
19
19
|
#[pyo3(signature = (key, value, event, prev_value))]
|
|
20
20
|
fn new(
|
|
21
|
-
key:
|
|
22
|
-
value:
|
|
21
|
+
key: Vec<u8>,
|
|
22
|
+
value: Vec<u8>,
|
|
23
23
|
event: PyWatchEventType,
|
|
24
|
-
prev_value: Option<
|
|
24
|
+
prev_value: Option<Vec<u8>>,
|
|
25
25
|
) -> Self {
|
|
26
26
|
Self {
|
|
27
27
|
key,
|
|
@@ -33,7 +33,7 @@ impl PyWatchEvent {
|
|
|
33
33
|
|
|
34
34
|
pub fn __repr__(&self) -> String {
|
|
35
35
|
format!(
|
|
36
|
-
"Event(event={:?}, key={}, value={}, prev_value={:?})",
|
|
36
|
+
"Event(event={:?}, key={:?}, value={:?}, prev_value={:?})",
|
|
37
37
|
self.event, self.key, self.value, self.prev_value
|
|
38
38
|
)
|
|
39
39
|
}
|
|
@@ -50,8 +50,8 @@ impl PyWatchEvent {
|
|
|
50
50
|
impl From<EtcdClientEvent> for PyWatchEvent {
|
|
51
51
|
fn from(event: EtcdClientEvent) -> Self {
|
|
52
52
|
let kv = event.kv().unwrap();
|
|
53
|
-
let key =
|
|
54
|
-
let value =
|
|
53
|
+
let key = kv.key().to_owned();
|
|
54
|
+
let value = kv.value().to_owned();
|
|
55
55
|
let prev_value = None;
|
|
56
56
|
let event = PyWatchEventType(event.event_type());
|
|
57
57
|
Self {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backend.AI AsyncEtcd Client Copy
|
|
3
|
+
Ref: https://github.com/lablup/backend.ai/blob/main/src/ai/backend/common/etcd.py
|
|
4
|
+
=================================================================================
|
|
5
|
+
"""
|
|
6
|
+
|
|
1
7
|
"""
|
|
2
8
|
An asynchronous client wrapper for etcd v3 API.
|
|
3
9
|
|
|
@@ -7,8 +13,6 @@ Fortunately, etcd3's watchers are not blocking because they are implemented
|
|
|
7
13
|
using callbacks in separate threads.
|
|
8
14
|
"""
|
|
9
15
|
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
16
|
import asyncio
|
|
13
17
|
from dataclasses import dataclass
|
|
14
18
|
import enum
|
|
@@ -44,8 +48,8 @@ from etcd_client import (
|
|
|
44
48
|
CompareOp,
|
|
45
49
|
CondVar,
|
|
46
50
|
ConnectOptions,
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
GRPCStatusCode,
|
|
52
|
+
GRPCStatusError,
|
|
49
53
|
WatchEvent,
|
|
50
54
|
)
|
|
51
55
|
|
|
@@ -64,7 +68,7 @@ class HostPortPair:
|
|
|
64
68
|
return f"{self.host}:{self.port}"
|
|
65
69
|
|
|
66
70
|
@classmethod
|
|
67
|
-
def parse(cls, s: str) -> HostPortPair:
|
|
71
|
+
def parse(cls, s: str) -> "HostPortPair":
|
|
68
72
|
if ":" in s:
|
|
69
73
|
host, port_str = s.rsplit(":")
|
|
70
74
|
port = int(port_str)
|
|
@@ -220,7 +224,7 @@ class AsyncEtcd:
|
|
|
220
224
|
scope_prefix = self._merge_scope_prefix_map(scope_prefix_map)[scope]
|
|
221
225
|
mangled_key = self._mangle_key(f"{_slash(scope_prefix)}{key}")
|
|
222
226
|
async with self.etcd.connect() as communicator:
|
|
223
|
-
await communicator.put(mangled_key, str(val))
|
|
227
|
+
await communicator.put(mangled_key.encode(self.encoding), str(val).encode(self.encoding))
|
|
224
228
|
|
|
225
229
|
async def put_prefix(
|
|
226
230
|
self,
|
|
@@ -260,7 +264,7 @@ class AsyncEtcd:
|
|
|
260
264
|
actions = []
|
|
261
265
|
for k, v in flattened_dict.items():
|
|
262
266
|
actions.append(
|
|
263
|
-
TxnOp.put(self._mangle_key(f"{_slash(scope_prefix)}{k}"), str(v))
|
|
267
|
+
TxnOp.put(self._mangle_key(f"{_slash(scope_prefix)}{k}").encode(self.encoding), str(v).encode(self.encoding))
|
|
264
268
|
)
|
|
265
269
|
|
|
266
270
|
await communicator.txn(
|
|
@@ -289,7 +293,7 @@ class AsyncEtcd:
|
|
|
289
293
|
actions = []
|
|
290
294
|
for k, v in flattened_dict_obj.items():
|
|
291
295
|
actions.append(
|
|
292
|
-
TxnOp.put(self._mangle_key(f"{_slash(scope_prefix)}{k}"), str(v))
|
|
296
|
+
TxnOp.put(self._mangle_key(f"{_slash(scope_prefix)}{k}").encode(self.encoding), str(v).encode(self.encoding))
|
|
293
297
|
)
|
|
294
298
|
|
|
295
299
|
async with self.etcd.connect() as communicator:
|
|
@@ -337,10 +341,10 @@ class AsyncEtcd:
|
|
|
337
341
|
async with self.etcd.connect() as communicator:
|
|
338
342
|
for scope_prefix in scope_prefixes:
|
|
339
343
|
value = await communicator.get(
|
|
340
|
-
self._mangle_key(f"{_slash(scope_prefix)}{key}")
|
|
344
|
+
self._mangle_key(f"{_slash(scope_prefix)}{key}").encode(self.encoding)
|
|
341
345
|
)
|
|
342
346
|
if value is not None:
|
|
343
|
-
return value
|
|
347
|
+
return bytes(value).decode(self.encoding)
|
|
344
348
|
return None
|
|
345
349
|
|
|
346
350
|
async def get_prefix(
|
|
@@ -409,9 +413,9 @@ class AsyncEtcd:
|
|
|
409
413
|
mangled_key_prefix = self._mangle_key(
|
|
410
414
|
f"{_slash(scope_prefix)}{key_prefix}"
|
|
411
415
|
)
|
|
412
|
-
values = await communicator.get_prefix(mangled_key_prefix)
|
|
416
|
+
values = await communicator.get_prefix(mangled_key_prefix.encode(self.encoding))
|
|
413
417
|
pair_sets.append(
|
|
414
|
-
[(self._demangle_key(k), v) for k, v in values
|
|
418
|
+
[(self._demangle_key(bytes(k).decode(self.encoding)), bytes(v).decode(self.encoding)) for k, v in values]
|
|
415
419
|
)
|
|
416
420
|
|
|
417
421
|
configs = [
|
|
@@ -440,10 +444,10 @@ class AsyncEtcd:
|
|
|
440
444
|
EtcdTransactionAction()
|
|
441
445
|
.when(
|
|
442
446
|
[
|
|
443
|
-
Compare.value(mangled_key, CompareOp.EQUAL, initial_val),
|
|
447
|
+
Compare.value(mangled_key.encode(self.encoding), CompareOp.EQUAL, initial_val.encode(self.encoding)),
|
|
444
448
|
]
|
|
445
449
|
)
|
|
446
|
-
.and_then([TxnOp.put(mangled_key, new_val)])
|
|
450
|
+
.and_then([TxnOp.put(mangled_key.encode(self.encoding), new_val.encode(self.encoding))])
|
|
447
451
|
.or_else([])
|
|
448
452
|
)
|
|
449
453
|
|
|
@@ -459,7 +463,7 @@ class AsyncEtcd:
|
|
|
459
463
|
scope_prefix = self._merge_scope_prefix_map(scope_prefix_map)[scope]
|
|
460
464
|
mangled_key = self._mangle_key(f"{_slash(scope_prefix)}{key}")
|
|
461
465
|
async with self.etcd.connect() as communicator:
|
|
462
|
-
await communicator.delete(mangled_key)
|
|
466
|
+
await communicator.delete(mangled_key.encode(self.encoding))
|
|
463
467
|
|
|
464
468
|
async def delete_multi(
|
|
465
469
|
self,
|
|
@@ -473,7 +477,7 @@ class AsyncEtcd:
|
|
|
473
477
|
actions = []
|
|
474
478
|
for k in keys:
|
|
475
479
|
actions.append(
|
|
476
|
-
TxnOp.delete(self._mangle_key(f"{_slash(scope_prefix)}{k}"))
|
|
480
|
+
TxnOp.delete(self._mangle_key(f"{_slash(scope_prefix)}{k}").encode(self.encoding))
|
|
477
481
|
)
|
|
478
482
|
communicator.txn(EtcdTransactionAction().and_then(actions).or_else([]))
|
|
479
483
|
|
|
@@ -487,7 +491,7 @@ class AsyncEtcd:
|
|
|
487
491
|
scope_prefix = self._merge_scope_prefix_map(scope_prefix_map)[scope]
|
|
488
492
|
mangled_key_prefix = self._mangle_key(f"{_slash(scope_prefix)}{key_prefix}")
|
|
489
493
|
async with self.etcd.connect() as communicator:
|
|
490
|
-
await communicator.delete_prefix(mangled_key_prefix)
|
|
494
|
+
await communicator.delete_prefix(mangled_key_prefix.encode(self.encoding))
|
|
491
495
|
|
|
492
496
|
async def _watch_impl(
|
|
493
497
|
self,
|
|
@@ -508,7 +512,7 @@ class AsyncEtcd:
|
|
|
508
512
|
)
|
|
509
513
|
except asyncio.TimeoutError:
|
|
510
514
|
pass
|
|
511
|
-
yield Event(ev.key[scope_prefix_len:], ev.event, ev.value)
|
|
515
|
+
yield Event(bytes(ev.key).decode(self.encoding)[scope_prefix_len:], ev.event, bytes(ev.value).decode(self.encoding))
|
|
512
516
|
if once:
|
|
513
517
|
return
|
|
514
518
|
finally:
|
|
@@ -535,7 +539,7 @@ class AsyncEtcd:
|
|
|
535
539
|
try:
|
|
536
540
|
async for ev in self._watch_impl(
|
|
537
541
|
lambda communicator: communicator.watch(
|
|
538
|
-
mangled_key,
|
|
542
|
+
mangled_key.encode(self.encoding),
|
|
539
543
|
ready_event=ready_event,
|
|
540
544
|
),
|
|
541
545
|
scope_prefix_len,
|
|
@@ -545,10 +549,10 @@ class AsyncEtcd:
|
|
|
545
549
|
):
|
|
546
550
|
yield ev
|
|
547
551
|
ended_without_error = True
|
|
548
|
-
except
|
|
552
|
+
except GRPCStatusError as e:
|
|
549
553
|
err_detail = e.args[0]
|
|
550
554
|
|
|
551
|
-
if err_detail["code"] ==
|
|
555
|
+
if err_detail["code"] == GRPCStatusCode.Unavailable:
|
|
552
556
|
log.warn(
|
|
553
557
|
"watch(): error while connecting to Etcd server, retrying..."
|
|
554
558
|
)
|
|
@@ -577,7 +581,7 @@ class AsyncEtcd:
|
|
|
577
581
|
try:
|
|
578
582
|
async for ev in self._watch_impl(
|
|
579
583
|
lambda communicator: communicator.watch_prefix(
|
|
580
|
-
mangled_key_prefix,
|
|
584
|
+
mangled_key_prefix.encode(self.encoding),
|
|
581
585
|
ready_event=ready_event,
|
|
582
586
|
),
|
|
583
587
|
scope_prefix_len,
|
|
@@ -587,10 +591,10 @@ class AsyncEtcd:
|
|
|
587
591
|
):
|
|
588
592
|
yield ev
|
|
589
593
|
ended_without_error = True
|
|
590
|
-
except
|
|
594
|
+
except GRPCStatusError as e:
|
|
591
595
|
err_detail = e.args[0]
|
|
592
596
|
|
|
593
|
-
if err_detail["code"] ==
|
|
597
|
+
if err_detail["code"] == GRPCStatusCode.Unavailable:
|
|
594
598
|
log.warn(
|
|
595
599
|
"watch_prefix(): error while connecting to Etcd server, retrying..."
|
|
596
600
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|