Skip to content

Commit f258022

Browse files
authored
Merge branch 'master' into master
2 parents 91e96b1 + cf725d4 commit f258022

File tree

9 files changed

+273
-67
lines changed

9 files changed

+273
-67
lines changed

.github/actions/run-tests/action.yml

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ runs:
4646
CLIENT_LIBS_TEST_IMAGE_TAG: ${{ inputs.redis-version }}
4747
run: |
4848
set -e
49-
49+
5050
echo "::group::Installing dependencies"
5151
pip install -r dev_requirements.txt
5252
pip uninstall -y redis # uninstall Redis package installed via redis-entraid
@@ -57,80 +57,79 @@ runs:
5757
pip install -e ./hiredis-py
5858
else
5959
pip install "hiredis${{inputs.hiredis-version}}"
60-
fi
60+
fi
6161
echo "PARSER_BACKEND=$(echo "${{inputs.parser-backend}}_${{inputs.hiredis-version}}" | sed 's/[^a-zA-Z0-9]/_/g')" >> $GITHUB_ENV
6262
else
6363
echo "PARSER_BACKEND=${{inputs.parser-backend}}" >> $GITHUB_ENV
6464
fi
6565
echo "::endgroup::"
66-
66+
6767
echo "::group::Starting Redis servers"
6868
redis_major_version=$(echo "$REDIS_VERSION" | grep -oP '^\d+')
6969
echo "REDIS_MAJOR_VERSION=${redis_major_version}" >> $GITHUB_ENV
70-
70+
7171
if (( redis_major_version < 8 )); then
7272
echo "Using redis-stack for module tests"
73-
74-
# Mapping of redis version to stack version
73+
74+
# Mapping of redis version to stack version
7575
declare -A redis_stack_version_mapping=(
7676
["7.4.2"]="rs-7.4.0-v2"
7777
["7.2.7"]="rs-7.2.0-v14"
78-
["6.2.17"]="rs-6.2.6-v18"
7978
)
80-
79+
8180
if [[ -v redis_stack_version_mapping[$REDIS_VERSION] ]]; then
8281
export CLIENT_LIBS_TEST_STACK_IMAGE_TAG=${redis_stack_version_mapping[$REDIS_VERSION]}
8382
echo "REDIS_MOD_URL=redis://127.0.0.1:6479/0" >> $GITHUB_ENV
8483
else
8584
echo "Version not found in the mapping."
8685
exit 1
8786
fi
88-
87+
8988
if (( redis_major_version < 7 )); then
9089
export REDIS_STACK_EXTRA_ARGS="--tls-auth-clients optional --save ''"
91-
export REDIS_EXTRA_ARGS="--tls-auth-clients optional --save ''"
90+
export REDIS_EXTRA_ARGS="--tls-auth-clients optional --save ''"
9291
fi
93-
92+
9493
invoke devenv --endpoints=all-stack
9594
else
9695
echo "Using redis CE for module tests"
9796
echo "REDIS_MOD_URL=redis://127.0.0.1:6379" >> $GITHUB_ENV
9897
invoke devenv --endpoints all
99-
fi
100-
98+
fi
99+
101100
sleep 10 # time to settle
102101
echo "::endgroup::"
103102
shell: bash
104103

105104
- name: Run tests
106105
run: |
107106
set -e
108-
107+
109108
run_tests() {
110109
local protocol=$1
111110
local eventloop=""
112-
111+
113112
if [ "${{inputs.event-loop}}" == "uvloop" ]; then
114113
eventloop="--uvloop"
115114
fi
116-
115+
117116
echo "::group::RESP${protocol} standalone tests"
118117
echo "REDIS_MOD_URL=${REDIS_MOD_URL}"
119-
118+
120119
if (( $REDIS_MAJOR_VERSION < 7 )) && [ "$protocol" == "3" ]; then
121120
echo "Skipping module tests: Modules doesn't support RESP3 for Redis versions < 7"
122121
invoke standalone-tests --redis-mod-url=${REDIS_MOD_URL} $eventloop --protocol="${protocol}" --extra-markers="not redismod and not cp_integration"
123-
else
122+
else
124123
invoke standalone-tests --redis-mod-url=${REDIS_MOD_URL} $eventloop --protocol="${protocol}"
125124
fi
126-
125+
127126
echo "::endgroup::"
128-
127+
129128
echo "::group::RESP${protocol} cluster tests"
130129
invoke cluster-tests $eventloop --protocol=${protocol}
131-
echo "::endgroup::"
130+
echo "::endgroup::"
132131
}
133-
132+
134133
run_tests 2 "${{inputs.event-loop}}"
135134
run_tests 3 "${{inputs.event-loop}}"
136135
shell: bash

