Skip to content

Commit 00b278e

Browse files
authored
Merge pull request #1125 from tmccombs/prefix-range-end
Support using prefixes with 0xff as the last byte.
2 parents 967cc5c + b432062 commit 00b278e

File tree

4 files changed

+21
-14
lines changed

4 files changed

+21
-14
lines changed

etcd3/client.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def get_prefix_response(self, key_prefix, **kwargs):
295295

296296
range_request = self._build_get_range_request(
297297
key=key_prefix,
298-
range_end=utils.increment_last_byte(utils.to_bytes(key_prefix)),
298+
range_end=utils.prefix_range_end(utils.to_bytes(key_prefix)),
299299
**kwargs
300300
)
301301

@@ -518,7 +518,7 @@ def delete_prefix(self, prefix):
518518
"""Delete a range of keys with a prefix in etcd."""
519519
delete_request = self._build_delete_request(
520520
prefix,
521-
range_end=utils.increment_last_byte(utils.to_bytes(prefix))
521+
range_end=utils.prefix_range_end(utils.to_bytes(prefix))
522522
)
523523
return self.kvstub.DeleteRange(
524524
delete_request,
@@ -586,7 +586,7 @@ def add_watch_prefix_callback(self, key_prefix, callback, **kwargs):
586586
:returns: watch_id. Later it could be used for cancelling watch.
587587
"""
588588
kwargs['range_end'] = \
589-
utils.increment_last_byte(utils.to_bytes(key_prefix))
589+
utils.prefix_range_end(utils.to_bytes(key_prefix))
590590

591591
return self.add_watch_callback(key_prefix, callback, **kwargs)
592592

@@ -665,7 +665,7 @@ def watch_prefix_response(self, key_prefix, **kwargs):
665665
:returns: tuple of ``responses_iterator`` and ``cancel``.
666666
"""
667667
kwargs['range_end'] = \
668-
utils.increment_last_byte(utils.to_bytes(key_prefix))
668+
utils.prefix_range_end(utils.to_bytes(key_prefix))
669669
return self.watch_response(key_prefix, **kwargs)
670670

671671
def watch_prefix(self, key_prefix, **kwargs):
@@ -677,7 +677,7 @@ def watch_prefix(self, key_prefix, **kwargs):
677677
:returns: tuple of ``events_iterator`` and ``cancel``.
678678
"""
679679
kwargs['range_end'] = \
680-
utils.increment_last_byte(utils.to_bytes(key_prefix))
680+
utils.prefix_range_end(utils.to_bytes(key_prefix))
681681
return self.watch(key_prefix, **kwargs)
682682

683683
@_handle_errors
@@ -730,7 +730,7 @@ def watch_prefix_once_response(self, key_prefix, timeout=None, **kwargs):
730730
will raise ``WatchTimedOut`` exception.
731731
"""
732732
kwargs['range_end'] = \
733-
utils.increment_last_byte(utils.to_bytes(key_prefix))
733+
utils.prefix_range_end(utils.to_bytes(key_prefix))
734734
return self.watch_once_response(key_prefix, timeout=timeout, **kwargs)
735735

736736
def watch_prefix_once(self, key_prefix, timeout=None, **kwargs):
@@ -741,7 +741,7 @@ def watch_prefix_once(self, key_prefix, timeout=None, **kwargs):
741741
will raise ``WatchTimedOut`` exception.
742742
"""
743743
kwargs['range_end'] = \
744-
utils.increment_last_byte(utils.to_bytes(key_prefix))
744+
utils.prefix_range_end(utils.to_bytes(key_prefix))
745745
return self.watch_once(key_prefix, timeout=timeout, **kwargs)
746746

747747
@_handle_errors

etcd3/utils.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
def increment_last_byte(byte_string):
2-
s = bytearray(byte_string)
3-
s[-1] = s[-1] + 1
1+
def prefix_range_end(prefix):
2+
"""Create a bytestring that can be used as a range_end for a prefix."""
3+
s = bytearray(prefix)
4+
for i in reversed(range(len(s))):
5+
if s[i] < 0xff:
6+
s[i] = s[i] + 1
7+
break
48
return bytes(s)
59

610

tests/test_etcd3.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ def test_nested_transactions(self, etcd):
555555
def test_transaction_range_conditions(self, etcd):
556556
etcdctl('put', '/doot/key1', 'dootdoot')
557557
etcdctl('put', '/doot/key2', 'notdootdoot')
558-
range_end = utils.increment_last_byte(utils.to_bytes('/doot/'))
558+
range_end = utils.prefix_range_end(utils.to_bytes('/doot/'))
559559
compare = [etcd.transactions.value('/doot/', range_end) == 'dootdoot']
560560
status, _ = etcd.transaction(compare=compare, success=[], failure=[])
561561
assert not status
@@ -980,8 +980,11 @@ def test_disarm_alarm(self, etcd):
980980

981981

982982
class TestUtils(object):
983-
def test_increment_last_byte(self):
984-
assert etcd3.utils.increment_last_byte(b'foo') == b'fop'
983+
def test_prefix_range_end(self):
984+
assert etcd3.utils.prefix_range_end(b'foo') == b'fop'
985+
assert etcd3.utils.prefix_range_end(b'ab\xff') == b'ac\xff'
986+
assert (etcd3.utils.prefix_range_end(b'a\xff\xff\xff\xff\xff')
987+
== b'b\xff\xff\xff\xff\xff')
985988

986989
def test_to_bytes(self):
987990
assert isinstance(etcd3.utils.to_bytes(b'doot'), bytes) is True

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ exclude = .venv,.git,.tox,dist,docs,*lib/python*,*egg,build,etcd3/etcdrpc/
4444
application-import-names = etcd3
4545
max-complexity = 10
4646
# TODO add docstrings for public methods, modules, etc
47-
ignore = D1
47+
ignore = D1, W503
4848

4949
[travis]
5050
python = 3.6: py36, flake8

0 commit comments

Comments
 (0)