Skip to content

Commit

Permalink
Merge pull request #162 from laixintao/server-command
Browse files Browse the repository at this point in the history
More server commands support, and refactor get server version.
  • Loading branch information
laixintao authored Oct 22, 2019
2 parents ed4eff8 + 8ed0914 commit 79b3a34
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 100 deletions.
40 changes: 38 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,47 @@ jobs:
. venv/bin/activate
pytest
unittest-python3.8:
<<: *unit-template
unittest-python3.8: &unit-template
docker:
- image: circleci/python:3.8.0
- image: circleci/redis:5

working_directory: ~/repo

steps:
- checkout

- run:
name: "Pull Submodules"
command: |
git submodule init
git submodule update --remote
# Download and cache dependencies
- restore_cache:
keys:
- v4-dependencies-3.8.0-{{ checksum "pyproject.toml" }}
# fallback to using the latest cache if no exact match is found
- v4-dependencies-3.8.0-

- run:
name: install dependencies
command: |
python3 -m venv venv
. venv/bin/activate
pip install poetry pip==19
poetry install --develop=DEVELOP
- save_cache:
paths:
- ./venv
key: v4-dependencies-3.8.0-{{ checksum "pyproject.toml" }}

- run:
name: run tests
command: |
. venv/bin/activate
pytest
workflows:
version: 2
test-n-release:
Expand Down
26 changes: 13 additions & 13 deletions iredis/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from . import markdown
from .utils import compose_command_syntax
from .exceptions import NotRedisCommand
from . import utils

project_path = Path(os.path.dirname(os.path.abspath(__file__))) / "data"
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -75,8 +76,11 @@ def __init__(self, host, port, db, password=None, encoding=None, get_info=True):
config.no_version_reason = "--no-info flag activated"

def get_server_info(self):
info_resp = self.execute_command_and_read_response(None, "INFO")
version = re.findall(r"^redis_version:(.*)$", info_resp, re.MULTILINE)[0]
self.connection.send_command("INFO")
# safe to decode Redis's INFO response
info_resp = utils.ensure_str(self.connection.read_response())

version = re.findall(r"^redis_version:([\d\.]+)\r\n", info_resp, re.MULTILINE)[0]
logger.debug(f"[Redis Version] {version}")
config.version = version