.github/workflows/integration.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474
max-parallel: 15
7575
fail-fast: false
7676
matrix:
77-
redis-version: ['8.0.1-pre', '${{ needs.redis_version.outputs.CURRENT }}', '7.2.7', '6.2.17']
77+
redis-version: ['8.0.1-pre', '${{ needs.redis_version.outputs.CURRENT }}', '7.2.7']
7878
python-version: ['3.9', '3.13']
7979
parser-backend: ['plain']
8080
event-loop: ['asyncio']

.github/workflows/spellcheck.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
- name: Checkout
99
uses: actions/checkout@v4
1010
- name: Check Spelling
11-
uses: rojopolis/spellcheck-github-actions@0.48.0
11+
uses: rojopolis/spellcheck-github-actions@0.49.0
1212
with:
1313
config_path: .github/spellcheck-settings.yml
1414
task_name: Markdown

redis/asyncio/sentinel.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -223,19 +223,31 @@ async def execute_command(self, *args, **kwargs):
223223
once - If set to True, then execute the resulting command on a single
224224
node at random, rather than across the entire sentinel cluster.
225225
"""
226-
once = bool(kwargs.get("once", False))
227-
if "once" in kwargs.keys():
228-
kwargs.pop("once")
226+
once = bool(kwargs.pop("once", False))
227+
228+
# Check if command is supposed to return the original
229+
# responses instead of boolean value.
230+
return_responses = bool(kwargs.pop("return_responses", False))
229231

230232
if once:
231-
await random.choice(self.sentinels).execute_command(*args, **kwargs)
232-
else:
233-
tasks = [
234-
asyncio.Task(sentinel.execute_command(*args, **kwargs))
235-
for sentinel in self.sentinels
236-
]
237-
await asyncio.gather(*tasks)
238-
return True
233+
response = await random.choice(self.sentinels).execute_command(
234+
*args, **kwargs
235+
)
236+
if return_responses:
237+
return [response]
238+
else:
239+
return True if response else False
240+
241+
tasks = [
242+
asyncio.Task(sentinel.execute_command(*args, **kwargs))
243+
for sentinel in self.sentinels
244+
]
245+
responses = await asyncio.gather(*tasks)
246+
247+
if return_responses:
248+
return responses
249+
250+
return all(responses)
239251

240252
def __repr__(self):
241253
sentinel_addresses = []

redis/cluster.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,6 @@ def pipeline(self, transaction=None, shard_hint=None):
856856
startup_nodes=self.nodes_manager.startup_nodes,
857857
result_callbacks=self.result_callbacks,
858858
cluster_response_callbacks=self.cluster_response_callbacks,
859-
cluster_error_retry_attempts=self.retry.get_retries(),
860859
read_from_replicas=self.read_from_replicas,
861860
load_balancing_strategy=self.load_balancing_strategy,
862861
reinitialize_steps=self.reinitialize_steps,

redis/commands/sentinel.py

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,35 @@ def sentinel(self, *args):
1111
"""Redis Sentinel's SENTINEL command."""
1212
warnings.warn(DeprecationWarning("Use the individual sentinel_* methods"))
1313

