Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SCAN command highlights, completer, and result renders. #139

Merged
merged 22 commits into from
Sep 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 11 additions & 3 deletions iredis/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def execute_command_and_read_response(
if command_name.upper() in ["EXEC", "DISCARD"]:
logger.debug(f"[After hook] Command is {command_name}, unset transaction.")
config.transaction = False
if command_name.upper() in ["ZSCAN", "ZPOPMAX", "ZPOPMIN"]:
config.withscores = True

try:
self.connection.send_command(command_name, *args)
Expand Down Expand Up @@ -154,17 +156,18 @@ def send_command(self, command, completer):
input_command = ""
try:
input_command, args = split_command_args(command, all_commands)
self.patch_completers(command, completer)
self.pre_hook(command, completer)
redis_resp = self.execute_command_and_read_response(
completer, input_command, *args
)
except Exception as e:
logger.exception(e)
return render_error(str(e))

finally:
config.withscores = False
return redis_resp

def patch_completers(self, command, completer):
def pre_hook(self, command, completer):
"""
Before execute command, patch completers first.
Eg: When user run `GET foo`, key completer need to
Expand All @@ -181,6 +184,11 @@ def patch_completers(self, command, completer):
# invalide command!
return
variables = m.variables()
# zset withscores
withscores = variables.get("withscores")
logger.debug(f"[PRE HOOK] withscores: {withscores}")
if withscores:
config.withscores = True

# auto update LatestUsedFirstWordCompleter
for _token, _completer in completer.completers.items():
Expand Down
6 changes: 4 additions & 2 deletions iredis/completers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,12 @@ def touch(self, word):
Make sure word is in the first place of the completer
list.
"""
logger.info(f"[LRU]: {self.words} ++ {word}")
if word in self.words:
self.words.remove(word)
else: # not in words
if len(self.words) == self.max_words: # full
self.words.pop()
self.words.insert(0, word)
logger.info(f"[LRU] Done: {self.words}")

def touch_words(self, words):
for word in words:
Expand Down Expand Up @@ -96,6 +94,10 @@ def get_completer(group2commands, redis_grammar):
"incr": "INCR",
"withscores": "WITHSCORES",
"resetchoise": "HARD SOFT",
"match": "MATCH",
"count_const": "COUNT",
"type_const": "TYPE",
"type": "string list set zset hash stream",
}
completer_mapping.update(
{
Expand Down
2 changes: 2 additions & 0 deletions iredis/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def __init__(self):
self.queued_commands = []
# show command hint?
self.newbie_mode = False
# display zset withscores?
self.withscores = False

def __setter__(self, name, value):
# for every time start a transaction
Expand Down
16 changes: 8 additions & 8 deletions iredis/data/command_syntax.csv
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ generic,RANDOMKEY,command,
generic,RENAME,command_key_newkey,
generic,RENAMENX,command_key_newkey,
generic,RESTORE,command_pass,
generic,SCAN,command_pass,
generic,SCAN,command_cursor_match_pattern_count_type,command_scan
generic,SORT,command_pass,
generic,TOUCH,command_keys,
generic,TTL,command_key,
generic,TYPE,command_key,
generic,TYPE,command_key,render_simple_strings
generic,UNLINK,command_keys,
generic,WAIT,command_pass,
geo,GEOADD,command_pass,
Expand Down Expand Up @@ -156,7 +156,7 @@ set,SMOVE,command_key_newkey_member,render_int
set,SPOP,command_key_count_x,render_list_or_string
set,SRANDMEMBER,command_key_count_x,render_list_or_string
set,SREM,command_key_members,render_int
set,SSCAN,command_pass,
set,SSCAN,command_key_cursor_match_pattern_count,command_sscan
set,SUNION,command_keys,render_list
set,SUNIONSTORE,command_destination_keys,render_int
sorted_set,BZPOPMAX,command_keys_timeout,render_list_or_string
Expand All @@ -167,11 +167,11 @@ sorted_set,ZCOUNT,command_key_min_max,render_int
sorted_set,ZINCRBY,command_key_float_member,render_simple_strings
sorted_set,ZINTERSTORE,command_pass,render_int
sorted_set,ZLEXCOUNT,command_key_lexmin_lexmax,render_int
sorted_set,ZPOPMAX,command_key_count_x,render_list
sorted_set,ZPOPMIN,command_key_count_x,render_list
sorted_set,ZRANGE,command_key_start_end_withscores_x,render_list
sorted_set,ZPOPMAX,command_key_count_x,render_members
sorted_set,ZPOPMIN,command_key_count_x,render_members
sorted_set,ZRANGE,command_key_start_end_withscores_x,render_members
sorted_set,ZRANGEBYLEX,command_key_lexmin_lexmax_limit_offset_count,render_list
sorted_set,ZRANGEBYSCORE,command_key_min_max_withscore_x_limit_offset_count_x,render_list
sorted_set,ZRANGEBYSCORE,command_key_min_max_withscore_x_limit_offset_count_x,render_members
sorted_set,ZRANK,command_key_member,render_int
sorted_set,ZREM,command_key_members,render_int
sorted_set,ZREMRANGEBYLEX,command_key_lexmin_lexmax,render_int
Expand All @@ -181,7 +181,7 @@ sorted_set,ZREVRANGE,command_key_start_end_withscores_x,render_list
sorted_set,ZREVRANGEBYLEX,command_key_lexmin_lexmax_limit_offset_count,render_list
sorted_set,ZREVRANGEBYSCORE,command_key_min_max_withscore_x_limit_offset_count_x,render_list
sorted_set,ZREVRANK,command_key_member,render_int
sorted_set,ZSCAN,command_pass,
sorted_set,ZSCAN,command_key_cursor_match_pattern_count,command_sscan
sorted_set,ZSCORE,command_key_member,render_simple_strings
sorted_set,ZUNIONSTORE,command_pass,render_int
stream,XACK,command_pass,
Expand Down
6 changes: 6 additions & 0 deletions iredis/lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,17 @@ def get_lexer(command_groups, redis_grammar):
"max": SimpleLexer("class:integer"),
"score": SimpleLexer("class:integer"),
"timeout": SimpleLexer("class:integer"),
"cursor": SimpleLexer("class:integer"),
"pattern": SimpleLexer("class:pattern"),
"type": SimpleLexer("class:string"),
# const
"condition": SimpleLexer("class:const"),
"operation": SimpleLexer("class:const"),
"withscores": SimpleLexer("class:const"),
"limit": SimpleLexer("class:const"),
"match": SimpleLexer("class:const"),
"count_const": SimpleLexer("class:const"),
"type_const": SimpleLexer("class:const"),
}

lexers_dict.update({key: SimpleLexer("class:command") for key in command_groups})
Expand Down
18 changes: 12 additions & 6 deletions iredis/redis_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
MESSAGE = fr"(?P<message>{VALID_TOKEN})"
BIT = r"(?P<bit>0|1)"
FLOAT = fr"(?P<float>{_FLOAT})"
CURSOR = fr"(?P<cursor>{NUM})"
# IP re copied from:
# https://www.regular-expressions.info/ip.html
IP = r"""(?P<ip>(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.
Expand Down Expand Up @@ -72,18 +73,18 @@
CHANGED = r"(?P<changed>(CH|ch))"
INCR = r"(?P<incr>(INCR|incr))"
WITHSCORES = r"(?P<withscores>(WITHSCORES|withscores))"
FAILOVERCHOICE = (
r"(?P<failoverchoice>(FORCE|TAKEOVER|force|takeover))"
)
RESETCHOICE = (
r"(?P<resetchoice>(HARD|SOFT|hard|soft))"
)
FAILOVERCHOICE = r"(?P<failoverchoice>(FORCE|TAKEOVER|force|takeover))"
RESETCHOICE = r"(?P<resetchoice>(HARD|SOFT|hard|soft))"
SLOTSUBCMD = r"(?P<slotsubcmd>(IMPORTING|MIGRATING|NODE|importing|migrating|node))"
SLOTSUBCMDBARE = r"(?P<slotsubcmd>(STABLE|stable))"
WEIGHTS_CONST = r"(?P<weights_const>(WEIGHTS|weights))"
AGGREGATE_CONST = r"(?P<aggregate_const>(AGGREGATE|aggregate))"
AGGREGATE = r"(?P<aggregate>(SUM|MIN|MAX|sum|min|max))"
LIMIT = r"(?P<limit>(LIMIT|limit))"
MATCH = r"(?P<match>(MATCH|match))"
COUNT_CONST = r"(?P<count_const>(COUNT|count))"
TYPE_CONST = r"(?P<type_const>(TYPE|type))"
TYPE = r"(?P<type>(STRING|LIST|SET|ZSET|HASH|STREAM|string|list|set|zset|hash|stream))"

REDIS_COMMANDS = fr"""
(\s* (?P<command_slots>({t['command_slots']})) \s+ {SLOTS} \s*)|
Expand Down Expand Up @@ -189,6 +190,11 @@
({t['command_key_min_max_withscore_x_limit_offset_count_x']}))
\s+ {KEY} \s+ {MIN} \s+ {MAX} (\s+ {WITHSCORES})?
(\s+ {LIMIT} \s+ {OFFSET} \s+ {COUNT})? \s*)|
(\s* (?P<command_cursor_match_pattern_count_type>({t['command_cursor_match_pattern_count_type']}))
\s+ {CURSOR} (\s+ {MATCH} \s+ {PATTERN})?
(\s+ {COUNT_CONST} \s+ {COUNT})? (\s+ {TYPE_CONST} \s+ {TYPE})? \s*)|
(\s* (?P<command_key_cursor_match_pattern_count>({t['command_key_cursor_match_pattern_count']}))
\s+ {KEY} \s+ {CURSOR} (\s+ {MATCH} \s+ {PATTERN})? (\s+ {COUNT_CONST} \s+ {COUNT})? s*)|
"""


Expand Down
104 changes: 94 additions & 10 deletions iredis/renders.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def render_string_or_int(text, completer=None):


def render_error(error_msg):
# FIXME raw
text = _ensure_str(error_msg)
return FormattedText([("class:type", "(error) "), ("class:error", text)])

Expand All @@ -157,6 +158,7 @@ def render_ok(text, completer):
If response is b'OK', render ok with success color.
else render message with Error color.
"""
# FIXME raw
if text is None:
return NIL
text = _ensure_str(text)
Expand All @@ -170,29 +172,111 @@ def render_transaction_queue(text, completer):

