1"""Response-callback dictionaries and the protocol/legacy selector.
2
3This module is the single source of truth for the mapping between Redis
4command names and the Python-side callbacks that post-process raw parser
5output into user-facing values.
6
7Six dictionaries are defined:
8
9* ``_RedisCallbacks`` — entries that produce the same Python value
10 regardless of wire protocol or legacy-response selection.
11* ``_RedisCallbacksRESP2`` — RESP2 wire, legacy Python shapes.
12* ``_RedisCallbacksRESP3`` — RESP3 wire, Python shapes from the previous
13 RESP3 callbacks.
14* ``_RedisCallbacksRESP2Unified`` — RESP2 wire, unified Python shapes
15 (``legacy_responses=False``).
16* ``_RedisCallbacksRESP3Unified`` — RESP3 wire, unified Python shapes
17 (``legacy_responses=False``).
18* ``_RedisCallbacksRESP3toRESP2Legacy`` — RESP3 wire converted back to the
19 legacy RESP2 Python shapes for users who keep ``legacy_responses=True``
20 on a RESP3 connection.
21
22``get_response_callbacks`` merges ``_RedisCallbacks`` with the appropriate
23protocol-specific overlay. Callers wrap the returned dict in
24``CaseInsensitiveDict``.
25"""
26
27from typing import Any, Callable, Optional
28
29from redis.utils import str_if_bytes
30
31from .helpers import (
32 bool_ok,
33 bzpop_score_resp3_to_resp2_legacy,
34 bzpop_score_unified,
35 float_or_none,
36 hrandfield_resp3_to_resp2_legacy,
37 hrandfield_unified,
38 pairs_to_dict,
39 parse_acl_getuser,
40 parse_acl_getuser_resp3_to_resp2_legacy,
41 parse_acl_getuser_unified,
42 parse_acl_log,
43 parse_acl_log_resp3_to_resp2_legacy,
44 parse_acl_log_resp3_unified,
45 parse_arinfo,
46 parse_client_info,
47 parse_client_kill,
48 parse_client_list,
49 parse_client_trackinginfo_resp3_to_resp2_legacy,
50 parse_client_trackinginfo_unified,
51 parse_cluster_info,
52 parse_cluster_links_resp3_to_resp2_legacy,
53 parse_cluster_links_unified,
54 parse_cluster_nodes,
55 parse_command,
56 parse_command_resp3,
57 parse_command_unified,
58 parse_config_get,
59 parse_config_get_resp3_to_resp2_legacy,
60 parse_debug_object,
61 parse_function_list_resp3_to_resp2_legacy,
62 parse_function_list_unified,
63 parse_geopos_resp3_to_resp2_legacy,
64 parse_geopos_unified,
65 parse_geosearch_generic,
66 parse_geosearch_generic_unified,
67 parse_hscan,
68 parse_info,
69 parse_lcs_idx_resp3_to_resp2_legacy,
70 parse_lcs_idx_unified,
71 parse_list_of_dicts,
72 parse_list_of_dicts_resp3,
73 parse_memory_stats,
74 parse_memory_stats_resp3,
75 parse_memory_stats_unified,
76 parse_pubsub_numsub,
77 parse_scan,
78 parse_sentinel_get_master,
79 parse_sentinel_master,
80 parse_sentinel_master_resp3_to_resp2_legacy,
81 parse_sentinel_master_unified,
82 parse_sentinel_master_unified_resp3,
83 parse_sentinel_masters,
84 parse_sentinel_masters_resp3,
85 parse_sentinel_masters_resp3_to_resp2_legacy,
86 parse_sentinel_masters_unified,
87 parse_sentinel_masters_unified_resp3,
88 parse_sentinel_slaves_and_sentinels,
89 parse_sentinel_slaves_and_sentinels_resp3,
90 parse_sentinel_slaves_and_sentinels_resp3_to_resp2_legacy,
91 parse_sentinel_slaves_and_sentinels_unified,
92 parse_sentinel_slaves_and_sentinels_unified_resp3,
93 parse_sentinel_state_resp3,
94 parse_set_result,
95 parse_slowlog_get,
96 parse_stralgo,
97 parse_stralgo_resp3_unified,
98 parse_stralgo_unified,
99 parse_stream_list,
100 parse_xautoclaim,
101 parse_xclaim,
102 parse_xinfo_stream,
103 parse_xpending,
104 parse_xread,
105 parse_xread_resp3,
106 parse_xread_resp3_to_resp2_legacy,
107 parse_xread_unified,
108 parse_zadd,
109 parse_zmscore,
110 parse_zscan,
111 parse_zscan_unified,
112 sort_return_tuples,
113 string_keys_to_dict,
114 timestamp_to_datetime,
115 zmpop_resp3_to_resp2_legacy,
116 zmpop_unified,
117 zpop_score_pairs,
118 zpop_score_pairs_resp3_to_resp2_legacy,
119 zpop_score_pairs_resp3_unified,
120 zpop_score_pairs_unified,
121 zset_score_for_rank,
122 zset_score_for_rank_resp3,
123 zset_score_for_rank_resp3_to_resp2_legacy,
124 zset_score_for_rank_unified,
125 zset_score_pairs,
126 zset_score_pairs_resp3,
127 zset_score_pairs_resp3_to_resp2_legacy,
128 zset_score_pairs_resp3_to_resp2_legacy_flat,
129 zset_score_pairs_unified,
130)
131
132_RedisCallbacks = {
133 **string_keys_to_dict(
134 "AUTH COPY EXPIRE EXPIREAT HEXISTS HMSET MOVE MSETNX PERSIST PSETEX "
135 "PEXPIRE PEXPIREAT RENAMENX SETEX SETNX SMOVE",
136 bool,
137 ),
138 **string_keys_to_dict("HINCRBYFLOAT INCRBYFLOAT", float),
139 **string_keys_to_dict(
140 "ASKING FLUSHALL FLUSHDB LSET LTRIM MSET PFMERGE READONLY READWRITE "
141 "RENAME SAVE SELECT SHUTDOWN SLAVEOF SWAPDB WATCH UNWATCH",
142 bool_ok,
143 ),
144 **string_keys_to_dict("XREAD XREADGROUP", parse_xread),
145 **string_keys_to_dict(
146 "GEORADIUS GEORADIUSBYMEMBER GEOSEARCH",
147 parse_geosearch_generic,
148 ),
149 **string_keys_to_dict("XRANGE XREVRANGE", parse_stream_list),
150 "ACL GETUSER": parse_acl_getuser,
151 "ACL LOAD": bool_ok,
152 "ACL LOG": parse_acl_log,
153 "ACL SETUSER": bool_ok,
154 "ACL SAVE": bool_ok,
155 "CLIENT INFO": parse_client_info,
156 "CLIENT KILL": parse_client_kill,
157 "CLIENT LIST": parse_client_list,
158 "CLIENT PAUSE": bool_ok,
159 "CLIENT SETINFO": bool_ok,
160 "CLIENT SETNAME": bool_ok,
161 "CLIENT UNBLOCK": bool,
162 "CLUSTER ADDSLOTS": bool_ok,
163 "CLUSTER ADDSLOTSRANGE": bool_ok,
164 "CLUSTER DELSLOTS": bool_ok,
165 "CLUSTER DELSLOTSRANGE": bool_ok,
166 "CLUSTER FAILOVER": bool_ok,
167 "CLUSTER FORGET": bool_ok,
168 "CLUSTER INFO": parse_cluster_info,
169 "CLUSTER MEET": bool_ok,
170 "CLUSTER NODES": parse_cluster_nodes,
171 "CLUSTER REPLICAS": parse_cluster_nodes,
172 "CLUSTER REPLICATE": bool_ok,
173 "CLUSTER RESET": bool_ok,
174 "CLUSTER SAVECONFIG": bool_ok,
175 "CLUSTER SET-CONFIG-EPOCH": bool_ok,
176 "CLUSTER SETSLOT": bool_ok,
177 "CLUSTER SLAVES": parse_cluster_nodes,
178 "COMMAND": parse_command,
179 "CONFIG RESETSTAT": bool_ok,
180 "CONFIG SET": bool_ok,
181 "FUNCTION DELETE": bool_ok,
182 "FUNCTION FLUSH": bool_ok,
183 "FUNCTION RESTORE": bool_ok,
184 "GEODIST": float_or_none,
185 "HSCAN": parse_hscan,
186 "INFO": parse_info,
187 "LASTSAVE": timestamp_to_datetime,
188 "MEMORY PURGE": bool_ok,
189 "MODULE LOAD": bool,
190 "MODULE UNLOAD": bool,
191 "PING": lambda r: str_if_bytes(r) == "PONG",
192 "PUBSUB NUMSUB": parse_pubsub_numsub,
193 "PUBSUB SHARDNUMSUB": parse_pubsub_numsub,
194 "QUIT": bool_ok,
195 "SET": parse_set_result,
196 "SCAN": parse_scan,
197 "SCRIPT EXISTS": lambda r: list(map(bool, r)),
198 "SCRIPT FLUSH": bool_ok,
199 "SCRIPT KILL": bool_ok,
200 "SCRIPT LOAD": str_if_bytes,
201 "SENTINEL CKQUORUM": bool_ok,
202 "SENTINEL FAILOVER": bool_ok,
203 "SENTINEL FLUSHCONFIG": bool_ok,
204 "SENTINEL GET-MASTER-ADDR-BY-NAME": parse_sentinel_get_master,
205 "SENTINEL MONITOR": bool_ok,
206 "SENTINEL RESET": bool_ok,
207 "SENTINEL REMOVE": bool_ok,
208 "SENTINEL SET": bool_ok,
209 "SLOWLOG GET": parse_slowlog_get,
210 "SLOWLOG RESET": bool_ok,
211 "SORT": sort_return_tuples,
212 "SSCAN": parse_scan,
213 "TIME": lambda x: (int(x[0]), int(x[1])),
214 "XAUTOCLAIM": parse_xautoclaim,
215 "XCLAIM": parse_xclaim,
216 "XGROUP CREATE": bool_ok,
217 "XGROUP DESTROY": bool,
218 "XGROUP SETID": bool_ok,
219 "ARINFO": parse_arinfo,
220 "XINFO STREAM": parse_xinfo_stream,
221 "XPENDING": parse_xpending,
222 "ZSCAN": parse_zscan,
223}
224
225
226_RedisCallbacksRESP2 = {
227 **string_keys_to_dict(
228 "SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
229 ),
230 **string_keys_to_dict(
231 "ZINTER ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE ZUNION",
232 zset_score_pairs,
233 ),
234 **string_keys_to_dict("ZPOPMAX ZPOPMIN", zpop_score_pairs),
235 **string_keys_to_dict(
236 "ZREVRANK ZRANK",
237 zset_score_for_rank,
238 ),
239 **string_keys_to_dict("ZINCRBY ZSCORE", float_or_none),
240 **string_keys_to_dict("BGREWRITEAOF BGSAVE", lambda r: True),
241 **string_keys_to_dict("BLPOP BRPOP", lambda r: r and tuple(r) or None),
242 **string_keys_to_dict(
243 "BZPOPMAX BZPOPMIN", lambda r: r and (r[0], r[1], float(r[2])) or None
244 ),
245 "ACL CAT": lambda r: list(map(str_if_bytes, r)),
246 "ACL GENPASS": str_if_bytes,
247 "ACL HELP": lambda r: list(map(str_if_bytes, r)),
248 "ACL LIST": lambda r: list(map(str_if_bytes, r)),
249 "ACL USERS": lambda r: list(map(str_if_bytes, r)),
250 "ACL WHOAMI": str_if_bytes,
251 "CLIENT GETNAME": str_if_bytes,
252 "CLIENT TRACKINGINFO": lambda r: list(map(str_if_bytes, r)),
253 "CLUSTER GETKEYSINSLOT": lambda r: list(map(str_if_bytes, r)),
254 "COMMAND GETKEYS": lambda r: list(map(str_if_bytes, r)),
255 "CONFIG GET": parse_config_get,
256 "DEBUG OBJECT": parse_debug_object,
257 "GEOHASH": lambda r: list(map(str_if_bytes, r)),
258 "GEOPOS": lambda r: list(
259 map(lambda ll: (float(ll[0]), float(ll[1])) if ll is not None else None, r)
260 ),
261 "HGETALL": lambda r: r and pairs_to_dict(r) or {},
262 "HOTKEYS GET": lambda r: [pairs_to_dict(m) for m in r],
263 "MEMORY STATS": parse_memory_stats,
264 "MODULE LIST": lambda r: [pairs_to_dict(m) for m in r],
265 "RESET": str_if_bytes,
266 "SENTINEL MASTER": parse_sentinel_master,
267 "SENTINEL MASTERS": parse_sentinel_masters,
268 "SENTINEL SENTINELS": parse_sentinel_slaves_and_sentinels,
269 "SENTINEL SLAVES": parse_sentinel_slaves_and_sentinels,
270 "STRALGO": parse_stralgo,
271 "XINFO CONSUMERS": parse_list_of_dicts,
272 "XINFO GROUPS": parse_list_of_dicts,
273 "ZADD": parse_zadd,
274 "ZMSCORE": parse_zmscore,
275}
276
277
278_RedisCallbacksRESP3 = {
279 **string_keys_to_dict(
280 "SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
281 ),
282 **string_keys_to_dict(
283 "ZRANGE ZINTER ZPOPMAX ZPOPMIN HGETALL XREADGROUP",
284 lambda r, **kwargs: r,
285 ),
286 **string_keys_to_dict(
287 "ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE ZUNION",
288 zset_score_pairs_resp3,
289 ),
290 **string_keys_to_dict(
291 "ZREVRANK ZRANK",
292 zset_score_for_rank_resp3,
293 ),
294 **string_keys_to_dict("XREAD XREADGROUP", parse_xread_resp3),
295 "ACL LOG": lambda r: (
296 [
297 {str_if_bytes(key): str_if_bytes(value) for key, value in x.items()}
298 for x in r
299 ]
300 if isinstance(r, list)
301 else bool_ok(r)
302 ),
303 "COMMAND": parse_command_resp3,
304 "CONFIG GET": parse_config_get_resp3_to_resp2_legacy,
305 "MEMORY STATS": parse_memory_stats_resp3,
306 "SENTINEL MASTER": parse_sentinel_state_resp3,
307 "SENTINEL MASTERS": parse_sentinel_masters_resp3,
308 "SENTINEL SENTINELS": parse_sentinel_slaves_and_sentinels_resp3,
309 "SENTINEL SLAVES": parse_sentinel_slaves_and_sentinels_resp3,
310 "STRALGO": lambda r, **options: (
311 {str_if_bytes(key): str_if_bytes(value) for key, value in r.items()}
312 if isinstance(r, dict)
313 else str_if_bytes(r)
314 ),
315 "XINFO CONSUMERS": parse_list_of_dicts_resp3,
316 "XINFO GROUPS": parse_list_of_dicts_resp3,
317}
318
319
320# RESP2 wire, unified response shapes (``legacy_responses=False``).
321_RedisCallbacksRESP2Unified: dict[str, Callable[..., Any]] = {
322 **_RedisCallbacksRESP2,
323 **string_keys_to_dict(
324 "ZDIFF ZINTER ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE ZUNION",
325 zset_score_pairs_unified,
326 ),
327 **string_keys_to_dict(
328 "ZPOPMAX ZPOPMIN",
329 zpop_score_pairs_unified,
330 ),
331 **string_keys_to_dict(
332 "ZREVRANK ZRANK",
333 zset_score_for_rank_unified,
334 ),
335 **string_keys_to_dict(
336 "BZPOPMAX BZPOPMIN",
337 bzpop_score_unified,
338 ),
339 **string_keys_to_dict("XREAD XREADGROUP", parse_xread_unified),
340 **string_keys_to_dict(
341 "GEORADIUS GEORADIUSBYMEMBER GEOSEARCH",
342 parse_geosearch_generic_unified,
343 ),
344 **string_keys_to_dict("BLPOP BRPOP", lambda r: r or None),
345 **string_keys_to_dict("ZMPOP BZMPOP", zmpop_unified),
346 "ZSCAN": parse_zscan_unified,
347 "ZRANDMEMBER": zset_score_pairs_unified,
348 "HRANDFIELD": hrandfield_unified,
349 "ACL GETUSER": parse_acl_getuser_unified,
350 "CLIENT TRACKINGINFO": parse_client_trackinginfo_unified,
351 "CLUSTER GETKEYSINSLOT": lambda r, **kwargs: r,
352 "CLUSTER LINKS": parse_cluster_links_unified,
353 "COMMAND": parse_command_unified,
354 "COMMAND GETKEYS": lambda r, **kwargs: r,
355 "FUNCTION LIST": parse_function_list_unified,
356 "GEOPOS": parse_geopos_unified,
357 "LCS": parse_lcs_idx_unified,
358 "MEMORY STATS": parse_memory_stats_unified,
359 "SENTINEL MASTER": parse_sentinel_master_unified,
360 "SENTINEL MASTERS": parse_sentinel_masters_unified,
361 "SENTINEL SENTINELS": parse_sentinel_slaves_and_sentinels_unified,
362 "SENTINEL SLAVES": parse_sentinel_slaves_and_sentinels_unified,
363 "STRALGO": parse_stralgo_unified,
364}
365
366
367# RESP3 wire, unified response shapes (``legacy_responses=False``).
368_RedisCallbacksRESP3Unified: dict[str, Callable[..., Any]] = {
369 **_RedisCallbacksRESP3,
370 **string_keys_to_dict(
371 "ZDIFF ZINTER ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE ZUNION",
372 zset_score_pairs_resp3,
373 ),
374 **string_keys_to_dict(
375 "ZPOPMAX ZPOPMIN",
376 zpop_score_pairs_resp3_unified,
377 ),
378 **string_keys_to_dict(
379 "BZPOPMAX BZPOPMIN",
380 bzpop_score_unified,
381 ),
382 **string_keys_to_dict("XREAD XREADGROUP", parse_xread_unified),
383 **string_keys_to_dict(
384 "GEORADIUS GEORADIUSBYMEMBER GEOSEARCH",
385 parse_geosearch_generic_unified,
386 ),
387 "ZSCAN": parse_zscan_unified,
388 "ZRANDMEMBER": zset_score_pairs_resp3,
389 "ACL CAT": lambda r: list(map(str_if_bytes, r)),
390 "ACL GENPASS": str_if_bytes,
391 "ACL HELP": lambda r: list(map(str_if_bytes, r)),
392 "ACL LIST": lambda r: list(map(str_if_bytes, r)),
393 "ACL LOG": parse_acl_log_resp3_unified,
394 "ACL USERS": lambda r: list(map(str_if_bytes, r)),
395 "ACL WHOAMI": str_if_bytes,
396 "CLIENT GETNAME": str_if_bytes,
397 "CLIENT TRACKINGINFO": parse_client_trackinginfo_unified,
398 "CLUSTER LINKS": parse_cluster_links_unified,
399 "COMMAND": parse_command_unified,
400 "FUNCTION LIST": parse_function_list_unified,
401 "GEOHASH": lambda r: list(map(str_if_bytes, r)),
402 "LCS": parse_lcs_idx_unified,
403 "RESET": str_if_bytes,
404 "SENTINEL MASTER": parse_sentinel_master_unified_resp3,
405 "SENTINEL MASTERS": parse_sentinel_masters_unified_resp3,
406 "SENTINEL SENTINELS": parse_sentinel_slaves_and_sentinels_unified_resp3,
407 "SENTINEL SLAVES": parse_sentinel_slaves_and_sentinels_unified_resp3,
408 "STRALGO": parse_stralgo_resp3_unified,
409}
410
411
412# RESP3 wire converted back to the legacy RESP2 Python shapes. Only the
413# entries needed to undo RESP3-side differences are listed; everything
414# else falls through to ``_RedisCallbacks``. Scores are re-encoded to bytes
415# before being passed to ``score_cast_func`` so the callable observes the
416# same input type it would on a RESP2 connection.
417_RedisCallbacksRESP3toRESP2Legacy: dict[str, Callable[..., Any]] = {
418 **string_keys_to_dict(
419 "SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
420 ),
421 **string_keys_to_dict(
422 "ZINTER ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE ZUNION",
423 zset_score_pairs_resp3_to_resp2_legacy,
424 ),
425 "ZDIFF": zset_score_pairs_resp3_to_resp2_legacy_flat,
426 "ZRANDMEMBER": zset_score_pairs_resp3_to_resp2_legacy_flat,
427 **string_keys_to_dict(
428 "ZPOPMAX ZPOPMIN",
429 zpop_score_pairs_resp3_to_resp2_legacy,
430 ),
431 **string_keys_to_dict(
432 "BZPOPMAX BZPOPMIN",
433 bzpop_score_resp3_to_resp2_legacy,
434 ),
435 **string_keys_to_dict(
436 "ZREVRANK ZRANK",
437 zset_score_for_rank_resp3_to_resp2_legacy,
438 ),
439 **string_keys_to_dict("BGREWRITEAOF BGSAVE", lambda r: True),
440 **string_keys_to_dict("XREAD XREADGROUP", parse_xread_resp3_to_resp2_legacy),
441 **string_keys_to_dict("BLPOP BRPOP", lambda r: r and tuple(r) or None),
442 **string_keys_to_dict("ZMPOP BZMPOP", zmpop_resp3_to_resp2_legacy),
443 "HRANDFIELD": hrandfield_resp3_to_resp2_legacy,
444 "ACL CAT": lambda r: list(map(str_if_bytes, r)),
445 "ACL GENPASS": str_if_bytes,
446 "ACL GETUSER": parse_acl_getuser_resp3_to_resp2_legacy,
447 "ACL HELP": lambda r: list(map(str_if_bytes, r)),
448 "ACL LIST": lambda r: list(map(str_if_bytes, r)),
449 "ACL LOG": parse_acl_log_resp3_to_resp2_legacy,
450 "ACL USERS": lambda r: list(map(str_if_bytes, r)),
451 "ACL WHOAMI": str_if_bytes,
452 "CLIENT GETNAME": str_if_bytes,
453 "CLIENT TRACKINGINFO": parse_client_trackinginfo_resp3_to_resp2_legacy,
454 "CLUSTER GETKEYSINSLOT": lambda r: list(map(str_if_bytes, r)),
455 "CLUSTER LINKS": parse_cluster_links_resp3_to_resp2_legacy,
456 "COMMAND GETKEYS": lambda r: list(map(str_if_bytes, r)),
457 "CONFIG GET": parse_config_get_resp3_to_resp2_legacy,
458 "DEBUG OBJECT": parse_debug_object,
459 "FUNCTION LIST": parse_function_list_resp3_to_resp2_legacy,
460 "GEOHASH": lambda r: list(map(str_if_bytes, r)),
461 "GEOPOS": parse_geopos_resp3_to_resp2_legacy,
462 "LCS": parse_lcs_idx_resp3_to_resp2_legacy,
463 "MEMORY STATS": parse_memory_stats_resp3,
464 "RESET": str_if_bytes,
465 "SENTINEL MASTER": parse_sentinel_master_resp3_to_resp2_legacy,
466 "SENTINEL MASTERS": parse_sentinel_masters_resp3_to_resp2_legacy,
467 "SENTINEL SENTINELS": parse_sentinel_slaves_and_sentinels_resp3_to_resp2_legacy,
468 "SENTINEL SLAVES": parse_sentinel_slaves_and_sentinels_resp3_to_resp2_legacy,
469 "XINFO CONSUMERS": parse_list_of_dicts_resp3,
470 "XINFO GROUPS": parse_list_of_dicts_resp3,
471}
472
473
474def get_response_callbacks(
475 user_protocol: Optional[int],
476 legacy_responses: bool,
477) -> dict[str, Callable[..., Any]]:
478 """Return the merged callback dict for the given (protocol, legacy)
479 combination.
480
481 ``user_protocol`` is the value the user supplied to the client
482 constructor (``None`` means "not specified"). ``legacy_responses``
483 defaults to ``True`` and selects today's RESP2-style Python shapes
484 even when the wire protocol is RESP3.
485
486 Callers wrap the returned dict in ``CaseInsensitiveDict``.
487 """
488 callbacks: dict[str, Callable[..., Any]] = dict(_RedisCallbacks)
489 if legacy_responses:
490 if user_protocol is None:
491 callbacks.update(_RedisCallbacksRESP3toRESP2Legacy)
492 elif user_protocol in (3, "3"):
493 callbacks.update(_RedisCallbacksRESP3)
494 else:
495 callbacks.update(_RedisCallbacksRESP2)
496 else:
497 if user_protocol is None or user_protocol in (3, "3"):
498 callbacks.update(_RedisCallbacksRESP3Unified)
499 else:
500 callbacks.update(_RedisCallbacksRESP2Unified)
501 return callbacks