Expand All @@ -96,23 +100,18 @@ def execute_command_and_read_response(
"Execute a command and return a parsed response"
try:
self.connection.send_command(command_name, *args)
resp = self.parse_response(
self.connection, completer, command_name, **options
)
response = self.connection.read_response()
# retry on timeout
except (ConnectionError, TimeoutError) as e:
self.connection.disconnect()
if not (self.connection.retry_on_timeout and isinstance(e, TimeoutError)):
raise
self.connection.send_command(command_name, *args)
resp = self.parse_response(
self.connection, completer, command_name, **options
)
response = self.connection.read_response()
except redis.exceptions.ExecAbortError:
config.transaction = False
raise

return resp
return self.render_response(response, completer, command_name, **options)

def render_command_result(self, command_name, response, completer):
"""
Expand All @@ -139,9 +138,8 @@ def render_command_result(self, command_name, response, completer):
logger.info(f"[rendered] {rendered}")
return rendered

def parse_response(self, connection, completer, command_name, **options):
def render_response(self, response, completer, command_name, **options):
"Parses a response from the Redis server"
response = connection.read_response()
logger.info(f"[Redis-Server] Response: {response}")
# if in transaction, use queue render first
if config.transaction:
Expand Down Expand Up @@ -260,12 +258,14 @@ def do_help(self, *args):

avaiable_version = summary_dict.get("since", "?")
server_version = config.version
# FIXME anything strange with single quotes?
logger.debug(f"[--version--] '{server_version}'")
try:
is_avaiable = StrictVersion(server_version) > StrictVersion(
avaiable_version
)
except Exception as e:
logger.error(e)
logger.exception(e)
is_avaiable = None

if is_avaiable:
Expand Down
30 changes: 6 additions & 24 deletions iredis/completers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@
from typing import Iterable
from prompt_toolkit.contrib.regular_languages.completion import GrammarCompleter
from prompt_toolkit.contrib.regular_languages.compiler import compile
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.completion import WordCompleter, FuzzyWordCompleter
from prompt_toolkit.document import Document
from prompt_toolkit.completion import Completion, CompleteEvent

from .config import config, COMPILING_DONE, COMPILING_JUST_FINISH
from .redis_grammar import REDIS_COMMANDS
from .redis_grammar import REDIS_COMMANDS, CONST
from .lexer import get_lexer
from .commands_csv_loader import group2commands, commands_summary
from .commands_csv_loader import group2commands, commands_summary, all_commands


logger = logging.getLogger(__name__)


class LatestUsedFirstWordCompleter(WordCompleter):
class LatestUsedFirstWordCompleter(FuzzyWordCompleter):
"""
Not thread safe.
"""
Expand Down Expand Up @@ -84,29 +84,10 @@ def get_completer(group2commands, redis_grammar):
key_completer = LatestUsedFirstWordCompleter(config.completer_max, [])
member_completer = LatestUsedFirstWordCompleter(config.completer_max, [])
field_completer = LatestUsedFirstWordCompleter(config.completer_max, [])
const_completers = {
"failoverchoice": "TAKEOVER FORCE",
"withscores": "WITHSCORES",
"limit": "LIMIT",
"expiration": "EX PX",
"condition": "NX XX",
"operation": "AND OR XOR NOT",
"changed": "CHANGED",
"incr": "INCR",
"withscores": "WITHSCORES",
"resetchoise": "HARD SOFT",
"match": "MATCH",
"count_const": "COUNT",
"type_const": "TYPE",
"type": "string list set zset hash stream",
"position_choice": "BEFORE AFTER",
"error": "TIMEOUT ERROR",
"async": "ASYNC",
}
completer_mapping.update(
{
key: WordCompleter(tokens.split(" "), ignore_case=True)
for key, tokens in const_completers.items()
for key, tokens in CONST.items()
}
)
completer_mapping.update(
Expand All @@ -124,6 +105,7 @@ def get_completer(group2commands, redis_grammar):
"fields": field_completer,
}
)
completer_mapping["commandname"] = WordCompleter(all_commands, ignore_case=True)
completer = RedisGrammarCompleter(redis_grammar, completer_mapping)
return completer

Expand Down
20 changes: 10 additions & 10 deletions iredis/data/command_syntax.csv
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ server,BGSAVE,command,render_simple_string
server,CLIENT GETNAME,command,render_bulk_string
server,CLIENT ID,command,render_int
server,CLIENT KILL,command_pass,
server,CLIENT LIST,command_pass,
server,CLIENT LIST,command_type_conntype_x,render_bulk_string_decode
server,CLIENT PAUSE,command_pass,
server,CLIENT REPLY,command_pass,
server,CLIENT SETNAME,command_value,render_simple_string
Expand All @@ -129,21 +129,21 @@ server,FLUSHALL,command_asyncx,render_simple_string
server,FLUSHDB,command_asyncx,render_simple_string
server,INFO,command,render_bulk_string_decode
server,LASTSAVE,command,render_unixtime
server,MEMORY DOCTOR,command_pass,
server,MEMORY HELP,command_pass,
server,MEMORY DOCTOR,command,render_bulk_string_decode
server,MEMORY HELP,command,render_list
server,MEMORY MALLOC-STATS,command_pass,
server,MEMORY PURGE,command_pass,
server,MEMORY STATS,command_pass,
server,MEMORY USAGE,command_pass,
server,MEMORY PURGE,command,render_simple_string
server,MEMORY STATS,command,render_nested_pair
server,MEMORY USAGE,command_key_samples_count,render_int
server,MONITOR,command_pass,
server,REPLICAOF,command_pass,
server,ROLE,command_pass,
server,ROLE,command,
server,SAVE,command,render_simple_string
server,SHUTDOWN,command_pass,
server,SLAVEOF,command_pass,
server,SLOWLOG,command_pass,
server,SYNC,command_pass,
server,TIME,command_pass,
server,SYNC,command,
server,TIME,command,render_time
set,SADD,command_key_members,render_int
set,SCARD,command_key,render_int
set,SDIFF,command_keys,render_list
Expand Down Expand Up @@ -226,4 +226,4 @@ transactions,EXEC,command,render_list
transactions,MULTI,command,render_simple_string
transactions,UNWATCH,command,render_simple_string
transactions,WATCH,command_keys,render_simple_string
iredis,HELP,command_pass,
iredis,HELP,command_commandname,
13 changes: 2 additions & 11 deletions iredis/lexer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from prompt_toolkit.lexers import SimpleLexer
from prompt_toolkit.contrib.regular_languages.lexer import GrammarLexer
from .redis_grammar import CONST


def get_lexer(command_groups, redis_grammar):
Expand Down Expand Up @@ -44,19 +45,9 @@ def get_lexer(command_groups, redis_grammar):
"type": SimpleLexer("class:string"),
"fields": SimpleLexer("class:field"),
"field": SimpleLexer("class:field"),
# 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"),
"position_choice": SimpleLexer("class:const"),
"error": SimpleLexer("class:const"),
"async": SimpleLexer("class:const"),
}

lexers_dict.update({key: SimpleLexer("class:const") for key in CONST})
lexers_dict.update({key: SimpleLexer("class:command") for key in command_groups})
lexer = GrammarLexer(redis_grammar, lexers=lexers_dict)
return lexer
Loading

0 comments on commit 79b3a34

Please sign in to comment.