14-
def sentinel_get_master_addr_by_name(self, service_name):
15-
"""Returns a (host, port) pair for the given ``service_name``"""
16-
return self.execute_command("SENTINEL GET-MASTER-ADDR-BY-NAME", service_name)
17-
18-
def sentinel_master(self, service_name):
19-
"""Returns a dictionary containing the specified masters state."""
20-
return self.execute_command("SENTINEL MASTER", service_name)
14+
def sentinel_get_master_addr_by_name(self, service_name, return_responses=False):
15+
"""
16+
Returns a (host, port) pair for the given ``service_name`` when return_responses is True,
17+
otherwise returns a boolean value that indicates if the command was successful.
18+
"""
19+
return self.execute_command(
20+
"SENTINEL GET-MASTER-ADDR-BY-NAME",
21+
service_name,
22+
once=True,
23+
return_responses=return_responses,
24+
)
25+
26+
def sentinel_master(self, service_name, return_responses=False):
27+
"""
28+
Returns a dictionary containing the specified masters state, when return_responses is True,
29+
otherwise returns a boolean value that indicates if the command was successful.
30+
"""
31+
return self.execute_command(
32+
"SENTINEL MASTER", service_name, return_responses=return_responses
33+
)
2134

2235
def sentinel_masters(self):
23-
"""Returns a list of dictionaries containing each master's state."""
36+
"""
37+
Returns a list of dictionaries containing each master's state.
38+
39+
Important: This function is called by the Sentinel implementation and is
40+
called directly on the Redis standalone client for sentinels,
41+
so it doesn't support the "once" and "return_responses" options.
42+
"""
2443
return self.execute_command("SENTINEL MASTERS")
2544

2645
def sentinel_monitor(self, name, ip, port, quorum):
@@ -31,16 +50,27 @@ def sentinel_remove(self, name):
3150
"""Remove a master from Sentinel's monitoring"""
3251
return self.execute_command("SENTINEL REMOVE", name)
3352

34-
def sentinel_sentinels(self, service_name):
35-
"""Returns a list of sentinels for ``service_name``"""
36-
return self.execute_command("SENTINEL SENTINELS", service_name)
53+
def sentinel_sentinels(self, service_name, return_responses=False):
54+
"""
55+
Returns a list of sentinels for ``service_name``, when return_responses is True,
56+
otherwise returns a boolean value that indicates if the command was successful.
57+
"""
58+
return self.execute_command(
59+
"SENTINEL SENTINELS", service_name, return_responses=return_responses
60+
)
3761

3862
def sentinel_set(self, name, option, value):
3963
"""Set Sentinel monitoring parameters for a given master"""
4064
return self.execute_command("SENTINEL SET", name, option, value)
4165

4266
def sentinel_slaves(self, service_name):
43-
"""Returns a list of slaves for ``service_name``"""
67+
"""
68+
Returns a list of slaves for ``service_name``
69+
70+
Important: This function is called by the Sentinel implementation and is
71+
called directly on the Redis standalone client for sentinels,
72+
so it doesn't support the "once" and "return_responses" options.
73+
"""
4474
return self.execute_command("SENTINEL SLAVES", service_name)
4575

4676
def sentinel_reset(self, pattern):

redis/sentinel.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,16 +254,27 @@ def execute_command(self, *args, **kwargs):
254254
once - If set to True, then execute the resulting command on a single
255255
node at random, rather than across the entire sentinel cluster.
256256
"""
257-
once = bool(kwargs.get("once", False))
258-
if "once" in kwargs.keys():
259-
kwargs.pop("once")
257+
once = bool(kwargs.pop("once", False))
258+
259+
# Check if command is supposed to return the original
260+
# responses instead of boolean value.
261+
return_responses = bool(kwargs.pop("return_responses", False))
260262

261263
if once:
262-
random.choice(self.sentinels).execute_command(*args, **kwargs)
263-
else:
264-
for sentinel in self.sentinels:
265-
sentinel.execute_command(*args, **kwargs)
266-
return True
264+
response = random.choice(self.sentinels).execute_command(*args, **kwargs)
265+
if return_responses:
266+
return [response]
267+
else:
268+
return True if response else False
269+
270+
responses = []
271+
for sentinel in self.sentinels:
272+
responses.append(sentinel.execute_command(*args, **kwargs))
273+
274+
if return_responses:
275+
return responses
276+
277+
return all(responses)
267278

268279
def __repr__(self):
269280
sentinel_addresses = []

0 commit comments

Comments
 (0)