Response message should be "QUEUE" or Error.
"""
# FIXME raw
text = _ensure_str(text)
assert text == "QUEUED"
return FormattedText([("class:queued", text)])


def command_keys(items, completer):
def _update_completer_then_render(items, completer, complter_name, style, completer_iter_step=1):
"""
:param completer_iter_step: every `completer_iter_step` items is used to update complters
"""
str_items = _ensure_str(items)

# update completers
if completer:
key_completer = completer.completers["key"]
key_completer.touch_words(str_items)
logger.debug(f"[Completer] key completer updated.")
token_completer = completer.completers[complter_name]
token_completer.touch_words(str_items[::completer_iter_step])
logger.debug(f"[Completer] {complter_name} completer updated.")
else:
logger.debug(f"[Completer] completer is None, not updated.")

# render is render, completer is completer
# render and completer are in same function but code are splitted.
# Give back to Ceasar what is Ceasar's and to God what is God's.
double_quoted = _double_quotes(str_items)
rendered = _render_list(items, double_quoted, "class:key")
rendered = _render_list(items, double_quoted, style)
return rendered


def _update_completer_then_render_withscores(items, completer):
if not items:
return EMPTY_LIST
complter_name = "member"
str_items = _ensure_str(items)

members = [item for item in str_items[::2]]
scores = [item for item in str_items[1::2]]
logger.debug(f"[MEMBERS] {members}")
logger.debug(f"[SCORES] {scores}")
# update completers
if completer:
token_completer = completer.completers[complter_name]
token_completer.touch_words(members)
logger.debug(f"[Completer] {complter_name} completer updated.")
else:
logger.debug(f"[Completer] completer is None, not updated.")
# render display
double_quoted = _double_quotes(members)
index_width = len(str(len(double_quoted)))
score_width = max(len(score) for score in scores)
rendered = []
for index, item in enumerate(double_quoted):
index_const_width = f"{index+1:{index_width}})"
rendered.append(("", index_const_width))
# add a space between index and member
rendered.append(("", " "))
# add score
rendered.append(("class:integer", f"{scores[index]:{score_width}} "))
# add member
if item is None:
rendered.append(NIL_TUPLE)
else:
rendered.append(("class:member", item))

# add a newline for eachline
if index + 1 < len(double_quoted):
rendered.append(NEWLINE_TUPLE)
return FormattedText(rendered)


def command_keys(items, completer):
return _update_completer_then_render(items, completer, "key", "class:key")


def render_members(items, completer):
if config.withscores:
if config.raw:
return _update_completer_then_render(items, completer, "member", "class:member", completer_iter_step=2)
return _update_completer_then_render_withscores(items, completer)
return _update_completer_then_render(items, completer, "member", "class:member", completer_iter_step=1)


def _render_scan(render_response, response, completer):
cursor, responses = response
if config.raw:
return b"\n".join([cursor, render_response(responses, completer)])

rendered = [
("class:type", "(cursor) "),
("class:integer", cursor.decode()),
("", "\n"),
]
rendered_keys = render_response(responses, completer)
return FormattedText(rendered + rendered_keys)


def command_scan(response, completer):
"""
Render Scan command result.
see: https://redis.io/commands/scan
"""
return _render_scan(command_keys, response, completer)


def command_sscan(response, completer):
return _render_scan(render_members, response, completer)


def command_zscan(response, completer):
return _render_scan(render_members, response, completer)


# TODO
# special list render, bzpopmax, key-value pair
1 change: 1 addition & 0 deletions scripts/add_fooset_commands.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SADD fooset alligator ant bear bee bird camel cat cheetah chicken chimpanzee cow crocodile deer dog dolphin duck eagle elephant fish fly fox frog giraffe goat goldfish hamster hippopotamus horse kangaroo kitten lion lobster monkey octopus owl panda pig puppy rabbit rat scorpion seal shark sheep snail snake spider squirrel tiger turtle wolf zebra
52 changes: 52 additions & 0 deletions scripts/add_myzset_commands.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
ZADD myzset 28693 alligator
ZADD myzset 29596 ant
ZADD myzset 11320 bear
ZADD myzset 28872 bee
ZADD myzset 26656 bird
ZADD myzset 708 camel
ZADD myzset 31829 cat
ZADD myzset 7424 cheetah
ZADD myzset 30668 chicken
ZADD myzset 154 chimpanzee
ZADD myzset 24709 cow
ZADD myzset 25916 crocodile
ZADD myzset 6888 deer
ZADD myzset 32034 dog
ZADD myzset 15528 dolphin
ZADD myzset 667 duck
ZADD myzset 202 eagle
ZADD myzset 19551 elephant
ZADD myzset 32231 fish
ZADD myzset 4002 fly
ZADD myzset 18679 fox
ZADD myzset 10147 frog
ZADD myzset 28405 giraffe
ZADD myzset 15557 goat
ZADD myzset 2062 goldfish
ZADD myzset 25018 hamster
ZADD myzset 19888 hippopotamus
ZADD myzset 24984 horse
ZADD myzset 16088 kangaroo
ZADD myzset 7907 kitten
ZADD myzset 9814 lion
ZADD myzset 32194 lobster
ZADD myzset 8036 monkey
ZADD myzset 19483 octopus
ZADD myzset 9398 owl
ZADD myzset 24987 panda
ZADD myzset 28153 pig
ZADD myzset 28829 puppy
ZADD myzset 9709 rabbit
ZADD myzset 14184 rat
ZADD myzset 11208 scorpion
ZADD myzset 11385 seal
ZADD myzset 1900 shark
ZADD myzset 6890 sheep
ZADD myzset 6897 snail
ZADD myzset 7659 snake
ZADD myzset 22338 spider
ZADD myzset 3676 squirrel
ZADD myzset 1490 tiger
ZADD myzset 31275 turtle
ZADD myzset 8113 wolf
ZADD myzset 1601 zebra