Merging upstream version 1.12.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
efb6cb056d
commit
66b78e69ac
17 changed files with 293 additions and 71 deletions
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 1.12.0
|
||||
current_version = 1.12.1
|
||||
commit = True
|
||||
tag = True
|
||||
|
||||
|
|
2
.github/workflows/test.yaml
vendored
2
.github/workflows/test.yaml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
python: ['3.6', '3.7', '3.8', '3.9', '3.10']
|
||||
redis: [5, 6]
|
||||
redis: [5, 6, 7]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
services:
|
||||
|
|
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -1,3 +1,11 @@
|
|||
## UPCOMING
|
||||
|
||||
- Feature: support new command: `HRANDFIELD`.
|
||||
- Bugfix: all tests pass on redis:7 now.
|
||||
- Feature: IRedis now accept `username` for auth, redis server version under 6
|
||||
will ignore `username`.
|
||||
- Feature: IRedis support prompt now, you can customize prompt string. (thanks to [aymericbeaumet])
|
||||
|
||||
## 1.12
|
||||
|
||||
- Feature: `CLIENT KILL` now support `LADDR` argument.
|
||||
|
@ -15,14 +23,17 @@
|
|||
- Feature: support new command: `GETEX`.
|
||||
- Feature: `FLUSHDB` and `FLUSHALL` supports `SYNC` option.
|
||||
- Feature: `GEOADD` supports `CH XX NX` options.
|
||||
- Feature: Timestamp Completers are now support completion for timestamp fields and milliseconds timestamp fields.
|
||||
- Deprecate: `GEORADIUS` is deprecated, no auto-complete for this command anymore.
|
||||
- Deprecate: `GEORADIUSBYMEMBER` is deprecated, no auto-complete for this command anymore.
|
||||
- Feature: Timestamp Completers are now support completion for timestamp fields
|
||||
and milliseconds timestamp fields.
|
||||
- Deprecate: `GEORADIUS` is deprecated, no auto-complete for this command
|
||||
anymore.
|
||||
- Deprecate: `GEORADIUSBYMEMBER` is deprecated, no auto-complete for this
|
||||
command anymore.
|
||||
|
||||
### 1.11.1
|
||||
|
||||
- Bugfix: Switch `distutils.version` to `packaging.version` to fix the version parse
|
||||
for windows. (new dependency: pypi's python-packaging.
|
||||
- Bugfix: Switch `distutils.version` to `packaging.version` to fix the version
|
||||
parse for windows. (new dependency: pypi's python-packaging.
|
||||
|
||||
## 1.11
|
||||
|
||||
|
@ -282,3 +293,4 @@
|
|||
[hanaasagi]: https://github.com/Hanaasagi
|
||||
[sid-maddy]: https://github.com/sid-maddy
|
||||
[tssujt]: https://github.com/tssujt
|
||||
[aymericbeaumet]: https://github.com/aymericbeaumet
|
||||
|
|
25
README.md
25
README.md
|
@ -60,6 +60,8 @@ like `KEYS *` (see
|
|||
- Written in pure Python, but IRedis was packaged into a single binary with
|
||||
[PyOxidizer](https://github.com/indygreg/PyOxidizer), you can use cURL to
|
||||
download and run, it just works, even you don't have a Python interpreter.
|
||||
- You can change the cli prompt using `--prompt` option or set via `~/.iredisrc`
|
||||
config file.
|
||||
- Hide password for `AUTH` command.
|
||||
- Says "Goodbye!" to you when you exit!
|
||||
- For full features, please see: [iredis.io](https://www.iredis.io)
|
||||
|
@ -190,6 +192,29 @@ staging=redis://username:password@staging-redis.example.com:6379/1
|
|||
Put this in your `iredisrc` then connect via `iredis -d staging` or
|
||||
`iredis -d dev`.
|
||||
|
||||
### Change The Default Prompt
|
||||
|
||||
You can change the prompt str, the default prompt is:
|
||||
|
||||
```shell
|
||||
127.0.0.1:6379>
|
||||
```
|
||||
|
||||
Which is rendered by `{host}:{port}[{db}]> `, you can change this via `--prompt`
|
||||
option or change
|
||||
[iredisrc](https://github.com/laixintao/iredis/blob/master/iredis/data/iredisrc)
|
||||
config file. The prompwt string uses python string format engine, supported
|
||||
interpolations:
|
||||
|
||||
- `{client_name}`
|
||||
- `{db}`
|
||||
- `{host}`
|
||||
- `{path}`
|
||||
- `{port}`
|
||||
- `{username}`
|
||||
- `{client_addr}`
|
||||
- `{client_id}`
|
||||
|
||||
### Configuration
|
||||
|
||||
IRedis supports config files. Command-line options will always take precedence
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "1.12.0"
|
||||
__version__ = "1.12.1"
|
||||
|
|
|
@ -55,25 +55,35 @@ class Client:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
host=None,
|
||||
port=None,
|
||||
host="127.0.0.1",
|
||||
port=6379,
|
||||
db=0,
|
||||
password=None,
|
||||
path=None,
|
||||
scheme="redis",
|
||||
username=None,
|
||||
client_name=None,
|
||||
prompt=None,
|
||||
):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.db = db
|
||||
self.path = path
|
||||
# FIXME username is not using...
|
||||
self.username = username
|
||||
self.client_name = client_name
|
||||
self.scheme = scheme
|
||||
self.password = password
|
||||
|
||||
# cli args --prompt will overwrite the prompt in iredisrc config file
|
||||
self.prompt = ""
|
||||
if config.prompt:
|
||||
self.prompt = config.prompt
|
||||
if prompt:
|
||||
self.prompt = prompt
|
||||
|
||||
self.client_id = None
|
||||
self.client_addr = None
|
||||
|
||||
self.build_connection()
|
||||
|
||||
# all command upper case
|
||||
|
@ -93,6 +103,13 @@ class Client:
|
|||
else:
|
||||
config.no_version_reason = "--no-info flag activated"
|
||||
|
||||
if self.prompt and "client_addr" in self.prompt:
|
||||
self.client_addr = ":".join(
|
||||
str(x) for x in self.connection._sock.getsockname()
|
||||
)
|
||||
if self.prompt and "client_id" in self.prompt:
|
||||
self.client_id = str(self.execute("CLIENT ID"))
|
||||
|
||||
if config.version and re.match(r"([\d\.]+)", config.version):
|
||||
self.auth_compat(config.version)
|
||||
|
||||
|
@ -131,6 +148,11 @@ class Client:
|
|||
"socket_keepalive": config.socket_keepalive,
|
||||
"client_name": client_name,
|
||||
}
|
||||
|
||||
# if username is set without setting paswword, password will be ignored
|
||||
if password:
|
||||
connection_kwargs["username"] = username
|
||||
|
||||
if scheme == "rediss":
|
||||
connection_class = SSLConnection
|
||||
else:
|
||||
|
@ -141,6 +163,7 @@ class Client:
|
|||
"password": password,
|
||||
"path": path,
|
||||
"client_name": client_name,
|
||||
"username": username,
|
||||
}
|
||||
connection_class = UnixDomainSocketConnection
|
||||
|
||||
|
@ -150,7 +173,8 @@ class Client:
|
|||
connection_kwargs["encoding_errors"] = "replace"
|
||||
|
||||
logger.debug(
|
||||
f"connection_class={connection_class}, connection_kwargs={connection_kwargs}"
|
||||
f"connection_class={connection_class},"
|
||||
f" connection_kwargs={connection_kwargs}"
|
||||
)
|
||||
|
||||
return connection_class(**connection_kwargs)
|
||||
|
@ -191,6 +215,18 @@ class Client:
|
|||
config.version = version
|
||||
|
||||
def __str__(self):
|
||||
if self.prompt: # not None and not empty
|
||||
return self.prompt.format(
|
||||
client_name=self.client_name,
|
||||
db=self.db,
|
||||
host=self.host,
|
||||
path=self.path,
|
||||
port=self.port,
|
||||
username=self.username,
|
||||
client_addr=self.client_addr,
|
||||
client_id=self.client_id,
|
||||
)
|
||||
|
||||
if self.scheme == "unix":
|
||||
prompt = f"redis {self.path}"
|
||||
else:
|
||||
|
@ -198,7 +234,8 @@ class Client:
|
|||
|
||||
if self.db:
|
||||
prompt = f"{prompt}[{self.db}]"
|
||||
return prompt
|
||||
|
||||
return f"{prompt}> "
|
||||
|
||||
def client_execute_command(self, command_name, *args):
|
||||
command = command_name.upper()
|
||||
|
@ -222,7 +259,8 @@ class Client:
|
|||
Here we retry once for ConnectionError.
|
||||
"""
|
||||
logger.info(
|
||||
f"execute by connection: connection={connection}, name={command_name}, {args}, {options}"
|
||||
f"execute by connection: connection={connection}, name={command_name},"
|
||||
f" {args}, {options}"
|
||||
)
|
||||
retry_times = config.retry_times # FIXME configurable
|
||||
last_error = None
|
||||
|
@ -248,7 +286,7 @@ class Client:
|
|||
last_error = e
|
||||
retry_times -= 1
|
||||
need_refresh_connection = True
|
||||
except (ResponseError) as e:
|
||||
except ResponseError as e:
|
||||
response_message = str(e)
|
||||
if response_message.startswith("MOVED"):
|
||||
return self.reissue_with_redirect(
|
||||
|
|
|
@ -63,6 +63,8 @@ class Config:
|
|||
self.withscores = False
|
||||
self.version = "Unknown"
|
||||
|
||||
self.prompt = None
|
||||
|
||||
def __setter__(self, name, value):
|
||||
# for every time start a transaction
|
||||
# clear the queued commands first
|
||||
|
@ -126,5 +128,6 @@ def load_config_files(iredisrc):
|
|||
config.shell = config_obj["main"].as_bool("shell")
|
||||
config.pager = config_obj["main"].get("pager")
|
||||
config.enable_pager = config_obj["main"].as_bool("enable_pager")
|
||||
config.prompt = config_obj["main"].get("prompt")
|
||||
|
||||
return config_obj
|
||||
|
|
|
@ -87,6 +87,7 @@ hash,HKEYS,command_key,command_hkeys
|
|||
hash,HLEN,command_key,render_int
|
||||
hash,HMGET,command_key_fields,render_list
|
||||
hash,HMSET,command_key_fieldvalues,render_bulk_string
|
||||
hash,HRANDFIELD,command_key_count_withvalues,render_list_or_string
|
||||
hash,HSCAN,command_key_cursor_match_pattern_count,command_hscan
|
||||
hash,HSET,command_key_field_value,render_int
|
||||
hash,HSETNX,command_key_field_value,render_int
|
||||
|
@ -103,14 +104,14 @@ list,LINDEX,command_key_position,render_bulk_string
|
|||
list,LINSERT,command_key_positionchoice_pivot_value,render_int
|
||||
list,LLEN,command_key,render_int
|
||||
list,LPOS,command_lpos,render_list_or_string
|
||||
list,LPOP,command_key,render_bulk_string
|
||||
list,LPOP,command_key,render_list_or_string
|
||||
list,LPUSH,command_key_values,render_int
|
||||
list,LPUSHX,command_key_values,render_int
|
||||
list,LRANGE,command_key_start_end,render_list
|
||||
list,LREM,command_key_position_value,render_int
|
||||
list,LSET,command_key_position_value,render_simple_string
|
||||
list,LTRIM,command_key_start_end,render_simple_string
|
||||
list,RPOP,command_key,render_bulk_string
|
||||
list,RPOP,command_key,render_list_or_string
|
||||
list,RPOPLPUSH,command_key_newkey,render_bulk_string
|
||||
list,RPUSH,command_key_values,render_int
|
||||
list,RPUSHX,command_key_value,render_int
|
||||
|
|
|
|
@ -58,6 +58,21 @@ warning = True
|
|||
# eg. ~/.iredis.log
|
||||
log_location =
|
||||
|
||||
# You can change the prompt str, if left blank, the default prompt would be:
|
||||
# 127.0.0.1:6379>
|
||||
# which is rendered by "{host}:{port}[{db}]> "
|
||||
# supported interpolations:
|
||||
# {client_name}
|
||||
# {db}
|
||||
# {host}
|
||||
# {path}
|
||||
# {port}
|
||||
# {username}
|
||||
# {client_addr}
|
||||
# {client_id}
|
||||
# The prompt string uses python string format engine
|
||||
prompt =
|
||||
|
||||
# History file location
|
||||
history_location = ~/.iredis_history
|
||||
|
||||
|
|
|
@ -123,22 +123,22 @@ def write_result(text, max_height=None):
|
|||
|
||||
class Rainbow:
|
||||
color = [
|
||||
("#cc2244"),
|
||||
("#bb4444"),
|
||||
("#996644"),
|
||||
("#cc8844"),
|
||||
("#ccaa44"),
|
||||
("#bbaa44"),
|
||||
("#99aa44"),
|
||||
("#778844"),
|
||||
("#55aa44"),
|
||||
("#33aa44"),
|
||||
("#11aa44"),
|
||||
("#11aa66"),
|
||||
("#11aa88"),
|
||||
("#11aaaa"),
|
||||
("#11aacc"),
|
||||
("#11aaee"),
|
||||
"#cc2244",
|
||||
"#bb4444",
|
||||
"#996644",
|
||||
"#cc8844",
|
||||
"#ccaa44",
|
||||
"#bbaa44",
|
||||
"#99aa44",
|
||||
"#778844",
|
||||
"#55aa44",
|
||||
"#33aa44",
|
||||
"#11aa44",
|
||||
"#11aa66",
|
||||
"#11aa88",
|
||||
"#11aaaa",
|
||||
"#11aacc",
|
||||
"#11aaee",
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
@ -160,8 +160,7 @@ class Rainbow:
|
|||
|
||||
|
||||
def prompt_message(client):
|
||||
# TODO custom prompt
|
||||
text = "{hostname}> ".format(hostname=str(client))
|
||||
text = str(client)
|
||||
if config.rainbow:
|
||||
return list(zip(Rainbow(), text))
|
||||
return text
|
||||
|
@ -248,8 +247,11 @@ PAGER_HELP = """Using pager when output is too tall for your window, default to
|
|||
@click.option(
|
||||
"-s", "--socket", default=None, help="Server socket (overrides hostname and port)."
|
||||
)
|
||||
@click.option("-n", help="Database number.(overwrites dsn/url's db number)", default=0)
|
||||
@click.option(
|
||||
"-n", help="Database number.(overwrites dsn/url's db number)", default=None
|
||||
"-u",
|
||||
"--username",
|
||||
help="User name used to auth, will be ignore for redis version < 6.",
|
||||
)
|
||||
@click.option("-a", "--password", help="Password to use when connecting to the server.")
|
||||
@click.option("--url", default=None, envvar="IREDIS_URL", help=URL_HELP)
|
||||
|
@ -271,6 +273,14 @@ PAGER_HELP = """Using pager when output is too tall for your window, default to
|
|||
@click.option("--rainbow/--no-rainbow", default=None, is_flag=True, help=RAINBOW)
|
||||
@click.option("--shell/--no-shell", default=None, is_flag=True, help=SHELL)
|
||||
@click.option("--pager/--no-pager", default=None, is_flag=True, help=PAGER_HELP)
|
||||
@click.option(
|
||||
"--prompt",
|
||||
default=None,
|
||||
help=(
|
||||
"Prompt format (supported interpolations: {client_name}, {db}, {host}, {path},"
|
||||
" {port}, {username}, {client_addr}, {client_id})."
|
||||
),
|
||||
)
|
||||
@click.version_option()
|
||||
@click.argument("cmd", nargs=-1)
|
||||
def gather_args(
|
||||
|
@ -278,6 +288,7 @@ def gather_args(
|
|||
h,
|
||||
p,
|
||||
n,
|
||||
username,
|
||||
password,
|
||||
client_name,
|
||||
newbie,
|
||||
|
@ -291,6 +302,7 @@ def gather_args(
|
|||
socket,
|
||||
shell,
|
||||
pager,
|
||||
prompt,
|
||||
):
|
||||
"""
|
||||
IRedis: Interactive Redis
|
||||
|
@ -311,9 +323,9 @@ def gather_args(
|
|||
load_config_files(iredisrc)
|
||||
setup_log()
|
||||
logger.info(
|
||||
f"[commandline args] host={h}, port={p}, db={n}, newbie={newbie}, "
|
||||
f"iredisrc={iredisrc}, decode={decode}, raw={raw}, "
|
||||
f"cmd={cmd}, rainbow={rainbow}."
|
||||
f"[commandline args] host={h}, port={p}, db={n}, user={username},"
|
||||
f" newbie={newbie}, iredisrc={iredisrc}, decode={decode}, raw={raw}, cmd={cmd},"
|
||||
f" rainbow={rainbow}."
|
||||
)
|
||||
# raw config
|
||||
if raw is not None:
|
||||
|
@ -368,8 +380,10 @@ def create_client(params):
|
|||
host = params["h"]
|
||||
port = params["p"]
|
||||
db = params["n"]
|
||||
username = params["username"]
|
||||
password = params["password"]
|
||||
client_name = params["client_name"]
|
||||
prompt = params["prompt"]
|
||||
|
||||
dsn_from_url = None
|
||||
dsn = params["dsn"]
|
||||
|
@ -390,17 +404,26 @@ def create_client(params):
|
|||
scheme=dsn_from_url.scheme,
|
||||
username=dsn_from_url.username,
|
||||
client_name=client_name,
|
||||
prompt=prompt,
|
||||
)
|
||||
if params["socket"]:
|
||||
return Client(
|
||||
scheme="unix",
|
||||
path=params["socket"],
|
||||
db=db,
|
||||
username=username,
|
||||
password=password,
|
||||
client_name=client_name,
|
||||
prompt=prompt,
|
||||
)
|
||||
return Client(
|
||||
host=host, port=port, db=db, password=password, client_name=client_name
|
||||
host=host,
|
||||
port=port,
|
||||
db=db,
|
||||
username=username,
|
||||
password=password,
|
||||
client_name=client_name,
|
||||
prompt=prompt,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ logger = logging.getLogger(__name__)
|
|||
CONST = {
|
||||
"failoverchoice": "TAKEOVER FORCE",
|
||||
"withscores": "WITHSCORES",
|
||||
"withvalues_const": "WITHVALUES",
|
||||
"limit": "LIMIT",
|
||||
"expiration": "EX PX",
|
||||
"exat_const": "EXAT",
|
||||
|
@ -362,6 +363,7 @@ TIMEOUT_CONST = rf"(?P<timeout_const>{c('timeout_const')})"
|
|||
ABORT_CONST = rf"(?P<abort_const>{c('abort_const')})"
|
||||
PXAT_CONST = rf"(?P<pxat_const>{c('pxat_const')})"
|
||||
EXAT_CONST = rf"(?P<exat_const>{c('exat_const')})"
|
||||
WITHVALUES_CONST = rf"(?P<withvalues_const>{c('withvalues_const')})"
|
||||
|
||||
command_grammar = compile(COMMAND)
|
||||
|
||||
|
@ -660,6 +662,10 @@ GRAMMAR = {
|
|||
(\s+ {EXAT_CONST} \s+ {TIMESTAMP})
|
||||
)?
|
||||
\s*""",
|
||||
"command_key_count_withvalues": rf"""
|
||||
\s+ {KEY}
|
||||
(\s+ {COUNT} (\s+ {WITHVALUES_CONST})?)?
|
||||
\s*""",
|
||||
}
|
||||
|
||||
pipeline = r"(?P<shellcommand>\|.*)?"
|
||||
|
|
20
poetry.lock
generated
20
poetry.lock
generated
|
@ -30,7 +30,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
|||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
@ -89,7 +89,7 @@ python-versions = "*"
|
|||
|
||||
[[package]]
|
||||
name = "mistune"
|
||||
version = "2.0.2"
|
||||
version = "2.0.3"
|
||||
description = "A sane Markdown parser with useful plugins and renderers"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
@ -173,11 +173,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
|||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.11.2"
|
||||
version = "2.12.0"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
|
@ -305,8 +305,8 @@ click = [
|
|||
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
|
||||
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
|
||||
]
|
||||
configobj = [
|
||||
{file = "configobj-5.0.6.tar.gz", hash = "sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902"},
|
||||
|
@ -324,8 +324,8 @@ iniconfig = [
|
|||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||
]
|
||||
mistune = [
|
||||
{file = "mistune-2.0.2-py2.py3-none-any.whl", hash = "sha256:6bab6c6abd711c4604206c7d8cad5cd48b28f072b4bb75797d74146ba393a049"},
|
||||
{file = "mistune-2.0.2.tar.gz", hash = "sha256:6fc88c3cb49dba8b16687b41725e661cf85784c12e8974a29b9d336dd596c3a1"},
|
||||
{file = "mistune-2.0.3-py2.py3-none-any.whl", hash = "sha256:e3964140c0775535fba50bd616fe180920044a64bc21850253267b07bff89924"},
|
||||
{file = "mistune-2.0.3.tar.gz", hash = "sha256:d7605b46b6156b53b7d52a465202b29a6f00f4ea4130ad5d25e9d5547d6b7e50"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||
|
@ -375,8 +375,8 @@ py = [
|
|||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
pygments = [
|
||||
{file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"},
|
||||
{file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"},
|
||||
{file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"},
|
||||
{file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "iredis"
|
||||
version = "1.12.0"
|
||||
version = "1.12.1"
|
||||
description = "Terminal client for Redis with auto-completion and syntax highlighting."
|
||||
authors = ["laixintao <laixintao1995@163.com>"]
|
||||
readme = 'README.md'
|
||||
|
|
|
@ -81,3 +81,11 @@ def test_connect_via_socket(fake_redis_socket):
|
|||
c.expect("redis /tmp/test.sock")
|
||||
|
||||
c.close()
|
||||
|
||||
|
||||
def test_iredis_start_with_prompt():
|
||||
cli = pexpect.spawn("iredis --prompt '{host}abc{port}def{client_name}'", timeout=2)
|
||||
cli.logfile_read = open("cli_test.log", "ab")
|
||||
cli.expect("iredis")
|
||||
cli.expect("127.0.0.1abc6379defNone")
|
||||
cli.close()
|
||||
|
|
|
@ -23,3 +23,37 @@ def test_log_location_config():
|
|||
content = logfile.read()
|
||||
|
||||
assert len(content) > 100
|
||||
|
||||
|
||||
def test_load_prompt_from_config(iredis_client, clean_redis):
|
||||
config_content = dedent(
|
||||
"""
|
||||
[main]
|
||||
prompt = {host}abc{port}xx{db}
|
||||
"""
|
||||
)
|
||||
with open("/tmp/iredisrc", "w+") as etc_config:
|
||||
etc_config.write(config_content)
|
||||
|
||||
cli = pexpect.spawn("iredis -n 15 --iredisrc /tmp/iredisrc", timeout=1)
|
||||
cli.expect("iredis")
|
||||
cli.expect("127.0.0.1abc6379xx15")
|
||||
cli.close()
|
||||
|
||||
|
||||
def test_prompt_cli_overwrite_config(iredis_client, clean_redis):
|
||||
config_content = dedent(
|
||||
"""
|
||||
[main]
|
||||
prompt = {host}abc{port}xx{db}
|
||||
"""
|
||||
)
|
||||
with open("/tmp/iredisrc", "w+") as etc_config:
|
||||
etc_config.write(config_content)
|
||||
|
||||
cli = pexpect.spawn(
|
||||
"iredis -n 15 --iredisrc /tmp/iredisrc --prompt='{db}-12345'", timeout=1
|
||||
)
|
||||
cli.expect("iredis")
|
||||
cli.expect("15-12345")
|
||||
cli.close()
|
||||
|
|
|
@ -43,3 +43,24 @@ def test_hset(judge_command):
|
|||
"HSET foo bar hello",
|
||||
{"command": "HSET", "key": "foo", "field": "bar", "value": "hello"},
|
||||
)
|
||||
|
||||
|
||||
def test_hrandfield(judge_command):
|
||||
judge_command(
|
||||
"HRANDFIELD coin",
|
||||
{"command": "HRANDFIELD", "key": "coin"},
|
||||
)
|
||||
judge_command(
|
||||
"HRANDFIELD coin -5 WITHVALUES",
|
||||
{
|
||||
"command": "HRANDFIELD",
|
||||
"key": "coin",
|
||||
"count": "-5",
|
||||
"withvalues_const": "WITHVALUES",
|
||||
},
|
||||
)
|
||||
judge_command(
|
||||
"HRANDFIELD coin -5",
|
||||
{"command": "HRANDFIELD", "key": "coin", "count": "-5"},
|
||||
)
|
||||
judge_command("HRANDFIELD coin WITHVALUES", None)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
import re
|
||||
import pytest
|
||||
import redis
|
||||
|
@ -20,6 +21,11 @@ def completer():
|
|||
return IRedisCompleter()
|
||||
|
||||
|
||||
zset_type = "ziplist"
|
||||
if os.environ["REDIS_VERSION"] == "7":
|
||||
zset_type = "listpack"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"_input, command_name, expect_args",
|
||||
[
|
||||
|
@ -169,7 +175,16 @@ def test_not_retry_on_authentication_error(iredis_client, config):
|
|||
iredis_client.execute("None", "GET", ["foo"])
|
||||
|
||||
|
||||
@pytest.mark.skipif("int(os.environ['REDIS_VERSION']) < 6")
|
||||
@pytest.mark.skipif(
|
||||
"int(os.environ['REDIS_VERSION']) != 6",
|
||||
reason="""
|
||||
in redis7, it will not work if you:
|
||||
1. connect redis without password
|
||||
2. set a password
|
||||
3. auth
|
||||
|
||||
the auth will fail""",
|
||||
)
|
||||
def test_auto_select_db_and_auth_for_reconnect_only_6(iredis_client, config):
|
||||
config.retry_times = 2
|
||||
config.raw = True
|
||||
|
@ -256,6 +271,13 @@ def test_peek_key_not_exist(iredis_client, clean_redis, config):
|
|||
assert peek_result == ["non-exist-key doesn't exist."]
|
||||
|
||||
|
||||
def test_iredis_with_username():
|
||||
with patch("redis.connection.Connection.connect"):
|
||||
c = Client("127.0.0.1", "6379", username="abc", password="abc1")
|
||||
assert c.connection.username == "abc"
|
||||
assert c.connection.password == "abc1"
|
||||
|
||||
|
||||
def test_peek_string(iredis_client, clean_redis):
|
||||
clean_redis.set("foo", "bar")
|
||||
peek_result = list(iredis_client.do_peek("foo"))
|
||||
|
@ -337,12 +359,13 @@ def test_peek_zset_fetch_all(iredis_client, clean_redis):
|
|||
"myzset", dict(zip([f"hello-{index}" for index in range(3)], range(3)))
|
||||
)
|
||||
peek_result = list(iredis_client.do_peek("myzset"))
|
||||
|
||||
formatted_text_rematch(
|
||||
peek_result[0][0:9],
|
||||
FormattedText(
|
||||
[
|
||||
("class:dockey", "key: "),
|
||||
("", r"zset \(ziplist\) mem: \d+ bytes, ttl: -1"),
|
||||
("", rf"zset \({zset_type}\) mem: \d+ bytes, ttl: -1"),
|
||||
("", "\n"),
|
||||
("class:dockey", "zcount: "),
|
||||
("", "3"),
|
||||
|
@ -365,7 +388,7 @@ def test_peek_zset_fetch_part(iredis_client, clean_redis):
|
|||
FormattedText(
|
||||
[
|
||||
("class:dockey", "key: "),
|
||||
("", r"zset \(ziplist\) mem: \d+ bytes, ttl: -1"),
|
||||
("", rf"zset \({zset_type}\) mem: \d+ bytes, ttl: -1"),
|
||||
("", "\n"),
|
||||
("class:dockey", "zcount: "),
|
||||
("", "40"),
|
||||
|
@ -527,29 +550,23 @@ def test_version_parse_for_auth(iredis_client):
|
|||
"info, version",
|
||||
[
|
||||
(
|
||||
(
|
||||
"# Server\r\nredis_version:df--128-NOTFOUND\r\n"
|
||||
"redis_mode:standalone\r\narch_bits:64"
|
||||
),
|
||||
"# Server\r\nredis_version:df--128-NOTFOUND\r\n"
|
||||
"redis_mode:standalone\r\narch_bits:64",
|
||||
"df--128-NOTFOUND",
|
||||
),
|
||||
(
|
||||
(
|
||||
"# Server\r\nredis_version:6.2.5\r\n"
|
||||
"redis_git_sha1:00000000\r\n"
|
||||
"redis_git_dirty:0\r\n"
|
||||
"redis_build_id:915e5480613bc9b6\r\n"
|
||||
"redis_mode:standalone "
|
||||
),
|
||||
"# Server\r\nredis_version:6.2.5\r\n"
|
||||
"redis_git_sha1:00000000\r\n"
|
||||
"redis_git_dirty:0\r\n"
|
||||
"redis_build_id:915e5480613bc9b6\r\n"
|
||||
"redis_mode:standalone ",
|
||||
"6.2.5",
|
||||
),
|
||||
(
|
||||
(
|
||||
"# Server\r\nredis_version:5.0.14.1\r\n"
|
||||
"redis_git_sha1:00000000\r\nredis_git_dirty:0\r\n"
|
||||
"redis_build_id:915e5480613bc9b6\r\n"
|
||||
"redis_mode:standalone "
|
||||
),
|
||||
"# Server\r\nredis_version:5.0.14.1\r\n"
|
||||
"redis_git_sha1:00000000\r\nredis_git_dirty:0\r\n"
|
||||
"redis_build_id:915e5480613bc9b6\r\n"
|
||||
"redis_mode:standalone ",
|
||||
"5.0.14.1",
|
||||
),
|
||||
],
|
||||
|
@ -564,3 +581,22 @@ def test_version_path(info, version):
|
|||
client = Client("127.0.0.1", "6379", None)
|
||||
client.get_server_info()
|
||||
assert mock_config.version == version
|
||||
|
||||
|
||||
def test_prompt():
|
||||
c = Client()
|
||||
assert str(c) == "127.0.0.1:6379> "
|
||||
|
||||
c = Client(prompt="{host} {port} {db}")
|
||||
assert str(c) == "127.0.0.1 6379 0"
|
||||
|
||||
c = Client(prompt="{host} {port} {db} {username}")
|
||||
assert str(c) == "127.0.0.1 6379 0 None"
|
||||
|
||||
c = Client(prompt="{host} {port} {db} {username}", username="foo1")
|
||||
assert str(c) == "127.0.0.1 6379 0 foo1"
|
||||
|
||||
c = Client(prompt="{client_id} aabc")
|
||||
assert re.match(r"^\d+ aabc$", str(c))
|
||||
c = Client(prompt="{client_addr} >")
|
||||
assert re.match(r"^127.0.0.1:\d+ >$", str(c))
|
||||
|
|
Loading…
Add table
Reference in a new issue